Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 244
BCarpe Avatar asked Jun 03 '11 17:06

BCarpe


2 Answers

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;
}
like image 64
Adam Avatar answered Nov 02 '22 23:11

Adam


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.

like image 31
Boro Avatar answered Nov 03 '22 01:11

Boro