Search code examples
javaswingmenujpopupmenu

How to prevent triggering of other events when closing a JPopupMenu by clicking outside it?


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


Solution

  • 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.