Adam Gawne-Cain
Adam Gawne-Cain

Reputation: 1720

How to show JOptionPane above modeless JDialog

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);
    }
}

JOptionPane showing under modeless JDialog

Upvotes: 1

Views: 244

Answers (2)

Adam Gawne-Cain
Adam Gawne-Cain

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

Sergiy Medvynskyy
Sergiy Medvynskyy

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

Related Questions