(another) Translucent window

you have probably seen this before, it is another screenshot-based implementation of a translucent window. (is there any other way besides JNI? if so, this is news to me.)

some things to note about it:

  • initialises with a string for the window image(found using getClass().getResource() on it)
  • it doesn’t refresh its background, except when being dragged, and even then, it only grabs a shot of the area it is moving into. good, because the window never flickers bad because the background more easily gets out of sync if you never move it.
  • you can put ordinary components onto its ContentPane
  • uses double buffering so should be very flicker-free.
  • performance is better with GIF images than PNG images. (ie, transparency is much faster than translucency)
  • for best results, use with setUndecorated(true) to hide the JFrame’s window decorations.

a picture is worth a thousand words as they say:

http://juddman.googlepages.com/bgw-example.png

here is the class file:
BackgroundWindow.java


import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
import javax.imageio.*;
import java.awt.event.*;


class InvisiblePanel extends JPanel
{
	public void paint(Graphics g)
	{
		paintChildren(g);
	}
}

public class BackgroundWindow extends JFrame implements MouseListener, MouseMotionListener
{
	final int     delay = 1;
	int           x, y, w, h,
	              oldX, oldY,
	              mouseX = 0,
	              mouseY = 0;
	long          time;
	
	BufferedImage image,
	              bgImage,
	              bgBuffer,
	              windowBuffer,
	              nativeImage;
	Robot         rob;
	
	public BackgroundWindow(String picture)
	{
		x = 0; y = 0;
		try{
			image = ImageIO.read(getClass().getResource(picture));
		} catch (Throwable e) {image = null;}
		w = image.getWidth();
		h = image.getHeight();
		setSize(w, h);
		addMouseListener(this);
		addMouseMotionListener(this);
		setContentPane(new InvisiblePanel());
	}
	
	public void paint(Graphics windowGraphics)
	{
		if(nativeImage == null || bgImage == null) {
			nativeImage = getGraphicsConfiguration().createCompatibleImage(w, h);
			windowBuffer = getGraphicsConfiguration().createCompatibleImage(w, h);
			setVisible(false);
			try{
				Thread.sleep(100);
				rob = new Robot(getGraphicsConfiguration().getDevice());
			}
			catch(Exception e) {System.exit(1);}
			bgImage = rob.createScreenCapture(new Rectangle(x, y, w, h));
			bgBuffer = rob.createScreenCapture(new Rectangle(x, y, w, h));
			setVisible(true);
		}
		Graphics g = windowBuffer.getGraphics();
		nativeImage.getGraphics().drawImage(bgImage, 0, 0, this);
		nativeImage.getGraphics().drawImage(image, 0, 0, this);
		g.drawImage(nativeImage, 0, 0, this);
		getContentPane().paint(g);
		windowGraphics.drawImage(windowBuffer, 0, 0, this);
		
	}
	public void repaint()
	{
		bgImage = null;
		super.repaint();
	}
	public void setLocation(int newX, int newY)
	{
		if(bgBuffer != null) {
			int dx = newX - x;
			int dy = newY - y;
			//first, copy background image onto iteslf displaced.
			bgBuffer.getGraphics().drawImage(bgImage, -dx, -dy, this);
			
			//second, copy new region into background...
			if(dx > 0) {
				int my = dy;
				if(my < 0) my = -my;
				Rectangle r = new Rectangle(x + w, y-my, dx, h+my*2);
				Image strip = rob.createScreenCapture(r);
				bgBuffer.getGraphics().drawImage(strip, w-dx, -dy-my, this);
			}
			else if(dx < 0) {
				int my = dy;
				if(my < 0) my = -my;
				Rectangle r = new Rectangle(x+dx, y-my, -dx, h+my*2);
				Image strip = rob.createScreenCapture(r);
				bgBuffer.getGraphics().drawImage(strip, 0, -dy-my, this);
			}
			if(dy > 0) {
				Rectangle r = new Rectangle(x, y+h, w, dy);
				Image strip = rob.createScreenCapture(r);
				bgBuffer.getGraphics().drawImage(strip, -dx, h-dy, this);
			}
			else if(dy < 0) {
				Rectangle r = new Rectangle(x, y+dy, w, -dy);
				Image strip = rob.createScreenCapture(r);
				bgBuffer.getGraphics().drawImage(strip, -dx, 0, this);
			}
			//finally, put bgBuffer back to bgImage.
			bgImage.getGraphics().drawImage(bgBuffer, 0, 0, this);
			
			this.x = newX;
			this.y = newY;
			paint(getGraphics());
		}
		super.setLocation(x, y);
		pause(1); //stops jitter
	}
	
	
	public void mousePressed(MouseEvent e){mouseX = e.getX(); mouseY = e.getY();}
	public void mouseReleased(MouseEvent e){mouseX = e.getX(); mouseY = e.getY();}
	public void mouseClicked(MouseEvent e){mouseX = e.getX(); mouseY = e.getY();}
	public void mouseEntered(MouseEvent e){mouseX = e.getX(); mouseY = e.getY();}
	public void mouseExited(MouseEvent e){mouseX = e.getX(); mouseY = e.getY();}
	
	//for dragging the window by any blank area
	public void mouseDragged(MouseEvent e){
		//first, calculate the distance moved...
		long now = System.currentTimeMillis();
		if(now - time < delay) return;
		time = now;
		
		int dx = e.getX() - mouseX;
		int dy =  e.getY() - mouseY;
		
		setLocation(x + dx, y + dy);
		mouseX = e.getX() - dx; mouseY = e.getY() - dy;
	}
	public void mouseMoved(MouseEvent e){}
	
	static void pause(long time) {
		try{ Thread.sleep(time);}
		catch(Exception e){}
	}
}

and here’s how i used it in the screenshot above:


import java.awt.*;
import javax.swing.*;
import java.awt.image.*;

public class Demo
{
	public static void main(String[] args)
	{
		JFrame mainFrame = new BackgroundWindow("winda.png");
		mainFrame.setUndecorated(true);
		JPanel cp = (JPanel)mainFrame.getContentPane();
		cp.setLayout(null);
		
		JButton b = new JButton("Hi there!");
		b.setLocation(130,10);
		b.setSize(100,30);	
		cp.add(b);
		
		
		JTextArea text = new JTextArea("this doesnt work quite right.");
		text.setFont(new Font("Arial", Font.BOLD, 14));
		text.setForeground(Color.blue);
		text.setLineWrap(true);       //line wrapping
		text.setWrapStyleWord(true);  //wrap on end of words.
		
		JScrollPane scroll = new JScrollPane(text);
		
		scroll.setSize(300,70);
		scroll.setLocation(40, 70);
		cp.add(scroll);
		
		mainFrame.setVisible(true);
	}
}

I gave up trying to do it on PCs, on a mac its just setting the window to translucent.

Nice work JuddMan looks great!!
I’ve been having a problem and you might be the person to help me, do you know how to make a color chooser translucent (well the background anyway) ? I’m talking about the panels on a color chooser that isn’t displayed in a JDialog.

what you could do is make a new class extending it and override its paint method to skip paintComponent and paintBorder or something. that’s what i had to do with the content pane.
though it might be more difficult than that.

I decided to make something a bit more interactive with it: a bouncy ball that you can pick up and throw around your screen
http://www.adam.com.au/kellyjones/YABB/Bouncy.jar
http://juddman.googlepages.com/Bouncy.jar

it’s an ‘executable’ jar. just double click it to run if your java is installed correctly.

(all source is included in the jar so just unzip and have a look it if you are curious / paranoid)

They say simple things ammuse small minds.
This kept me ammused for a couple of minutes ;D

you could add things like pinball bumpers that the user could place on screen and see how many points they can get with a single throw.

really really cool ;D

I just tried the code at the top, opened a graphics program, created a blue box and saved it as ‘winda.png’.
The demo worked but sadly the window wasn’t translucent at all.

Was the picture you saved translucent in the graphics program?

here’s winda.png used in the screenshot above:
http://juddman.googlepages.com/winda.png

I’ve always just set the background color of the JFrame to have alpha in it. But you can’t get curved edges too easily that way.

what java version and OS? and can you post an example? if i try it on 1.4 or 1.6, it just shows the solid colour.

Here’s another demo.

Download:
http://juddman.googlepages.com/ConnectionTest.jar

  • Makes a noise if the net drops out. handy for gamers.
  • For best results, change www.google.com in NetConn.java to your own ISP (unzip the JAR to get at the source).

I should have made it do something smarter like prompt for a hostname to ping on the first run, but i didn’t.

I do it in Mac OS X, and looking above it appears that this is the only OS you can do that on. Bummer.

Ahhh…
No wonder I couldn’t see the code was that setting the transparency of the loaded image…
:wink: