Search code examples
javaswingjpopupmenu

JPopupMenu doesn't disappear like it should


I have a Java application used to run tournaments in which I built an auto-suggestion feature that gets names from a database and displays them in a JPopupMenu. I haven't been able to replicate this bug on demand, but once in a while one of the JPopupMenus will disappear like normal with the exception that an outline of where it was is still on the screen and is displayed over everything including other programs even if my application is minimized.

Here is a screenshot of what I'm talking about: enter image description here

You can see that underneath "Espinoza" some remnant of the JPopupMenu is still being displayed. This sometimes contains text inside and other times just has the background color only in an empty box. This remnant is purely cosmetic and I haven't found any way of interacting with it either physically or programatically (hot-coding).

Here is the method I'm using to display the JPopupMenu:

private void resetLastNamePopup() {

    Thread t = new Thread() {

        @Override
        public void run() {

            lnPopup.setVisible(false);
            lnPopup.removeAll();

            if(DBHSDatabaseIntermediary.isConnected()) {

                if(!(fnTextField.getText().equals("") && lnTextField.getText().equals(""))) {

                    JMenuItem item = null;
                    String[] names = DBHSDatabaseIntermediary.getLastNames(fnTextField.getText(), lnTextField.getText());
                    for(int i=0; i < names.length; i++) {

                        if(!names[i].equals(lnTextField.getText().trim())) {

                            item = new JMenuItem(names[i]);
                            item.addActionListener(lnActionListener);
                            item.addMouseListener(NewPlayerPanel.this);
                            lnPopup.add(item);

                        }

                    }

                    if(names.length > 0 && !names[0].equals("")) {

                        lnPopup.setVisible(true);

                    }
                    lnPopup.grabFocus();

                }

            }

        }// ends run()

    };

    t.start();

}

Thank you in advance.


Solution

  • Swing methods and constructors must be called on the AWT event dispatch thread. You are calling those methods on a different thread. The result is “undefined behavior”—which usually means things will work sometimes, but not all the time.

    You need to separate Swing calls from database calls, which is done using EventQueue.invokeLater (or its alias, SwingUtilities.invokeLater):

    private void resetLastNamePopup() {
    
        lnPopup.setVisible(false);
        lnPopup.removeAll();
    
        final String fn = fnTextField.getText();
        final String ln = lnTextField.getText();
    
        Thread t = new Thread() {
    
            @Override
            public void run() {
                if(DBHSDatabaseIntermediary.isConnected()
                        && !fn.isEmpty() && !ln.isEmpty()) {
    
                    final String[] names =
                        DBHSDatabaseIntermediary.getLastNames(fn, ln);
    
                    // Rebuild JPopupMenu in AWT event thread.
                    EventQueue.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            for (String name : names) {
                                if (!name.equals(ln)) {
                                    JMenuItem item = new JMenuItem(name);
                                    item.addActionListener(lnActionListener);
                                    lnPopup.add(item);
                                }
                            }
    
                            if (names.length > 0 && !names[0].isEmpty()) {
                                lnPopup.setVisible(true);
                                lnPopup.grabFocus();
                            }
                        }
                    });
                }
            }// ends run()
    
        };
        t.start();
    }
    

    For more information, see the javax.swing package contract, and Concurrency in Swing in the Java Tutorials.