mkemper
mkemper

Reputation: 79

how position a JDialog relative to a JPanel within a JFrame

Below is some code that shows the essence of my issue. I show a JFrame containing a yellow and a green JPanel. These are two objects of the same class that extends JPanel and implements Mouselistener.

What I want is that, if the user right mouse clicks on either panel, a JDialog pops up at the mouse location. Alas, it doesn't, and I simply cannot find out how to get the correct behaviour (after trying loads of things). Can you help me?

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class DialogPositioning extends JFrame {
    public DialogPositioning() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Add two silly panels, then show this JFrame object
        add(new NonsensePanel(Color.YELLOW), BorderLayout.NORTH);
        add(new NonsensePanel(Color.GREEN), BorderLayout.CENTER);
        pack();
        setVisible(true);
    }

    private class NonsensePanel extends JPanel implements MouseListener {
        NonsensePanel(Color bgColor) {
            setPreferredSize(new Dimension(200,200));
            setBackground(bgColor);
            addMouseListener(this);
        }

        @Override
        public void mousePressed(MouseEvent e) {
            if (SwingUtilities.isRightMouseButton(e)) {
                new NonsenseDialog(this, e.getX(), e.getY()); //popup a home-made JDialog at mouse position
            }
        }

        @Override
        public void mouseClicked(MouseEvent e) {}

        @Override
        public void mouseReleased(MouseEvent e) {}

        @Override
        public void mouseEntered(MouseEvent e) {}

        @Override
        public void mouseExited(MouseEvent e) {}
    }

    private class NonsenseDialog extends JDialog {
        NonsenseDialog(Component c, int x, int y) {
            //Add a simple, emmpty panel to this JDialog so that there is something to see, then show this JDialog at the x,y position relative to Component c
            JPanel pnl = new JPanel();
            pnl.setPreferredSize(new Dimension(100, 100));
            add(pnl);

            pack();
            setModal(true);
            setLocationRelativeTo(c); //Should ensure the dialog pops up at posn x,y relative to the top/left corner of ...
            setLocation(x, y);        //... Component c (in our case the yellow or green NonsensePanel objects). DOES NOT WORK???
            setVisible(true);
        }
    }

    public static void main(String[] args) {
        new DialogPositioning();
    }
}

Upvotes: 0

Views: 630

Answers (2)

maloomeister
maloomeister

Reputation: 2486

Edit: You should use camickr's answer, as it is the better solution.

The issue you are having here is, that the coordinates returned by e.getX() and e.getY() are relative to the corresponding JPanel where you clicked. But when you pass these coordinates into your JDialog and use them with setLocation(x, y), these coordinates will be relative to your screen.

You are missing a mapping between your original coordinates and your target coordinates.

If you want to keep the custom dialog and not use e.g. JPopupMenu (which provides this for free), you can just copy the way that JPopupMenu#show() handles this. (See here)

For your example, using the code from the link above, the changes to the NonsenseDialog should look like the following:

private class NonsenseDialog extends JDialog {
    NonsenseDialog(Component c, int x, int y) {
        // code taken from jpopupmenu#show
        Point invokerOrigin;
        if (c != null) {
            invokerOrigin = c.getLocationOnScreen();
            // To avoid integer overflow
            long lx, ly;
            lx = ((long) invokerOrigin.x) + ((long) x);
            ly = ((long) invokerOrigin.y) + ((long) y);
            if (lx > Integer.MAX_VALUE)
                lx = Integer.MAX_VALUE;
            if (lx < Integer.MIN_VALUE)
                lx = Integer.MIN_VALUE;
            if (ly > Integer.MAX_VALUE)
                ly = Integer.MAX_VALUE;
            if (ly < Integer.MIN_VALUE)
                ly = Integer.MIN_VALUE;
            setLocation((int) lx, (int) ly);
        } else {
            setLocation(x, y);
        }
        // your code here
        setVisible(true);
    }
}

What this does is, it takes the coordinates from the component you clicked (your JPanel) and adds the coordinates you passed to the dialog (the coordinates relative to the panel), then uses the resulting coordinates to set the location of the dialog. With this approach, your dialog should pop up at the origin of the right click.

Upvotes: 0

camickr
camickr

Reputation: 324098

The getPoint() method of the MouseEvent will return the point that is relative to the panel.

The setLocation(...) method sets the JDialog relative to the screen.

So you need to convert the mouse point to the screen location.

I would change your NonsenseDialog to receive the mouse Point as a parameter (instead of the separate x/y values).

So you would use the getPoint() method of the MouseEvent to pass to your class.

Then in the constructor of your class you can use:

Point location = SwingUtilities.convertPointToScreen(...);
setLocation( location );

Upvotes: 1

Related Questions