4

My Swing application has to show a modal dialog to the user. Sorry for not posting SSCCE.

topContainer might be JFrame or JApplet.

private class NewGameDialog extends JDialog {
     public NewGameDialog () {
         super(SwingUtilities.windowForComponent(topContainer), "NEW GAME", ModalityType.APPLICATION_MODAL);

         //add components here

         getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));

         //TODO:
         setSize(new Dimension(250, 200));
         setLocation(650, 300);
     }
}

I start the dialog like this on network event

SwingUtilities.invokeLater(new Runnable() {
     @Override
     public void run() {
         NewGameDialog dialog = new NewGameDialog();
         dialog.setVisible(true);
     }
});

The problem is to set optimal location for my dialog.

1) If it is set as absolute value, and I move the app frame to the second screen, then dialog is shown on the first screen which is weird.

2) If it is set a relative value to JFrame, it might appear that user moved the app frame outside of the screen and the dialog being relatively located would not be visible to the user. And because it is modal, the game would be stuck.

What is the best solution considering two above mentioned issues?

Nikolay Kuznetsov
  • 9,467
  • 12
  • 55
  • 101

4 Answers4

4

I think, best would be to center the dialog in the middle of the current screen as described here.

Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension screenSize = toolkit.getScreenSize();
int x = (screenSize.width - d.getWidth()) / 2;
int y = (screenSize.height - d.getHeight()) / 2;
d.setLocation(x, y);

This always works and how it can be invisible to the user if it is right in the center of the screen? And setLocationRelativeTo can also be used but you need to invoke it at the right time.

Community
  • 1
  • 1
Audrius Meškauskas
  • 20,936
  • 12
  • 75
  • 93
  • 1
    how about `d.setLocationRelativeTo(null);`? this is the main line the answer under the link. – Nikolay Kuznetsov Jan 11 '13 at 07:55
  • Also can be used but see also link under "quirks". – Audrius Meškauskas Jan 11 '13 at 07:58
  • 1
    No quirks at all IMO using `setLocationRelativeTo` , must just be called at the *right time* as seen [here](http://stackoverflow.com/a/5351620/1133011). – David Kroukamp Jan 11 '13 at 08:57
  • Maybe, would be better to say "you need to invoke it at right time". – Audrius Meškauskas Jan 11 '13 at 09:28
  • I launch app at first screen and then move it to second, but dialog still appears at first screen. – Nikolay Kuznetsov Jan 14 '13 at 05:16
  • Please, see my answer to the question. Do you think anything wrong with that code? – Nikolay Kuznetsov Jan 15 '13 at 07:09
  • Dependently how the desktop is configured, the new dialog may appear in the screen where the mouse pointer is located, get the coordinates of only that one screen and be centered there. This prevents dialog from being split in the two or four window workstation, where the center of the whole desktop is at the edge of the screen. I am still looking for solution on know to position windows on multiple screens programamtically from Java code. – Audrius Meškauskas Jan 15 '13 at 07:26
4

This reminded me of a very favourite post of mine, using Window.setLocationByPlatform(true), on StackOverflow.

How to best position Swing GUIs

EDIT 1 :

You can add a FocusListener to your JDialog and on focusGained(...) method, you can use setLocationRelativeTo(null) for both the JFrame and the JDialog, so that they both come to the center of the screen no matter where they are before.

import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

/**
 * Created with IntelliJ IDEA.
 * User: Gagandeep Bali
 * Date: 1/14/13
 * Time: 7:34 PM
 * To change this template use File | Settings | File Templates.
 */
public class FrameFocus
{
    private JFrame mainwindow;
    private CustomDialog customDialog;

    private void displayGUI()
    {
        mainwindow = new JFrame("Frame Focus Window Example");
        customDialog = new CustomDialog(mainwindow, "Modal Dialog", true);
        mainwindow.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JPanel contentPane = new JPanel();
        JButton mainButton = new JButton(
                "Click me to open a MODAL Dialog");
        mainButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (!customDialog.isShowing())
                    customDialog.setVisible(true);
            }
        });
        contentPane.add(mainButton);
        mainwindow.setContentPane(contentPane);
        mainwindow.pack();
        mainwindow.setLocationByPlatform(true);
        mainwindow.setVisible(true);
    }

    public static void main(String... args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                new FrameFocus().displayGUI();
            }
        });
    }
}


class CustomDialog extends JDialog
{
    private JFrame mainWindow;
    public CustomDialog(JFrame owner, String title, boolean modal)
    {
        super(owner, title, modal);
        mainWindow = owner;
        JPanel contentPane = new JPanel();
        JLabel dialogLabel = new JLabel(
                "I am a Label on JDialog.", JLabel.CENTER);
        contentPane.add(dialogLabel);
        setContentPane(contentPane);
        pack();

        addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {
                mainWindow.setLocationRelativeTo(null);
                setLocationRelativeTo(null);
            }

            @Override
            public void focusLost(FocusEvent e) {
                /*
                 * Nothing written for this part yet
                 */
            }
        });
    }
}

EDIT 2 :

I searched a bit here and there, and it turns out, in my opinion, that actually on which Monitor Screen your application comes at the first instance, will determine it's GraphicsConfiguration. Though as I roamed through the API, there is only a getter method for the said GraphicsConfiguration thingy and no setter methods for the same (Still You can specify one through the constructor of any top level Window i.e. JFrame(...)/JDialog(...)).

Now you can occupy your head with this code, which can be used to determine the appropriate location, that you want to set, again, you might have to use focusGain() method in my opinion, to satisfy condition 2 of your question. Have a look at the code attached, though no need to create a new JFrame/JDialog, just watch how to get coordinates for the screen (that you can add in the focusGain() method to determine the location of the whole Application.)

GraphicsEnvironment ge = GraphicsEnvironment.
    getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
    GraphicsDevice gd = gs[j];
    GraphicsConfiguration[] gc =
            gd.getConfigurations();
    for (int i=0; i < gc.length; i++) {
        JFrame f = new
        JFrame(gs[j].getDefaultConfiguration());
        Canvas c = new Canvas(gc[i]);
        Rectangle gcBounds = gc[i].getBounds();
        int xoffs = gcBounds.x;
        int yoffs = gcBounds.y;
        f.getContentPane().add(c);
        f.setLocation((i*50)+xoffs, (i*60)+yoffs);
        f.show();
    }
}

EDIT 3 :

Try to change this :

int x = loc.getX() + (mainWindow.getWidth() - getWidth()) / 2;
int y = loc.getY() + (mainWindow.getHeight() - getHeight()) / 2;
setLocation(x, y);

to just :

setLocationRelativeTo(mainWindow);

To test the above thingy, I used my FrameFocus Class as is, though I had added your changes to my CustomDialog method, as shown in this modified CustomDialog Class.

class CustomDialog extends JDialog
{
    private JFrame mainWindow;
    public CustomDialog(JFrame owner, String title, boolean modal)
    {
        super(owner, title, modal);
        mainWindow = owner;
        JPanel contentPane = new JPanel();
        JLabel dialogLabel = new JLabel(
                "I am a Label on JDialog.", JLabel.CENTER);
        contentPane.add(dialogLabel);
        setContentPane(contentPane);
        pack();

        addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {
                //mainWindow.setLocationRelativeTo(null);
                //setLocationRelativeTo(null);
                GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
                GraphicsDevice[] gs = ge.getScreenDevices();
                for (int j = 0; j < gs.length; j++) {
                    GraphicsDevice gd = gs[j];
                    GraphicsConfiguration[] gc = gd.getConfigurations();
                    for (int i=0; i < gc.length; i++) {
                        Rectangle gcBounds = gc[i].getBounds();

                        Point loc = mainWindow.getLocationOnScreen();
                        if (gcBounds.contains(loc)) {
                            System.out.println("at " + j + " screen");

                            int x = gcBounds.x + (gcBounds.width - mainWindow.getWidth()) / 2;
                            int y = gcBounds.y + (gcBounds.height - mainWindow.getHeight()) / 2;
                            mainWindow.setLocation(x, y);

                            //x = (int) (loc.getX() + (mainWindow.getWidth() - CustomDialog.this.getWidth()) / 2);
                            //y = (int) (loc.getY() + (mainWindow.getHeight() - CustomDialog.this.getHeight()) / 2);
                            //CustomDialog.this.setLocation(x, y);
                            CustomDialog.this.setLocationRelativeTo(mainWindow);

                            break;
                        }
                    }
                }
            }

            @Override
            public void focusLost(FocusEvent e) {
                /*
                 * Nothing written for this part yet
                 */
            }
        });
    }
}
Community
  • 1
  • 1
nIcE cOw
  • 24,468
  • 7
  • 50
  • 143
  • I move JFrame to second screen, but JDialog appears on the first. It does not address issue #1. – Nikolay Kuznetsov Jan 14 '13 at 05:12
  • @NikolayKuznetsov : What if you you add a FocusListener, to your `JDialog` and perform steps as mentioned in my edit for the `focusGained()` method ? I hope this workaround can work for both the situations. – nIcE cOw Jan 14 '13 at 14:23
  • @NikolayKuznetsov : Or you can add a mixture of all the answers in the `focusGained()` method. Though I am still thinking too, to make it more suited to the USER's need. I might will update as I get more info on the topic. – nIcE cOw Jan 14 '13 at 17:40
  • 1
    @NikolayKuznetsov : Please do watch the latest edit, and see if this new information be of any help to you :-) – nIcE cOw Jan 14 '13 at 19:45
  • 1
    I think using EDIT-2 and `getLocation` I need to identify what screen the app is running and then move `JFrame` and `JDialog` to middle of that screen. – Nikolay Kuznetsov Jan 15 '13 at 06:27
  • 1
    Please, see my answer to the question. Do you think anything wrong with that code? – Nikolay Kuznetsov Jan 15 '13 at 07:08
  • EDIT-3 seems to show JDialog at 0,0 when mainFrame is minimized. – Nikolay Kuznetsov Jan 21 '13 at 10:54
4

use JDialog.setLocation() for moving JDialog on desired Point on the screen

import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;

public class JDialogAtPoint {

    private JFrame frame = new JFrame();
    private JPanel panel = new JPanel();
    private JDialog dialog;
    private Point location;

    public JDialogAtPoint() {
        createGrid();
        createDialog();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(panel);
        frame.setLocation(100, 100);
        frame.pack();
        frame.setVisible(true);
    }

    private void createGrid() {
        panel.setLayout(new GridLayout(3, 3, 4, 4));
        int l = 0;
        int row = 3;
        int col = 3;
        JButton buttons[][] = new JButton[row][col];
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                buttons[i][j] = new JButton("");
                buttons[i][j].putClientProperty("column", i + 1);
                buttons[i][j].putClientProperty("row", j + 1);
                buttons[i][j].setAction(updateCol());
                panel.add(buttons[i][j]);
                l++;
            }
        }
    }

    private void createDialog() {
        dialog = new JDialog();
        dialog.setAlwaysOnTop(true);
        dialog.setModal(true);
        dialog.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
        JPanel pane = (JPanel) dialog.getContentPane();
        pane.setBorder(new EmptyBorder(20, 20, 20, 20));
        dialog.pack();
    }

    public Action updateCol() {
        return new AbstractAction("Display JDialog at Point") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                JButton btn = (JButton) e.getSource();
                System.out.println("Locations coordinates" + btn.getLocation());
                System.out.println("clicked column "
                        + btn.getClientProperty("column")
                        + ", row " + btn.getClientProperty("row"));
                if (!dialog.isVisible()) {
                    showingDialog(btn.getLocationOnScreen());
                }
            }
        };
    }

    private void showingDialog(final Point loc) {
        dialog.setVisible(false);
        location = loc;
        int x = location.x;
        int y = location.y;
        dialog.setLocation(x, y);
        Runnable doRun = new Runnable() {

            @Override
            public void run() {//dialog.setLocationRelativeTo(frame);
                dialog.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(doRun);
    }

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

            @Override
            public void run() {
                JDialogAtPoint cf = new JDialogAtPoint();
            }
        });
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
2

With the help of all 3 answerers I have come up with code which seems exactly what I need. First, JFrame got placed in the middle of current screen and then JDialog accordingly.

GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
    GraphicsDevice gd = gs[j];
    GraphicsConfiguration[] gc = gd.getConfigurations();
    for (int i=0; i < gc.length; i++) {
        Rectangle gcBounds = gc[i].getBounds();

        Point loc = mainWindow.getLocationOnScreen();
        if (gcBounds.contains(loc)) {
            System.out.println("at " + j + " screen");

            int x = gcBounds.x + (gcBounds.width - mainWindow.getWidth()) / 2;
            int y = gcBounds.y + (gcBounds.height - mainWindow.getHeight()) / 2;
            mainWindow.setLocation(x, y);

            int x = loc.getX() + (mainWindow.getWidth() - getWidth()) / 2;
            int y = loc.getY() + (mainWindow.getHeight() - getHeight()) / 2;
            setLocation(x, y);

            break;
        }
    }
}
Nikolay Kuznetsov
  • 9,467
  • 12
  • 55
  • 101
  • See again in this code, your `First Monitor Screen` on which the `JFrame` will appear, will become a part of `GraphicsConfiguration`, so iterating through the second value will be meaningless. Even though if you bring this `JFrame` to the other screen, still again it will go to the first screen with this. So in that case, the `focusGained()` method again can work with `setLocationRelativeTo(null)`. Though I will try this code in sometime, as I get back to my home, then I can solidify as to how good this approach is. – nIcE cOw Jan 15 '13 at 15:07
  • @GagandeepBali, you are right. It was my original version which got updated drastically during testing. – Nikolay Kuznetsov Jan 15 '13 at 15:17
  • @NikolayKuznelsov : Ahha, though the updated answer does work, but at certain times, if I bring the `mainWindow` to the extreme bottom, of my second monitor, and then try to press the `JButton` as shown in my example, then with your code, the `JDialog` comes to this location of the `mainWindow` though `mainWindow` itself goes to the center. If you facing the same issue, then I had updated my answer with **EDIT 3**, have a look, that might can sort this issue out too, I guess... +1 to your efforts, in placing your answer :-) – nIcE cOw Jan 15 '13 at 16:23
  • Sure sure, You're MOST WELCOME and KEEP SMILING :-) In simpler terms, what I mean to say is, just find the location of your `JFrame` (as you already had done), then simply put `JDialog` relative to your `JFrame` after this (using `setLocationRelativeTo(mainWindow)`), instead of finding new co-ordinates for your `JDialog` by computing. – nIcE cOw Jan 15 '13 at 16:29