Skip to main content

Java Doodle: fading translucent windows, on PC & Mac

Posted by joshy on June 6, 2008 at 3:05 PM PDT

This is the next in my series of Java Doodles. There is a link to my previous one in the references below. This time I'm going to show you how to make a translucent window by setting the opacity value using new apis in JavaSE 6 update 10. However, I'm also going to show you how to make it fade in when you mouse over it, similar to some popular chat applications, as well as work properly on the Mac and degrade gracefully when running versions of Java.

The basic idea

Here is a screenshot of the basic app. It's a simple translucent window. I've you've read the recent SDN article then you probably already know the basic APIs. This app will add Mac support, a rollover effect, and show you how to degrade gracefully.




Translucent Window

Degrading gracefully

Turning on translucency with Java SE 6 is as simple as calling a new private API: com.sun.awt.AWTUtilities.setWindowOpacity(float). To make it degrade gracefully on older VMs we could test if the feature is supported using the isTranslucencySupported() method. However, this API will probably change in Java 7 (since it's currently in a non-public package) and of course it doesn't exist on older versions of Java. If I want to compile this on an older VM (such as on my MacBook) then I simply can't call the API directly. Instead I'll use reflection and wrap it in a try catch block. I've created my own method called setAlpha(float) which will safely call the setWindowOpacity() method.

    private static void setAlpha(float alpha) {
        try {
            Window win = frame;
            //invoke AWTUtilities.setWindowOpacity(win, 0.0f);
            Class awtutil = Class.forName("com.sun.awt.AWTUtilities");
            Method setWindowOpaque = awtutil.getMethod("setWindowOpacity", Window.class, float.class);
            setWindowOpaque.invoke(null, win, (float)alpha);
        } catch (Exception ex) {
            //ex.printStackTrace();
        }
    }

Now, if the method doesn't exist at runtime because the user has an older version of Java then the exception will be caught and nothing will happen. It degrades gracefully.

Mac support

So what about the Mac? Well, Mac OS X doesn't have a version of Java SE 6 update 10 yet, however in Leopard they did introduce a new API for doing translucency. Using this new support we can add translucency on Mac with a single extra line. In the catch block above we can add this:

        } catch (Exception ex) {
            //ex.printStackTrace();
            frame.getRootPane().putClientProperty("Window.alpha", new Float(alpha));
        }

This sets a magic property on the rootpane of the frame called Window.alpha. Because this is a client property it will have no effect on other platforms that don't know about it. Again, degrading gracefully.

The fade effect

Now that we can make a window translucent lets do something interesting with it. I want the window to be mostly transparent, but whenever the mouse moves into the window I want to return to mostly opaque. So let's say the opacity will go from 0.5 to 0.9. I could set a mouse listener on the background of th window, but that wouldn't work on subcomponents. I could also put in a glasspane to check for mouse events, but then I would have to redispatch all events back down to the real components as I did in my book Swing Hacks. Very messy. Fortunately, as of Java 5, we have an easier method: just poll the global mouse position using the MouseInfo class. Here's a Runnable which does that:

    private static Runnable mouseWatcher = new Runnable() {

        public void run() {
            boolean previouslyInside = false;
            while (true) {
                try {
                    Thread.sleep(100);
                    Point pt = MouseInfo.getPointerInfo().getLocation();
                    boolean currentlyInside = frame.getBounds().contains(pt);
                    if (previouslyInside != currentlyInside) {
                        if (currentlyInside) {
                            setAlpha(0.9f);
                        } else {
                            setAlpha(0.5f);
                        }
                    }
                    previouslyInside = currentlyInside;

                } catch (InterruptedException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    };

The code above will check the mouse position ten times a second. If the mouse has moved in or out of the window then it changes the opacity. It's that simple.

Run the app

You can try the live application here. It will work on MacOSX 10.5 or Windows/Solaris/Linux if you have a recent beta of JavaSE 6 update 10. On older versions of Java 6 you won't get the translucency effect, but the app will still run.

That's all there is to it. This is just one of the many cool new features in JavaSE 6 update 10. Go check'em out then come back to my blog for more Java Doodles in the future.


Bookmark and Share

References

Comments

Great blog Josh...good stuff as always! Java 6u10 is looking great!

If its a com.sun class doesn't that mean it most probably wont be implemented on other OS's? It's a fun Doodle; What about custom shaped windows?

I have no news on 6u10 for Mac. However, as this blog shows, you can do some of the u10 features on Mac already.

I mean, you can't keep using these if platform workarounds on WORA stuff forever, c an you?

I notice this screenshot is on OSX. Can you comment yet on the availability of 6u10 on Mac yet? Now or after next week?

mediarazzo changing the definition of setBackground() would effectively be changing the API, which is not allowed in an update release. However, that method or something like it may end up being the final API which is included in JavaSE 7.

I'm curious to know why Sun didn't fix JComponent.setBackground(Color c) where Color has an alpha parameter. That would seem to be an obvious solution.

It is a nice example though.

Polling the global mouse position? ... Polling? Seriously, there has to be a better way. Why isn't there a global mouse position event listener?

Josh, fixing a bug isn't changing an API. And if the method responds incorrectly to colours with transparancy that's a bug that needs to be fixed. Of course the behaviour of the API will change, but that's the nature of bugfixes, to change the behaviour of APIs or applications so they work as specified...