How to close a modal JDialog when user clicks outside of JDialog?

14,069

Solution 1

It's not a modal dialog if you can click outside of it and "something" happens. All the answers are correct, you should be creating a non-modal dialog and then deal with your use case via a FocusListener.

Solution 2

EDIT: Changed to use WindowFocusListener instead of FocusListener, as well as check for descending components on the focus lost in order to not hide if a child component gains focus.

A simple way would be to add a window focus listener on the dialog that hides it when focus is lost. I don't see the need for modality in this case. For example:

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class ClickAwayDialog extends JDialog {

    public ClickAwayDialog(final Frame owner) {
        super(owner);
        JPanel pnl = new JPanel(new BorderLayout());
        pnl.add(new JLabel("Click outside this dialog in the parent frame to close it"), BorderLayout.NORTH);
        JButton btn = new JButton("Click Me");
        btn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(ClickAwayDialog.this, "New Child Window");
            }
        });
        pnl.add(btn, BorderLayout.CENTER);
        this.setContentPane(pnl);
        this.pack();
        this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        this.setLocationRelativeTo(owner);
        this.setAlwaysOnTop(true);
        this.addWindowFocusListener(new WindowFocusListener() {

            public void windowGainedFocus(WindowEvent e) {
                //do nothing
            }

            public void windowLostFocus(WindowEvent e) {
                if (SwingUtilities.isDescendingFrom(e.getOppositeWindow(), ClickAwayDialog.this)) {
                    return;
                }
                ClickAwayDialog.this.setVisible(false);
            }

        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame parent = new JFrame();
                parent.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
                parent.setSize(300, 300);
                parent.setLocationByPlatform(true);
                parent.setVisible(true);
                ClickAwayDialog dlg = new ClickAwayDialog(parent);
                dlg.setVisible(true);                
            }
        });
    }
}

Solution 3

Try to set the modal to false, and then use windowsDeactivated() for close de dialog (dialog.dispose()), works for me.

Solution 4

It's not necessary to be a modal dialog (modal means that it prevents you from using the owner window until you hide the dialog). Better try this:

final JDialog dlg ...
dlg.setModal(false);

dlg.addWindowFocusListener(new WindowFocusListener() {            
    public void windowLostFocus(WindowEvent e) {
        dlg.setVisible(false);
    }            
    public void windowGainedFocus(WindowEvent e) {
    }
});

Solution 5

Use a WindowListener and handle the windowDeactivated() event.

Share:
14,069

Related videos on Youtube

Pyrolistical
Author by

Pyrolistical

Premature optimization is the root of all evil If you are not trying to learn all the time, then you are not a great developer User input is sacred

Updated on June 04, 2022

Comments

  • Pyrolistical
    Pyrolistical almost 2 years

    I have a Undecorated Modal JDialog which I want to setVisible(false) when the user clicks outside of the modal dialog.

    Is this possible in Swing?

    What I am doing is popping up a custom editor for a text field like a date selector. Is there an easier way to do what I want?

    EDIT

    Remember that modal blocks on the call to setVisible(true), so you can't just say "don't use a modal dialog"

    And I've tried focus listeners on the dialog, they don't trigger when its modal.

    • vickirk
      vickirk over 14 years
      Did you try the addAWTEventListener method, this should give you events for all specified event types, e.g. in the example I gave below this would be all mouse events.
    • vickirk
      vickirk over 14 years
      I know you said "so you can't just say "don't use a modal dialog"", presumably this is because you have code that executes straight after the setVisible call? Could you not move this into maybe a listener for when the dialog is closed? Without knowing details of your app it may provide a cleaner design, especially when it comes to unit testing, I like to move dialogs out into a strategy for getting user responses, that way I can inject mock strategies without hanging a unit test when it runs headless or without having to mess around with creating events programatically.
  • vickirk
    vickirk over 14 years
    Can a modal dialog (and child components) loose focus for anything other than switching to another application?
  • vickirk
    vickirk over 14 years
    Ah, got what you mean, you meant instead of a modal dialog! Don't know why this was voted down
  • vickirk
    vickirk over 14 years
    Focus lost would have to ensure the component that gained the focus is not a child component of the dialog so you will need to search up the component hierarchy via getParent()
  • Chris B.
    Chris B. over 14 years
    @vickirk -- good point. I changed the example above to check for descendant components (arguably an edge case, but I enhanced the example to allow creating a child window of the dialog). I also changed it to use a window focus listener.
  • Pyrolistical
    Pyrolistical over 12 years
    Yes I totally understand how one can do this without a modal dialog, but the point of the question was if it was possible to do it with a modal dialog. The main advantage of using a modal dialog is it blocks when setVisible(true). My goal was to not need to restructure the program to use a non-modal dialog or write a utility to emulate the blocking nature in a non-modal dialog.
  • Pyrolistical
    Pyrolistical over 12 years
    This is the closest answer to "No". That's all I was asking. Not how I can achieve this without a non-modal dialog.
  • Reto Höhener
    Reto Höhener almost 9 years
    I just tested this and it looks like this kind of listener also does not get any mouse events from outside the modal dialog.
  • Reto Höhener
    Reto Höhener almost 9 years
    I just tested this and for me this approach only seemed to work when clicking completely outside of the java application, but not when trying to click on the java main frame that spawned the modal dialog.
  • camickr
    camickr almost 9 years
    @Zalumon, I guess this answer was not very clear. This was a suggestion to NOT use a modal JDialog. When you use a modal JDialog as a popup, then you can't close the dialog if you click outside the dialog. This is a solution to allow you to close a non modal dialog when you click outside the dialog area, so the "popup dialog" gets closed.