I want to display a JPopupMenu
on right click on elements of a JList
.
jList.setComponentPopupMenu(...)
works fine and displays the context menu (JPopupMenu
) if I right click the JList
.
But my JList
often is larger (taller) than needed for its entries (as the JList is expanded to window height).
And the context menu always appears, even if I click on the blank area of the JList.
I want the context-menu (JPopupMenu) to only show up, if the user right-clicks an entry, not if the user clicks on the blank space below the last list entry.
The menu options are only applicable to a certain entry, not to the JList
in its entirety.
I tried setComponentPopupMenu
on a custom ListCellRenderer
instead of on the list but that doesn't show a menu at all.
How could I get an only-on-entry context menu for my JList?
CodePudding user response:
Use the following method to get the component renderer (renders the items on the list)
https://docs.oracle.com/javase/7/docs/api/javax/swing/JList.html#getCellRenderer()
Then fetch the component. Then attach a mouselistener, and upon right click, perform whatever action you want.
EDIT - Here's an even better way. I wasn't aware of this solution at the time of creating this answer.
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class SOQ_20220416
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Title");
frame.setSize(500, 500);
frame.setLocation(200, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createPanelAndList());
frame.setVisible(true);
}
public static JPanel createPanelAndList()
{
enum Fruit
{
APPLE,
BANANA,
CHERRY,
;
}
JPanel panel = new JPanel();
JList<Fruit> list = new JList<>(Fruit.values());
panel.add(list);
MouseListener mouseListener =
new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
int index = list.locationToIndex(e.getPoint());
System.out.println("Right clicked on Item " index);
}
}
};
list.addMouseListener(mouseListener);
return panel;
}
}
CodePudding user response:
Another option is to override JPopupMenu#show(Component, int, int)
method. Since it is possible to get the coordinates of the click, you can control whether the JPopupMenu
is shown or hidden by doing super.show(c, x, y);
only if those coordinates are in a JList
cell.
import java.awt.*;
import javax.swing.*;
public class ListPopupMenuTest {
private Component makeUI() {
String[] model = {"Entry 1", "Entry 2", "Entry 3"};
JList<String> list = new JList<>(model);
list.setComponentPopupMenu(new ListPopupMenu());
return new JScrollPane(list);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new ListPopupMenuTest().makeUI());
frame.setSize(320, 240);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
class ListPopupMenu extends JPopupMenu {
protected ListPopupMenu() {
super();
add("Item 1").addActionListener(System.out::println);
add("Item 2").addActionListener(System.out::println);
addSeparator();
add("Item 3").addActionListener(System.out::println);
}
@Override public void show(Component c, int x, int y) {
if (c instanceof JList) {
JList<?> list = (JList<?>) c;
Point pt = new Point(x, y);
int idx = list.locationToIndex(pt);
Rectangle r = list.getCellBounds(idx, idx);
if (r != null && r.contains(pt)) { // && !list.isSelectionEmpty()) {
super.show(c, x, y);
}
}
}
}