Question

I have a JPopupMenu which contains an inner JMenu and a separator with addSeparator(). Due to some odd handling, I've added a MouseListener to the JPopupMenu which makes it invisible on a mouseExited event. This works fine, except that when the mouse tries to cross over the separator, it's triggering the event (even though the JPopupMenu is the super component).

If I remove the addSeparator() line, it works as expected.

Is there any way to work around this? Or have I not set up the listener properly?

The code is like the following:

JPopupMenu popupMenu = new JPopupMenu();
JMenu innerMenu = new JMenu("Inner");
// ... add JMenuItems
popupMenu.add(innerMenu);
popupMenu.addSeparator();
popupMenu.add(new JMenuItem("Exit"));

popupMenu.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseExited(MouseEvent e) {
        popupMenu.setVisible(false);
    }
});

Full Compilable Example

Simply comment and uncomment the popupMenu.addSeparator() line to notice the different behaviors

public class Test {


    public static void main(String[] args) throws Exception {
        if(!SystemTray.isSupported()) {
            throw new UnsupportedOperationException("SystemTray is not supported.");
        }

        final TrayIcon trayIcon = new TrayIcon(ImageIO.read(new File("resources/icon.gif")));
        final JPopupMenu popupMenu = new JPopupMenu();
        JMenu intervalMenu = new JMenu("Interval");
        ButtonGroup itemGroup = new ButtonGroup();

        JRadioButtonMenuItem oneSecondMenuItem = new JRadioButtonMenuItem("1 sec");
        itemGroup.add(oneSecondMenuItem);
        JRadioButtonMenuItem twoSecondMenuItem = new JRadioButtonMenuItem("2 sec");
        itemGroup.add(twoSecondMenuItem);

        intervalMenu.add(oneSecondMenuItem);
        intervalMenu.add(twoSecondMenuItem);

        popupMenu.add(intervalMenu);

        popupMenu.addSeparator();

        JMenuItem exitMenuItem = new JMenuItem("Exit");
        exitMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                SystemTray.getSystemTray().remove(trayIcon);
                System.exit(0);
            }
        });

        popupMenu.add(exitMenuItem);

        //Thanks to Artem Ananiev for this implementation idea
        //https://weblogs.java.net/blog/ixmal/archive/2006/05/using_jpopupmen.html
        trayIcon.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                if(e.getButton() == MouseEvent.BUTTON3) {
                    popupMenu.setLocation(e.getX() - 40, e.getY() - 40);
                    popupMenu.setInvoker(popupMenu);
                    popupMenu.setVisible(true);
                }
            }
        });

        popupMenu.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseExited(MouseEvent e) {
                popupMenu.setVisible(false);
            }
        });

        SystemTray.getSystemTray().add(trayIcon);
    }

}
Was it helpful?

Solution

Wow, you are using a sytem tray icon. That information might have been important to know. That is why a SSCCE should be posted with EVERY question.

Anyway the following seems to work:

if (! popupMenu.contains( e.getPoint() ) )
    popupMenu.setVisible(false);

Edit:

It looks like the problem is that the JSeparator does not listen for MouseEvents by default so all the mouse events are passed to its parent. So when you leave a JMenuItem, the mouseEntered() event is generated for the popup menu and then when you re-enter another JMenuItem the mouseExited() event is generated.

If you enable MouseEvents for the JSeparator then it look like the JPopupMenu doesn't get the event

//popupMenu.addSeparator();
popupMenu.add( new MySeparator() );
...


static class MySeparator extends JSeparator
{
    public MySeparator( )
    {
        super( JSeparator.HORIZONTAL );
        enableEvents(AWTEvent.MOUSE_EVENT_MASK);
    }

    /**
     * Returns the name of the L&F class that renders this component.
     *
     * @return the string "PopupMenuSeparatorUI"
     * @see JComponent#getUIClassID
     * @see UIDefaults#getUI
     */
    public String getUIClassID()
    {
        return "PopupMenuSeparatorUI";

    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top