I am new to learning javax.swing
and have a doubt.
I have a JFrame
whose defaultCloseOperation
is set to EXIT_ON_CLOSE
. I have another component - a JOptionPane
- over the JFrame
. What I want to happen is that even when the focus is on the JOptionPane
message dialog, I want the program to terminate when I click the x
button on the JFrame
window.
So, precisely, I'm looking to bring the JFrame
in focus without dismissing the JOptionPane
message dialog is over it so that I can close the JFrame
window and, consequently, make the program terminate.
Here is my code:
import javax.swing.*;
public class JJSS {
public JFrame jf;
public JJSS(){
jf = new JFrame();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(400, 400);
jf.setVisible(true);
}
public void runMethod(){
String str = JOptionPane.showInputDialog(jf, "Enter something...");
str = String.valueOf(new StringBuffer(str).reverse());
JOptionPane.showMessageDialog(jf, "Reversed: "+str, "Output", JOptionPane.PLAIN_MESSAGE);
}
public static void main(String[] args){
new JJSS().runMethod();
System.exit(0);
}
}
With the current code, nothing happens when I click on the close button (x
) of the JFrame
window.
How do I bring the focus on the JFrame window, while the JOptionPane dialog is still on it, and terminate the program by closing the JFrame window?
You can't do this with a normal JOptionPane or with any modal dialog as the modality prevents the user from interacting with other components of the GUI while the dialog is displayed. You can only get this to work if you create a non-modal dialog, meaning that the JOptionPane must be created not with the JOptionPane static factory methods, but rather in a non-traditional way, using a JOptionPane constructor -- check the JOptionPane API for how to do this.
For example:
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class NonModalJOptionPane {
private static void createAndShowGui() {
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(400, 300));
final JFrame frame = new JFrame("NonModalJOptionPane");
panel.add(new JButton(new AbstractAction("Push Me") {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane optionPane = new JOptionPane("My Message", JOptionPane.PLAIN_MESSAGE);
JDialog dialog = optionPane.createDialog(frame, "My Option");
dialog.setModalityType(ModalityType.MODELESS); // **** key ***
dialog.setVisible(true);
}
}));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
The key to this code is here:
// create the JOptionPane using one of its constructors
JOptionPane optionPane = new JOptionPane("My Message", JOptionPane.PLAIN_MESSAGE);
// create a JDialog from it, tying it to the parent JFrame, here called "frame"
JDialog dialog = optionPane.createDialog(frame, "My Option");
// setting the modality type so that it is **not** modal
dialog.setModalityType(ModalityType.MODELESS); // **** key ***
// and then displaying it
dialog.setVisible(true);
Where I create a JOptionPane via its constructor, not via a static method, I create a JDialog and set it to be MODELESS, and then display it.
Another viable option is to create your own JDialog, making sure that you set it to being non-modal as well.
For example, you could add the following code to the code above:
panel.add(new JButton(new AbstractAction("Push Me 2 -- Using Dialog") {
@Override
public void actionPerformed(ActionEvent e) {
// button that when pressed, closes the JDialog that holds it
// similar to a JOptionPane's OK button
JButton disposeWinBtn = new JButton(new DisposeWindowAction("OK", KeyEvent.VK_O));
// create a bunch of JPanels, add components to them, ...
JPanel bottomPanel = new JPanel();
bottomPanel.add(disposeWinBtn);
JLabel msgLabel = new JLabel("My Message");
JPanel msgPanel = new JPanel();
msgPanel.add(msgLabel);
JPanel panel = new JPanel(new BorderLayout());
panel.add(msgPanel, BorderLayout.CENTER);
panel.add(bottomPanel, BorderLayout.PAGE_END);
// create a JDialog whose parent component is the main JFrame
// and make sure that it is *****non-modal ***** <===== this is KEY *****
JDialog dialog = new JDialog(frame, "Dialog", ModalityType.MODELESS);
dialog.add(panel); // add the JPanel, panel, created above, with components
dialog.pack(); // have layout managers do their thing
dialog.setLocationRelativeTo(frame); // center it over the main JFrame
dialog.setVisible(true); // and display it
}
}));
just under where the first button is added. You'll also need the DisposeWindowAction class, that allows the button to close and dispose of the window that is displaying it (here a JDialog window):
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.SwingUtilities;
@SuppressWarnings("serial")
public class DisposeWindowAction extends AbstractAction {
public DisposeWindowAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
@Override
public void actionPerformed(ActionEvent e) {
Component component = (Component) e.getSource();
if (component == null) {
return;
}
Window win = SwingUtilities.getWindowAncestor(component);
if (win == null) {
return;
}
win.dispose();
}
}