Problem with painted JButtons

Hey, I have a problem with my rendered JButtons in my applications. It seems that when I render them on a decorated JFrame that they are drawn according to the graphics coordinate system, i.e. starting from 0,0 behind the top bars, but the mouse responds on them as they were placed according to the frame itself. Best shown with an example I guess:


import java.awt.Color;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;

import javax.swing.JButton;
import javax.swing.JFrame;

public class TestJavaForum {

	public static void main(String[] args) {
		new TestJavaForum().init();
	}

	private TestJavaForumScreen screen;
	private JButton testButton;

	public void init() {
		screen = new TestJavaForumScreen();
		screen.initiateScreen();

		testButton = new JButton("I am an annoying BUTTON");

		JFrame frame = screen.getScreen();
		Container contentPane = frame.getContentPane();
		contentPane.setLayout(new FlowLayout());
		contentPane.add(testButton);
		renderLoop();
	}

	public void draw(Graphics2D g) {
		g.setColor(Color.GREEN);
		g.fillRect(0, 0, screen.getScreen().getWidth(), screen.getScreen()
				.getHeight());
		screen.getScreen().getLayeredPane().paintComponents(g);
	}

	public void renderLoop() {
		while (true) {
			Graphics2D g = screen.getGraphics();
			draw(g);
			g.dispose();
			screen.update();
			try {
				Thread.sleep(15);
			} catch (InterruptedException ex) {
			}
		}
	}
}
class TestJavaForumScreen {

	private GraphicsDevice device;
	private JFrame screen;

	public TestJavaForumScreen() {
		GraphicsEnvironment environment = GraphicsEnvironment
				.getLocalGraphicsEnvironment();
		device = environment.getDefaultScreenDevice();
		screen = new JFrame(device.getDefaultConfiguration());
	}

	public void initiateScreen() {
		screen.setUndecorated(false); // < - Problem
		screen.setResizable(false);
		screen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		screen.setSize(320, 200);
		screen.setVisible(true);	
		screen.createBufferStrategy(2);
	}

	public Graphics2D getGraphics() {
		BufferStrategy strategy = screen.getBufferStrategy();
		return (Graphics2D) strategy.getDrawGraphics();
	}

	public void update() {
		BufferStrategy strategy = screen.getBufferStrategy();
		if (!strategy.contentsLost()) {
			strategy.show();
		}
		Toolkit.getDefaultToolkit().sync();
	}

	public JFrame getScreen() {
		return screen;
	}
}

The button appears under the topbar, but can be click as though it was a few pixels below. It works correctly with setUndecorated(true), but I want it to be decorated :’( How do I get around that problem?

This is because the graphics object you’re passing in doesn’ t have the correct
transform (and clpping, and some other attributes) set, which is normally done done by
Swing repainting code. To see those attributes, override your Button’s paintComponent
and print out the passed graphics object during “normal” repaint request. I’m not suggesting
to just blindly copy what you get there, though.

Also, you’re doing the rendering on non-EventDispatchThread, bad idea.
Suppose a repaint event comes in, and Swing will do some painting on EDT while
you’re doing your rendering on the main thread.

Depending on how consistent you want your framerate to be, why not
just call frame.repaint() and do your rendering in paintComponent() as “normal”
Swing apps do?

Thanks,
Dmitri

Thanks for the reply, I had given up on using swing but I might take a look at it again.

Yes I need a consistent framerate, and I wanted it to be possible to go fullscreen when available on the OS, so I cannot rely on the normal repaint methods and I all other paint events.

Well, then you can disable the repaint events (Frame.setIgnoreRepaint(true)), and do all
the rendering yourself. But prior to passing the graphics to paintComponent(),
Graphics2D.translate() it by Frame.getInsets().top/left .

Also, you can create a frame w/o decorations (Frame.setUndecorated(true)), then you won’t see
the decorations and you will not need to translate the graphics.

Thanks,
Dmitri
Java2D team