Scott's Solutions - Using Swing's glass pane feature to perform overlay drawing
How can we use Swing's glass pane feature to perform overlay drawing? Every JFrame
has an associated glass pane that, as the name implies, is transparent. But we can use the glass pane just like any other Swing component-it can be, in fact, any JComponent
that we want it to be.
The problem that ultimately led us to this discussion was the need to move the cursor programmatically. Most GUI toolkits have some function that you can call that warps or moves the mouse to a particular location on the screen. Java does not have such a method (and don't get excited-I'm not going to provide such a method in this column).
Human interface design experts will argue (and I'd have to agree) that under almost all circumstances, it's very unfriendly for the program to move the cursor: the mouse should be under control of the user. But there are exceptions to every rule, and one of those involves possible implementations of a help function. One way to implement a help function is literally to show the user on the screen what he/she should do. The user sees the cursor move to a text field, sees text being typed into a field, sees the cursor move to a button, sees the button depress, and so on. If you want to get really advanced, you could use the Java Media Framework to help you with the animation and to time audio to the movements on the screen.
All of these operations are relatively straightforward in Java except for moving the cursor: You can set the text of a text field, you can draw a button in a depressed state, etc. As we think about it, however, we realize that we don't actually need to move the cursor to implement this help function: We just need the screen to look like we're moving the cursor. That's where the glass pane comes in.
This is our strategy: We'll change the cursor of our application to a 1-pixel dot, hiding it from the user. Then we'll animate an image (that just so happens to look like the cursor) by drawing it onto the glass pane of the frame we're working with. When we do that, only the image of the cursor on the glass pane is drawn; the rest of the glass pane remains transparent. The user sees a moving cursor and the underlying components at the same time.
Let's start with the glass pane itself. The glass pane can be any JComponent. Usually, you'll create a class that acts as the glass pane because you need to program that class to do something. In our case, we need the glass pane to draw the animated cursor. We'll do that by overriding the paint method and using really simple graphics calls to do the animation (see Listing 1).
This class is responsible for animating a cursor from the points that are passed to its start() method. It does this by using the MousePath class (which we won't show), which returns successive points along the path between those points; the AnimatePane class just repaints the cursor in a new point every 50 ms. Interested parties are notified when the animation starts or stops via a property change event.
Listing 2 shows how the glass pane is associated with a frame. The doit() method (presumably called in response to some user input) starts the animation on the glass pane. In addition, this skeleton code shows us two important points about working with the glass pane:
- The setGlassPane() method associates the frame with the glass pane.
- Glass panes are invisible by default. To make them visible, you must call the setVisible() method. However, even when visible, the pane is transparent: You see through the places on the pane where you don't draw. But if you left the pane invisible, you wouldn't see anything on it at all.
Admittedly, this solution is not perfect: We've only hidden the real cursor while the cursor is over this frame. While the animation is playing, the user can still move the mouse. If this moves the cursor into another window, the user will see two cursors: the one we're animating and the real one. We could arrange to hide the cursor on the rest of the Java windows in our application, but we can't solve this problem for other windows or the desktop. Similarly, if the user types in characters during the animation, these events have the potential to confuse whatever animation we are doing. Our implementation of the glass pane installs an empty mouse listener, which prevents mouse input from interfering in this way, but there's no way to block keyboard input at present.
There are, of course, other uses for the glass pane-when an application starts, it could animate an arrow on the glass pane that points to the start button to give the user a hint (if you think that really provides useful feedback). When you're doing a calculation, you can animate a timer image on the glass pane. And so on-the more you use the glass pane, the clearer these ideas will become.
Scott Oaks is a Systems Engineer for Sun Microsystems, where he focuses on practical applications of Java Technology. He is the co-author, with Henry Wong, of Java Threads. He can be contacted at email@example.com.