I've been just playing with JOptionPane API to show a custom dialog and I've found a strange situation: when I choose either OK or Cancel option or press Esc key this dialog won't dispose as expected.
The thing is, instead of using this single line to display a modal dialog:
JOptionPane.showConfirmDialog( null
, "The quick brown fox jumps over the lazy dog."
, "New Dialog"
, JOptionPane.OK_CANCEL_OPTION
, JOptionPane.PLAIN_MESSAGE);
I wanted to use the API, setting all the parameters one by one and displaying a dialog as shown in the docs (see Direct Use section):
JOptionPane pane = new JOptionPane(arguments);
pane.set.Xxxx(...); // Configure
JDialog dialog = pane.createDialog(parentComponent, title);
dialog.show();
However, when I close the dialog my application keeps running, even if I set the default close operation to DISPOSE_ON_CLOSE
, and it makes me suspect the dialog is not properly disposed.
Below is a MCVE to play with:
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
public class Demo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JOptionPane optionPane = new JOptionPane();
optionPane.setMessage("The quick brown fox jumps over the lazy dog.");
optionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
optionPane.setMessageType(JOptionPane.PLAIN_MESSAGE);
JDialog dialog = optionPane.createDialog("New Dialog");
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
}
});
}
}
Well, inspecting JOptionPane#createDialog(String title) source code, it turns out the dialog is not closed nor disposed but hidden instead, and it's done on a PropertyChangeEvent
when the option pane's value is set (to CANCEL_OPTION
or NO_OPTION
or CLOSED_OPTION
I guess):
public class JOptionPane extends JComponent implements Accessible {
...
// The following method is called within 'createDialog(...)'
// in order to initialize the JDialog that will be retrieved.
private void initDialog(final JDialog dialog, int style, Component parentComponent) {
...
final PropertyChangeListener listener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
// Let the defaultCloseOperation handle the closing
// if the user closed the window without selecting a button
// (newValue = null in that case). Otherwise, close the dialog.
if (dialog.isVisible() && event.getSource() == JOptionPane.this &&
(event.getPropertyName().equals(VALUE_PROPERTY)) &&
event.getNewValue() != null &&
event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE) {
dialog.setVisible(false);
}
}
};
WindowAdapter adapter = new WindowAdapter() {
private boolean gotFocus = false;
public void windowClosing(WindowEvent we) {
setValue(null);
}
public void windowClosed(WindowEvent e) {
removePropertyChangeListener(listener);
dialog.getContentPane().removeAll();
}
public void windowGainedFocus(WindowEvent we) {
// Once window gets focus, set initial focus
if (!gotFocus) {
selectInitialValue();
gotFocus = true;
}
}
};
dialog.addWindowListener(adapter);
dialog.addWindowFocusListener(adapter);
...
}
}
Despite the comment that states "Otherwise, close the dialog", no WindowEvent closing the dialog is even fired. Consequently the dialog won't be disposed properly unless we explicitely do so:
JDialog dialog = optionPane.createDialog("New Dialog");
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
dialog.dispatchEvent(new WindowEvent(dialog, WindowEvent.WINDOW_CLOSING));
Please note on a WINDOW_CLOSING
event the WindowListener attached to the dialog within initDialog(...)
method will just set the option pane's value to null
but still won't dispose the dialog. That's why we still need to set the default close operation to DISPOSE_ON_CLOSE
.