ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Hacking Swing: Translucent Windows
Pages: 1, 2, 3

The code is pretty simple, but it has two big flaws. First, if the window is moved, the background won't be refreshed automatically. paintComponent( ) only gets called when the user resizes the window. Second, if the screen ever changes, it won't match up with the background anymore.



You really don't want to update the screenshot often, though, because that involves hiding the window, taking a new screenshot, and then reshowing the window—all of which is disconcerting to the user. Actually detecting when the rest of the desktop changes is almost impossible, but most changes happen when the foreground window changes focus or moves. If you accept this idea (and I do), then you can watch for those events and only update the screenshot when that happens:

public class TransparentBackground extends JComponent
        implements ComponentListener, WindowFocusListener,
        Runnable {
    private JFrame frame;
    private Image background;
    private long lastupdate = 0;
    public boolean refreshRequested = true;
    public TransparentBackground(JFrame frame) { 
        this.frame = frame;
        updateBackground( );
        frame.addComponentListener(this); 
        frame.addWindowFocusListener(this);
        new Thread(this).start( );
    }
    public void componentShown(ComponentEvent evt) { repaint( ); }
    public void componentResized(ComponentEvent evt) { repaint( ); }
    public void componentMoved(ComponentEvent evt) { repaint( ); }
    public void componentHidden(ComponentEvent evt) { }

    public void windowGainedFocus(WindowEvent evt) { refresh( ); }    
    public void windowLostFocus(WindowEvent evt) { refresh( ); }

First, make the panel, TransparentWindow, implement ComponentListener, WindowFocusListener, and Runnable. The listener interfaces will let the panel catch events indicating that the window has moved, been resized, or the focus changes. Implementing Runnable will let the panel create a thread to handle custom repaint( )s.

The implementation of ComponentListener involves the four methods beginning with component. They each simply call repaint( ) so that the background will be updated whenever the user moves or resizes the window. Next are the two window focus handlers, which just call refresh( ), as shown here:

public void refresh( ) {
    if(frame.isVisible( )) {
        repaint( );
        refreshRequested = true;
        lastupdate = new Date( ).getTime( );
    }
}
public void run( ) {
    try {
        while(true) {
            Thread.sleep(250);
            long now = new Date( ).getTime( );
            if(refreshRequested &&
                ((now - lastupdate) > 1000)) {
                if(frame.isVisible( )) {
                    Point location = frame.getLocation( );
                    frame.hide( );
                    updateBackground( );
                    frame.show( );
                frame.setLocation(location);
                    refresh( );
                }
                lastupdate = now;
                refreshRequested = false;
                }
            }
        } catch (Exception ex) {
            p(ex.toString( ));
            ex.printStackTrace( );
        } 
    }

refresh( ) ensures that the frame is visible and schedules a repaint. It also sets the refreshRequested boolean to true and saves the current time, which will become very important shortly.

The run( ) method sleeps constantly, waking up every quarter-second to see if a refresh has been requested, and whether it has been more than a second since the last refresh. If more than a second has passed and the frame is actually visible, then run( ) will save the frame location, hide it, update the background, then put the frame back in place and call refresh( ). This ensures that the background is never updated more than needed.

So, why all of this rigmarole about using a thread to control refreshing? One word: recursion. The event handlers could simply call updateBackground( ) and repaint( ) directly, but hiding and showing the window to generate the screenshot would cause more focus-changed events. These would then trigger another background update, causing the window to hide again, and so on, creating an infinite loop. The new focus events are generated a few milliseconds after refresh( ) is processed, so simply checking for an isRecursing flag wouldn't stop a loop.

Pages: 1, 2, 3

Next Pagearrow