Search code examples
javaswingjframejdialog

JFrame moves to the background for some reason


After I asked a very vague question about this topic yesterday here (which I voted to close now), I was able to pinpoint the problem and create a MCVE which shows this behavior.

The scenario looks like this:

While some operation is ongoing in the background, a Modal "Wait" Dialog is provided in the foreground, also the JFrame is being set to be disabled, just to be sure. After the background task is finished, the Frame is enabled again and the dialog disposed.

The issue is, that after the JFrame is being enabled and a modal dialog is disposed, the JFrame suddenly moves to the background. With "background" meaning, it is moving behind the window that had focus before the JFrame. Why does this happen?

This code should replicate the issue:

private static JFrame frame;
private static JDialog dialog;

public static void main(String[] args) {
    try {
        SwingUtilities.invokeAndWait(new Runnable() {

            @Override
            public void run() {
                buildFrame();
                buildDialog();
            }
        });
    } catch (InvocationTargetException | InterruptedException e) {
        e.printStackTrace();
    }
}

protected static void buildDialog() {
    dialog = new JDialog(frame);
    dialog.getContentPane().add(new JLabel("This is the dialog"));
    dialog.setLocationRelativeTo(frame);
    javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            dialog.dispose();
            frame.setEnabled(true);
        }
    });
    t.setRepeats(false);
    t.start();
    dialog.pack();
    dialog.setVisible(true);
}

protected static void buildFrame() {
    frame = new JFrame();
    frame.setMinimumSize(new Dimension(400, 400));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(new JLabel("This is the Frame"));
    frame.setEnabled(false);
    frame.pack();
    frame.setVisible(true);
}

Does anyone know why this happens and how this could be prevented?


Solution

  • The problem is the methods frame.setEnabled(). I don't know why, but it hides the frame. My suggestion is to remove it and use the modality concept: dialog.setModal(true) (it also makes the parent frame unavailable when the dialog is shown. To make the frame unavailable, you can place a glasspane over it. Here is the code:

    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.FocusEvent;
    import java.awt.event.FocusListener;
    import java.awt.event.MouseAdapter;
    import java.lang.reflect.InvocationTargetException;
    
    import javax.swing.JDialog;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.SwingUtilities;
    
    /**
     * <code>DialogFrameTest</code>.
     */
    public class DialogFrameTest {
        private static JFrame frame;
    
        private static JDialog dialog;
    
        private static Component oldGlassPane;
    
        public static void main(String[] args) {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {
    
                    @Override
                    public void run() {
                        buildFrame();
                        buildDialog();
                    }
                });
            } catch (InvocationTargetException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        protected static void buildDialog() {
            dialog = new JDialog(frame);
            dialog.getContentPane().add(new JTextField("This is the dialog"));
            dialog.setLocationRelativeTo(frame);
            dialog.setModal(true);
            javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    dialog.dispose();
                    frame.setGlassPane(oldGlassPane);
                    oldGlassPane.setVisible(false);
                }
            });
            t.setRepeats(false);
            t.start();
            dialog.pack();
            dialog.setVisible(true);
        }
    
        protected static void buildFrame() {
            frame = new JFrame();
            frame.setMinimumSize(new Dimension(400, 400));
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(new JTextField("This is the Frame"));
            oldGlassPane = frame.getGlassPane();
            frame.setGlassPane(new SplashGlassPane());
            frame.getGlassPane().setVisible(true);
            frame.pack();
            frame.setVisible(true);
        }
    
        private static class SplashGlassPane extends JPanel implements FocusListener {
    
            /** Holds the id of this panel. The creator of this object can submit this id to determine whether it's the owner of this object. */
            private String typeId;
    
            /**
             * Creates new GlassPane.
             */
            public SplashGlassPane() {
                addMouseListener(new MouseAdapter() {});
                addMouseMotionListener(new MouseAdapter() {});
                addFocusListener(this);
                setOpaque(false);
                setFocusable(true);
                setBackground(new Color(0, 0, 0, 100));
            }
    
            @Override
            public final void setVisible(boolean v) {
                // Make sure we grab the focus so that key events don't go astray.
                if (v) {
                    requestFocus();
                }
                super.setVisible(v);
            }
    
            // Once we have focus, keep it if we're visible
            @Override
            public final void focusLost(FocusEvent fe) {
                if (isVisible()) {
                    requestFocus();
                }
            }
    
            /**
             * {@inheritDoc}
             */
            @Override
            public final void paint(Graphics g) {
                final Color old = g.getColor();
                g.setColor(getBackground());
                g.fillRect(0, 0, getSize().width, getSize().height);
                g.setColor(old);
                super.paint(g);
            }
    
            @Override
            public void focusGained(FocusEvent fe) {
                // nothing to do
            }
        }
    
    }