Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make JPopupMenu Display with a Certain Bottom Left Coordinate

Say that I want to make a button in Java in such a way so that when you click on it, a JPopupMenu appears. The relevant code for it appearing is menu.show(button, button.getWidth()/2, button.getHeight()/2);, which makes the JPopupMenu be displayed with its top left corner at the center of the button, as shown below:

Current Swing JPopupMenu

However, I would like to have it so that instead the bottom left corner is at the center of the button, somewhat like what iTunes does (there is a button underneath the bottom left corner, which is the same size as the + button to the left of it):

enter image description here

I tried to make this happen by getting the height of the JPopupMenu and adding it to the y coordinate that the popup menu is being displayed at, but I found out that the JPopupMenu has a height of 0 before it is visible, which doesn't help me since I'm trying to tell the computer where to make it visible. Also, hardcoding in an offset isn't possible because the number of items in the popup isn't necessarily going to be the same.

How can I make it so my JPopupMenu with an unknown height can be displayed so that it's bottom left coordinate matches a given coordinate?

like image 304
Thunderforge Avatar asked Mar 25 '23 00:03

Thunderforge


1 Answers

Basically, this creates a popup menu and registers it with the component using JComponent#setComponentPopupMenu. This means that I no longer need to monitor for mouse events or make decisions about when to show the popup.

I then override JComponent#getPopupLocation and calculate the location where I want the popup to appear.

Basically, I get the JComponent#getComponentPopupMenu, get it's preferred size and calculate an appropriate offset so that the bottom, left corner now appears in the center of the component...

enter image description here

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestPopup02 {

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

    public TestPopup02() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            JPopupMenu menu = new JPopupMenu();
            menu.add(new JMenuItem("Edit Playlist"));
            menu.addSeparator();
            menu.add(new JMenuItem("Check for Available Downloads..."));
            menu.addSeparator();
            menu.add(new JMenuItem("Export..."));
            menu.add(new JMenuItem("Burn Playlist to Disc"));
            menu.add(new JMenuItem("Copy To Play Order"));
            menu.addSeparator();
            menu.add(new JMenuItem("Delete"));
            setComponentPopupMenu(menu);
        }

        @Override
        public Point getPopupLocation(MouseEvent event) {
            // Get the registered popup menu...
            JPopupMenu popup = getComponentPopupMenu();
            // Get the super point, just in case...
            Point pos = super.getPopupLocation(event);
            if (popup != null) {
                // Create a new "point" location
                pos = new Point();
                // get the preferred size of the menu...
                Dimension size = popup.getPreferredSize();
                // Adjust the x position so that the left side of the popup
                // appears at the center of  the component
                pos.x = (getWidth() / 2);
                // Adjust the y position so that the y postion (top corner)
                // is positioned so that the bottom of the popup
                // appears in the center
                pos.y = (getHeight() / 2) - size.height;
            }
            return pos;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            // Simply draws a cross in the center of the window, so you 
            // know where the center is...
            int width = getWidth() - 1;
            int height = getHeight() - 1;
            g.drawLine(width / 2, 0, width / 2, height);
            g.drawLine(0, height / 2, width, height / 2);
        }

    }
}

Update with Mac output

enter image description here

Button example

It is unlikely that you will ever find a solution that meets precisely your needs. One of the greatest skills any developer can develop is the ability to take an idea and mold it there needs.

The previous example holds every thing you need, you just need to be able to make the leap from concept to solution.

getPopupLocation is part of the component popup API, so either overriding the method or calling it probably isn't quite what you need (unless you have a dedicated button for the task, which may not be a bad thing), so you would need to adapt the solution to your needs...

enter image description here

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestPopup02 {

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

    public TestPopup02() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JButton button;
        private JPopupMenu popup;

        public TestPane() {
            popup = new JPopupMenu();
            popup.add(new JMenuItem("Edit Playlist"));
            popup.addSeparator();
            popup.add(new JMenuItem("Check for Available Downloads..."));
            popup.addSeparator();
            popup.add(new JMenuItem("Export..."));
            popup.add(new JMenuItem("Burn Playlist to Disc"));
            popup.add(new JMenuItem("Copy To Play Order"));
            popup.addSeparator();
            popup.add(new JMenuItem("Delete"));

            setLayout(new GridBagLayout());
            button = new JButton("+");
            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    popup.pack();
                    Point pos = new Point();
                    // get the preferred size of the menu...
                    Dimension size = popup.getPreferredSize();
                    // Adjust the x position so that the left side of the popup
                    // appears at the center of  the component
                    pos.x = (button.getWidth() / 2);
                    // Adjust the y position so that the y postion (top corner)
                    // is positioned so that the bottom of the popup
                    // appears in the center
                    pos.y = (button.getHeight() / 2) - size.height;
                    popup.show(button, pos.x, pos.y);
                }
            });
            add(button);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            // Simply draws a cross in the center of the window, so you 
            // know where the center is...
            int width = getWidth() - 1;
            int height = getHeight() - 1;
            g.drawLine(width / 2, 0, width / 2, height);
            g.drawLine(0, height / 2, width, height / 2);
        }
    }
}
like image 101
MadProgrammer Avatar answered Apr 06 '23 04:04

MadProgrammer