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...
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
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...
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);
}
}
}