Home > Blockchain >  JPopupMenu only for entries of JList, not all space of the JList
JPopupMenu only for entries of JList, not all space of the JList

Time:04-17

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.

enter image description here

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);
      }
    }
  }
}
  • Related