Here is another approach that is not too harmful for hacking, if not elegant, and which, as far as I could tell, works. First, at the very top, I added the second logical name showPopup .
FocusListener should be as follows:
menu.addFocusListener(new FocusListener() { @Override public void focusLost(FocusEvent e) { System.out.println("LOST FOCUS"); isShowingPopup = false; } @Override public void focusGained(FocusEvent e) { System.out.println("GAINED FOCUS"); isShowingPopup = true; } });
The isShowingPopup value of isShowingPopup does not change anywhere - if it receives focus, it assumes it is shown, and if it loses focus, it assumes that it is not.
Then the ActionListener button on the button is different:
addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("isShowingPopup: " + isShowingPopup); if (showPopup) { Component c = (Component) e.getSource(); menu.show(c, -1, c.getHeight()); menu.requestFocus(); } else { showPopup = true; } } });
Now comes a new bit. This is the MouseListener button on the button:
addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { System.out.println("ispopup?: " + isShowingPopup); if (isShowingPopup) { showPopup = false; } } @Override public void mouseReleased(MouseEvent e) { showPopup = true; } });
Basically, mousePressed is called before the menu loses focus, so isShowingPopup reflects whether the popup was displayed before the button was clicked. Then, if the menu were there, we simply set showPopup to false , so the actionPerformed method actionPerformed not display the menu after it is called (after the mouse is released).
This was done as expected, in each case, except for one thing: if the menu was shown and the user clicked the button on the button, but released it outside it, actionPerformed never called. This meant that showPopup remained false and the menu was not shown the next time the button was pressed. To fix this, the mouseReleased method resets showPopup . As far as I know, the mouseReleased method mouseReleased called after actionPerformed .
I played with the resulting button a bit, doing everything I could think of a button, and worked as expected. However, I am not 100% sure that events will always occur in the same order.
Ultimately, I think it's at least worth a try.