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

java, mouse, mouseevent, mousewheel, swing

Solution

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.

Problem

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. (source: perp.se) (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); } }); } } ```

Original source