6

There are some properties of the right-click context menu I would like to replicate with a JPopupMenu:

  1. When menu is open and you click elsewhere, menu closes.
  2. When menu is open and you click elsewhere, nothing else happens.

I've got the first part down just fine. But when I click elsewhere, other events can occur. For instance, lets say I have button, A, which performs some action, B. Currently, if the JPopupMenu is open, and I click A, the JPopupMenu closes and B is performed. I would prefer that JPopupMenu close and B NOT be performed. Is this possible?

Thanks

mKorbel
  • 109,525
  • 20
  • 134
  • 319
BCarpe
  • 860
  • 10
  • 27
  • 1
    I dont see why you wouldn't want the button event to fire when the user clicked on it, but whenever the JPopupMenu event is fired, disable the button click event, then reenable the event when the menu closes. – Hunter McMillen Jun 03 '11 at 17:22
  • @BCarpe, is this a simple case of "check-then-act"? set a flag when the `JPopupMenu` is opened, and then have checks in your `actionPerformed` method(s) that will only carry out their routine if the flag is not set. – mre Jun 03 '11 at 17:32
  • It's just the convention. Try it out yourself. Right click on your browser and click anywhere afterwards. It always closes first, before letting you click anything else. As far as disabling/re-enabling, I've got a lot of components and will probably be adding more, so keeping track of that mechanism would add a bit more complexity than I can really deal with. – BCarpe Jun 03 '11 at 17:33
  • @mre, I'd really rather not turn to putting a method of handling the situation into each component. And is there a signal that JPopupMenu puts out when closing? --EDIT-- PopupMenuListener does so. – BCarpe Jun 03 '11 at 17:45
  • 1
    @BCarpe If i right click in my browser then click a different tab than the one i am in, it selects the new tab. (Chrome) – Hunter McMillen Jun 03 '11 at 17:50
  • @Hunter: @BCarpe: I can confirm Firefox does the same thing. It also works if you click on a link. – unholysampler Jun 03 '11 at 18:00
  • @Hunter Hmm... That's not what I get when using Chrome (@unholysampler or Firefox)... But I'm on Ubuntu and if that's what you get, it must not be completely the convention. Perhaps I'll just leave it as it is and make the user click on empty space if they want to get rid of the popup menu... – BCarpe Jun 03 '11 at 18:02

2 Answers2

11

This works, and is a lot simpler... although could be overridden by some look and feels.

UIManager.put("PopupMenu.consumeEventOnClose", Boolean.TRUE);

It is also worth noting this only consumes the MOUSE_PRESSED event, the subsequent MOUSE_CLICKED event is not consumed. You can emulate mouse clicked by setting flag in mousePressed() and testing it in mouseReleased(). If the initial mouse pressed is consumed then the flag won't be set in the mouseReleased()

private boolean pressed = false;

@Override
public void mousePressed(MouseEvent e) {
    pressed = true;
}

@Override
public void mouseReleased(MouseEvent e) {
    if (pressed) {
         // do click stuff
    }
    pressed = false;
}
Adam
  • 35,919
  • 9
  • 100
  • 137
  • This was useful to me to disable this behavior :) It caused a problem with subsequent right-clicks when popup menu already open, showing the new menu but not selecting the row. – Thomas W May 12 '14 at 01:28
  • I have exactly the opposite problem. This value was true when I checked it, so I set it to false (and double-checked that it stays false), but my events are still consumed. – Mark Jeronimus Jul 14 '15 at 10:25
4

Taking into account what was said in your question and comments, I would approach your problem in one of the following ways.

Technically you have two options here:

1.Hide the popup whenever user moves the mouse outside of the popup. This way you do not have the problem of user clicking since the popup will disappear itself.

2.Capture this particular mouse event globally and consume the event on left click if the popup is visible. I show this particular solution in the example below.

    import java.awt.AWTEvent;
    import java.awt.Toolkit;
    import java.awt.event.AWTEventListener;
    import java.awt.event.ActionEvent;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import javax.swing.AbstractAction;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JMenu;
    import javax.swing.JPanel;
    import javax.swing.JPopupMenu;
    import javax.swing.SwingUtilities;

    public class DisableClickWhenPopupVisibleTest
    {
        public static void main(String[] args)
        {
            SwingUtilities.invokeLater(new Runnable()
            {
                @Override
                public void run()
                {               
                    final JPopupMenu popup = new JPopupMenu();
                    popup.add(new JMenu("aAaa"));
                    JPanel contentPane = new JPanel();
                    contentPane.add(popup);
                    JButton b = new JButton();
                    b.setAction(new AbstractAction("Button")
                    {
                        private static final long serialVersionUID = 1L;
                        @Override
                        public void actionPerformed(ActionEvent e)
                        {
                            System.out.println("b actionPerformed");
                        }
                    });
                    contentPane.add(b);
                    contentPane.addMouseListener(new MouseAdapter() {
                        @Override
                        public void mousePressed(MouseEvent e)
                        {
                            showPopup(e);
                        }
                        @Override
                        public void mouseReleased(MouseEvent e)
                        {
                            showPopup(e);
                        }
                        private void showPopup(MouseEvent e)
                        {
                            if(e.isPopupTrigger())
                                popup.show(e.getComponent(), e.getX(), e.getY());
                        }
                    });
                    //use global mouse event capture to disable left click on anything when popup is visible
                    Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
                        @Override
                        public void eventDispatched(AWTEvent event)
                        {
                            MouseEvent me = (MouseEvent)event;
                            if(me.getID() == MouseEvent.MOUSE_PRESSED)
                            {
                                System.out.println("eventDispatched popup.vis="+popup.isVisible());
                                if( me.getButton() == MouseEvent.BUTTON3)
                                {   
                                    System.out.println("BUTTON3");
                                }   
                                else if(me.getButton() == MouseEvent.BUTTON1)
                                {
                                    System.out.println("BUTTON1");
                                    if(popup.isVisible())
                                        me.consume();
                                }
                            }
                        }
                    }, AWTEvent.MOUSE_EVENT_MASK);                      
                    JFrame f = new JFrame();
                    f.setContentPane(contentPane);
                    f.setSize(400, 300);
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    f.setVisible(true);
                }
            });
        }
    }

You can test the example by right clicking slightly on the left of the button then the popup will show. Then if you click over the button its action will not be called. The action is called normally if the popup is hidden. This functionality is provided by the following line of code Toolkit.getDefaultToolkit().addAWTEventListener(...). You can comment out the line and observe that then the action will occur in any case as you experience it currently.

Boro
  • 7,913
  • 4
  • 43
  • 85