-
May 3, 2010
Handling mouse events transparently in Swing
Code, TutorialI recently had the task to implement a basic roll-over behavior in a Swing GUI. Pretty simple — or so I thought.
The main problem in Swing is that mouse events are consumed automatically once you employ the very convenient MouseEventHandler, but that might cause some trouble in more sophisticated components. In my case it came down to a JTextPane which can display html and thus had clickable links in it. This JTextPane should be hidden by default and only be visible if a certain element was rolled over by the mouse cursor (similar to a pop-out menu). And there the headaches began: the mouse enter/exit events needed to be captured, but the JTextPane should still receive all mouse events in order to handle the link clicking.
To make things short: this is simple unsolvable in Swing alone. We needs to get our hands dirty and use some of the underlying AWT (yuck!) event handling.
Handling AWT directly is a bit messy. First you need to register an AWTEventHanlder to the so called Toolkit (the platform dependant part of the window handling, as far as I understand). You can narrow the events you will receive down by providing a bitmask (flag constants are provided by AWTEvent) which probably is a good idea — if your event handling code takes too long the GUI stalls.
The rest is pretty straightforward: the relative mouse cursor position is calculated and the abstract methods mouseEntered() and mouseExited() are called accordingly.
Note that I did not use the contains( Point p ) method provided by AWTComponents. If you look a little further below you’ll see the this method is overwriten and always returns false, a trick used to ensure that the mouse cursor changes correctly (i.e. displaying a hand cursor while hovering over a hyperlink).
package tools; import java.awt.AWTEvent; import java.awt.Cursor; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.AWTEventListener; import java.awt.event.MouseEvent; import javax.swing.JPanel; import javax.swing.SwingUtilities; public abstract class TransparentGlassPane extends JPanel implements AWTEventListener { private boolean mouseOver; public TransparentGlassPane() { super(); setOpaque(false); mouseOver = false; long mask = AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK; Toolkit.getDefaultToolkit().addAWTEventListener(this, mask); } public void eventDispatched(AWTEvent event) { MouseEvent me = (MouseEvent) event; Point point = SwingUtilities.convertPoint( me.getComponent(), me.getPoint(), this); boolean containsPoint = point.x >= 0 &amp;&amp; point.x <= getWidth() &amp;&amp; point.y >= 0 &amp;&amp; point.y <= getHeight(); if (!mouseOver &amp;&amp; containsPoint) { mouseEntered(); mouseOver = true; } else if (mouseOver &amp;&amp; !containsPoint) { mouseExited(); mouseOver = false; } } public abstract void mouseEntered(); public abstract void mouseExited(); @Override public boolean contains(int x, int y) { return false; // Be transparent wrt mouse cursor appearance } }I hope this might save some people out there the headaches I experienced while trying to get this work.
RSS feed for comments on this post. TrackBack URL


We recieved no comments so far. Be the first!
Right! I want to write a comment. »