Swing won't trigger mouseEntered/mouseExited properly after mouse wheel events?
Asked Answered
S

2

6

I have an issue where Swing (in Java 1.6, Windows) doesn't seem to trigger mouseEntered and mouseExited events the way I want it to. I have an application where I wish to have a number of JPanels stacked vertically in a JScrollPane, and that they should be highlighted with a different colour when the mouse is over them. Simple enough problem, but whenever I scroll using the mouse wheel, it doesn't quite behave.

I have made a sample application to illustrate my problem (code found below). The images below are from that one, not the "real" application.

When I hold the mouse cursor over the edge of a panel, it's highlighted correctly. Now, when I use the mouse wheel to scroll down, I expect the cursor to be over box B, and the proper mouseEntered/mouseExited events to be triggered so that A becomes white and B becomes red.

alt text
(source: perp.se)

alt text
(source: perp.se)

However, that doesn't seem to happen.

Now, B becomes highlighted if I trigger another mouse event, be it "move 1 pixel", "click a button" or "scroll another step". Knowing this, I could perhaps solve it in a hackish way, but I'd rather not if there's a proper solution.

So basically what I'm wondering is if this is to be regarded as a bug in Swing, or am I just doing things wrong?

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

public class ScrollTest extends JFrame {

    public static class LetterPanel extends JPanel {

        private static final Font BIG_FONT = new Font(Font.MONOSPACED, Font.BOLD, 24);

        public LetterPanel(String text) {
            setBackground(Color.WHITE);
            setBorder(BorderFactory.createLineBorder(Color.BLACK));

            addMouseListener(new MouseAdapter() {

                @Override
                public void mouseEntered(MouseEvent e) {
                    setBackground(Color.RED);
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    setBackground(Color.WHITE);
                }
            });

            setLayout(new GridLayout(1, 1));
            setPreferredSize(new Dimension(-1, 50));

            JLabel label = new JLabel(text, SwingConstants.CENTER);
            label.setFont(BIG_FONT);
            add(label);
        }
    }

    public ScrollTest() {
        setLayout(new GridLayout(1, 1));
        setSize(400, 400);

        JPanel base = new JPanel();

        JScrollPane jsp = new JScrollPane(base);
        jsp.getVerticalScrollBar().setUnitIncrement(16);
        add(jsp);

        base.setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0; 
        gbc.gridheight = 1;
        gbc.gridwidth = 1;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(0, 0, 10, 0);
        gbc.weightx = 1.0;

        for (char c = 'A'; c <= 'Z'; c++) {
            base.add(new LetterPanel(String.valueOf(c)), gbc);
            gbc.gridy++;
        }
    }

    public static void main(String[] args) {
        final JFrame f = new ScrollTest();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                f.setVisible(true);
            }
        });
    }
}
Syncopated answered 20/11, 2009 at 8:47 Comment(3)
The restrictions are there for a reason. Without it, SO would have had quite a bit of SPAM-posts. I added the images and removed your remarks about how "retarded" the rules here are.Spigot
Besides those remarks, you've asked an excellent question: clear images, a good snippet of code: well done. I'm no Swing guru, but I'll have a look at it later on (if it isn't answered already by then). +1Spigot
Thanks for the edit. It was a bit frustrating to first fight with the problem, write the question, write the snippet, take good screenshots, upload them to a server and then have SO barf at my post just because I'm a new user. I feel better now. :-)Syncopated
W
5

This seems like a similiar problem to the one described in Tooltips and Scrollpanes. That is, no mouse events are generated because the mouse itself doesn't move, the viewport moves. I'm not sure the exact solution other using the AdjustmentListener to track the component at the mouse location. Every time is changes you can fire a mouseExited event to the previous panel and a mouseEntered event to the new panel.

Willms answered 20/11, 2009 at 21:15 Comment(0)
C
3

I can get your code to reproduce this reliably but only when I don't quite finish the scrolling. On my mouse at least there is sort of a "catch" when the mouse wheel finished scrolling. If I scroll very slowly I can have it move but it doesn't change the highlight until the mouse wheel has reached the "catch".

When I do that the mouse enter message is received on the previous panel (same behaviour you are seeing).

Looking at it I scroll the mouse and it does not actually receive the exited/entered events unless I scroll enough to have the mouse wheel "catch". It is possible that Windows does not send the message to Java until the "catch" happens... from my testing that is what it looks like.

You might want to look into the MouseWheelListener interface and the MouseInfo class. I guess you might be able to detect the wheel movement and then figure out where you are with MouseInfo.getPointerInfo().getLocation() and then figure out what component you are over and change the highlighting.

Cop answered 20/11, 2009 at 11:1 Comment(1)
Curious... Your "Catch" could be rough enough that it moves the mouse a little, causing a mouse event to come through. You might try it with the bottom of the mouse pointed into the air so the light can't get any traction. It might also be that your scroll wheel causes a button-3 down or something--something that wouldn't otherwise be noticed.Worn

© 2022 - 2024 — McMap. All rights reserved.