javaswingjpopupmenu

Why isn't componentHidden called for my JPopupMenu?


I want to be notified when my JPopupMenu is hidden — whether because an item was selected, the menu was dismissed, or setVisible(false) was called on it. Here is my test code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class A extends ComponentAdapter implements Runnable, ActionListener {
    private JButton b;

    public static void main(String[] args) {
        EventQueue.invokeLater(new A());
    }

    public void run() {
        JFrame f = new JFrame("Test");
        b = new JButton("Click me");
        b.addActionListener(this);
        f.add(b);
        f.pack();
        f.setVisible(true);
    }

    public void actionPerformed(ActionEvent e) {
        JPopupMenu pm = new JPopupMenu();
        pm.addComponentListener(this);
        pm.add("Popup...");
        pm.add("...menu!");
        pm.show(b, 10, 10);
    }

    public void componentShown(ComponentEvent e) { System.out.println("componentShown"); }
    public void componentHidden(ComponentEvent e) { System.out.println("componentHidden"); }
}

Regardless of how I interact with the menu, neither of the two ComponentListener methods are being called. Why is that? Is there different/better/correct way of finding out when my JPopupMenu is hidden?

Thanks,

Cameron


Solution

  • JPopupMenu has a special listener for visibility change events:

    pm.addPopupMenuListener(new PopupMenuListener() {
        @Override
        public void popupMenuCanceled(PopupMenuEvent e) {
            System.out.println("cancelled");
        }
    
        @Override
        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
            System.out.println("vanishing");
        }
    
        @Override
        public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
            System.out.println("appearing");
        }
    });
    

    Note, however, as method names hint, they are called before visibility changes, so if you're calling isVisible() somewhere in the event handlers, you should be aware of that, for example:

    @Override
    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
        updateMenu();
    }
    
    private void updateMenu() {
        if (!menu.isVisible()) { // this won't work!
            // perform some updates
        }
    }
    

    With regards to why ComponentListener isn't sending you events on the menu disappearing, this might explain:

    The component-hidden and component-shown events occur only as the result of calls to a Component 's setVisible method. For example, a window might be miniaturized into an icon (iconified) without a component-hidden event being fired.

    Source: ComponentListener tutorial (non-canonical perhaps, but from the horse's mouth.)

    Consider that in conjunction with JPopupMenu's implementation of setVisible:

        public void setVisible(boolean b) {
            // Not supported for MenuComponents
        }
    

    And you might know how it so happens, but not why it happens (what is the justification and where is that properly documented?)