Вот еще один подход, который не так уж плох от взлома, если не элегантный, и который, насколько я могу судить, работает. Во-первых, на самом верху я добавил второй логический элемент с именем showPopup
.
Значение FocusListener
должно быть следующим:
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;
}
});
Логическое значение isShowingPopup
не изменяется где-либо еще - если оно получает фокус, оно предполагает, что оно показано, и если оно теряет фокус, оно предполагает, что это не так.
Далее ActionListener
на кнопке отличается:
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;
}
}
});
Теперь наступает действительно новый бит. Это MouseListener
на кнопке:
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;
}
});
Как правило, mousePressed
вызывается до того, как меню теряет фокус, поэтому isShowingPopup
отражает, отображалось ли всплывающее окно до нажатия кнопки. Затем, если меню было, мы просто устанавливаем showPopup
на false
, чтобы метод actionPerformed
не отображал меню после его вызова (после отпускания мыши).
В каждом случае это происходило, как и ожидалось, за исключением одного: если меню показывалось, и пользователь нажимал кнопку мыши, но отпускал ее снаружи, actionPerformed
никогда не вызывалось. Это означало, что showPopup
оставалось ложным, и меню не отображалось при следующем нажатии кнопки. Чтобы это исправить, метод mouseReleased
сбрасывает showPopup
. Насколько я могу судить, метод mouseReleased
вызывается после actionPerformed
.
Я немного поиграл с полученной кнопкой, выполняя все, что мог придумать для кнопки, и она работала, как и ожидалось. Однако я не уверен на 100%, что события всегда будут происходить в одном и том же порядке.
В конечном счете, я думаю, что, по крайней мере, стоит попробовать.