Reputation: 1720
I want JOptionPane to appear above modeless dialogs. For example, in the following app please press the JDialog
button to show a modeless dialog, and then press the JOptionPane
button to show a JOptionPane confirmation dialog. Unfortunately, the JOptionPane appears under the modeless dialog.
In my real app, I have several modeless JDialogs, and I use JOptionPane from several different places.
How can I easily make the JOptionPane appear above all the modeless JDialog instances? By "easily" I mean by adding 1 or 2 lines to each modeless JDialog construction or to each JOptionPane invocation.
One way I tried was to make a new temporary unowned JFrame with always-on-top option as the owner of the JOptionPane. This makes the JOptionPane on top, but the JOptionPane is in center of the screen instead of the center of original JFrame, and I worry that the user may not notice it.
Another way I tried was to make all modeless dialogs invisible before showing the JOptionPane and then making them visible again afterward. But this way is not easy to put round all calls to JOptionPane, because (I believe) it requires a try-finally block to do reliably.
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
public class App {
public static void main(String[] args) {
JFrame f = new JFrame("App Frame");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton btnDialog = new JButton("JDialog");
JButton btnOptionPane = new JButton("JOptionPane");
btnDialog.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JDialog dlg = new JDialog(f, "Modeless Dialog", false);
dlg.setSize(256, 256);
dlg.setLocationRelativeTo(f);
dlg.setVisible(true);
}
});
btnOptionPane.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showConfirmDialog(f, "Confirm JOptionPane");
}
});
f.add(btnDialog, BorderLayout.WEST);
f.add(btnOptionPane, BorderLayout.EAST);
f.setSize(512, 512);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Upvotes: 1
Views: 244
Reputation: 1720
After trying and experimenting with @Sergiy's idea of using static Window methods I came up with this:
import java.awt.BorderLayout;
import java.awt.Window;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class App {
static JFrame hideOwnedWindows(JFrame f) {
ArrayList<Window> arHidden = new ArrayList();
WindowAdapter wa = new WindowAdapter() {
@Override
public void windowActivated(WindowEvent e) {
for (Window w : arHidden)
w.setVisible(true);
f.removeWindowListener(this);
}
};
for (Window w : f.getOwnedWindows()) {
if (w.isVisible()) {
w.setVisible(false);
arHidden.add(w);
}
}
f.addWindowListener(wa);
return f;
}
public static void main(String[] args) {
JFrame f = new JFrame("App Frame");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton btnDialog = new JButton("JDialog");
JButton btnOptionPane = new JButton("JOptionPane");
btnDialog.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JDialog dlg = new JDialog(f, "Modeless Dialog", false);
dlg.setSize(256, 256);
dlg.setLocationRelativeTo(f);
dlg.setVisible(true);
}
});
btnOptionPane.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showConfirmDialog(hideOwnedWindows(f), "Confirm JOptionPane");
}
});
f.add(btnDialog, BorderLayout.WEST);
f.add(btnOptionPane, BorderLayout.EAST);
f.setSize(512, 512);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
The "hideOwnedWindows" method hides all owned windows including the dialogs, and then restores them next time the main JFrame is activated. As all owned Windows are invisible during the JOptionPane, I think (hope) the main JFrame is always activated when the JOptionPane closes.
Upvotes: 2
Reputation: 11327
You need to set the correct parent for your option pane. To determine it you can use the list of all opened windows. In my example I use the last opened window.
import java.awt.BorderLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class App {
public static void main(String[] args) {
JFrame f = new JFrame("App Frame");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton btnDialog = new JButton("JDialog");
JButton btnOptionPane = new JButton("JOptionPane");
btnDialog.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JDialog dlg = new JDialog(f, "Modeless Dialog", false);
dlg.setSize(256, 256);
dlg.setLocationRelativeTo(f);
dlg.setVisible(true);
}
});
btnOptionPane.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showConfirmDialog(findLatestWindow(), "Confirm JOptionPane");
}
});
f.add(btnDialog, BorderLayout.WEST);
f.add(btnOptionPane, BorderLayout.EAST);
f.setSize(512, 512);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static Window findLatestWindow() {
Window result = null;
for (Window w : Window.getWindows()) {
if (w.isVisible()) {
result = w;
}
}
return result;
}
}
If you have more than one dialog opened at the same time, and user can switch between these dialogs, so you need some more lines of code. Because in your case after button click the frame is always the focus owner.
Upvotes: 2