Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scrolling on a JComboBox popup hide it

My client is complaining that JComboBox popups often close when the scroll is being used over a JComboBox popup with no vertical scrollbar. (He seems to accidently use scrolling over it because he is using an Apple Magic Mouse.)

Any way to prevent this to happen ?

I know it has to do with the ComboBoxUI, but I would like a few pointer where to start. BasicComboPopup.handler is private (not reusable) and I don't see any code relative to any a MouseWhellListener in BasicComboPopup.

like image 895
Béatrice Cassistat Avatar asked Apr 12 '11 18:04

Béatrice Cassistat


4 Answers

As seen in the source, BasicPopupMenuUI contains a nested class, MouseGrabber, that implements the AWTEventListener interface. The receipt of MouseEvent.MOUSE_WHEEL in eventDispatched() cancels the popup as a function of isInPopup(). I know of no simple way to defeat the behavior.

Empirically, this example invokes show() from the actionPerformed() handler of a JButton; mouse wheel events are ignored. This might be a reasonable alternative for your user, perhaps combined with a suitable ActionEvent modifier mask.

In contrast, this example invokes show() in response to isPopupTrigger() in a MouseAdapter; as expected, mouse wheel events cancel the popup.

like image 155
trashgod Avatar answered Oct 04 '22 11:10

trashgod


Thanks to your suggestion, I've got an idea an found a solution by hacking AWTEventListeners.

    Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener()
    {
        public void eventDispatched(AWTEvent event)
        {
            if (event instanceof MouseWheelEvent)
            {
                Object source = event.getSource();
                if ((source instanceof JScrollPane) &&
                    (((JScrollPane) source).getParent().getClass().
                       getName().equals("com.apple.laf.AquaComboBoxPopup")))
                {
                    JViewport viewport = ((JScrollPane) source).getViewport();
                    if (viewport.getViewSize().height <= viewport.getHeight())
                        // prevent consuming if there is a vertical scrollbar
                        ((MouseWheelEvent) event).consume();
                }
            }
        }
    }, AWTEvent.MOUSE_WHEEL_EVENT_MASK);

Thanks guys !

like image 36
Béatrice Cassistat Avatar answered Oct 04 '22 11:10

Béatrice Cassistat


I have tested default behaviour of a combobox. And when I am scrolling over the popup it is fine it will not close it. But when I scroll outside it or even over the combobox itself then it disappears.

I do not know if you are after something like this but I have added the mouse wheel listener to the combobox this way if I detect the movement over the combobox there I am reshowing the popup. -- This bit only partially solves the issue that the mouse wheeling will not show the combo box when scrolling over the combobox.

import java.awt.HeadlessException;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ComboBoxMouseWheel
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createGUI();
            }
        });
    }

    private static void createGUI() throws HeadlessException
    {
        String[] items = new String[]
        {
            "oasoas", "saas", "saasas"
        };
        final JComboBox jcb = new JComboBox(items);
        jcb.addMouseWheelListener(new MouseWheelListener()
        {
            @Override
            public void mouseWheelMoved(MouseWheelEvent e)
            {
                System.out.println("ohjasajs");
                e.consume();
                jcb.showPopup();
            }
        });
        JPanel p = new JPanel();
        p.add(jcb);
        JPanel contentPane = new JPanel();
        contentPane.add(p);
        JFrame f = new JFrame();
        f.setContentPane(contentPane);
        f.setSize(300, 300);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
    }
}

I hope this is helpful even a bit. If you manage to solve other way please do share it with us. The solution provided by @trashgod seems doable but it looks so elaborated :), thus I propose mine approach an alternative.

Good luck, Boro.

like image 40
Boro Avatar answered Oct 04 '22 11:10

Boro


Here is a solution that will work in most cases

Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
            public void eventDispatched(AWTEvent event) {
                if (event instanceof MouseWheelEvent) {
                    Object source = event.getSource();

                    if (source instanceof JScrollPane) {
                        JScrollPane scroll = (JScrollPane) source;
                        if (scroll.getName().equals("ComboBox.scrollPane")) {
                            MouseWheelEvent sourceEvent = ((MouseWheelEvent) event);

                            for (MouseWheelListener listener : scroll.getListeners(MouseWheelListener.class)) {
                                listener.mouseWheelMoved(sourceEvent);
                            }

                            sourceEvent.consume();
                        }
                    }


                }
            }
        }, AWTEvent.MOUSE_WHEEL_EVENT_MASK);
like image 37
Alex Liang Avatar answered Oct 04 '22 11:10

Alex Liang