Skip to main content

Swing Hack 3: Overlay Graphics

Posted by joshy on September 26, 2003 at 1:49 PM PDT

It's Friday so I thought I'd do another Swing hack:

When I'm doing really complicated Swing layouts I often have trouble figuring
out which component on screen matches the one in my code, especially if there are custom widgets or subclasses that look the same as normal ones (like formatted text fields) or that don't have easy to see borders. To assist in debugging these I created a component which draws a border around each component and prints the class on top of it.


This is really easy to do in Swing because you can create what's known as a glasspane. It's an invisible Swing component which sits on top of the rest of a frame's contents, intercepting events. It's typically used to block input for modal dialogs or progress bars, but you can use it to paint on top of the frame as well. Combined with alpha channel colors we can do nifty tricks.

public class DrawOverTest {

public static void main(String[] args) {
    JFrame frame = new JFrame("glass pane test");
    JPanel panel = new JPanel();
    panel.setLayout(new GridLayout(4,1));
    panel.add(new JButton("my button"));
    panel.add(new JLabel("my button"));
    panel.add(new JButton("my button"));
    frame.getContentPane().add(panel);
    frame.pack();
    frame.setVisible(true);


    LabelGlassPane glass = new LabelGlassPane(frame);
    frame.setGlassPane(glass);
    glass.setVisible(true);
}


}

class LabelGlassPane extends JComponent {
    public LabelGlassPane(JFrame frame) {
      this.frame = frame;
     //this.addMouseListener(new MouseAdapter() {         });
    }
    public JFrame frame;
    public void paint(Graphics g) {
        g.setColor(Color.red);
        Container root = frame.getContentPane();
        g.setColor(new Color(100,100,100,100));
        rPaint(root,g);
    }
    private void rPaint(Container cont, Graphics g) {
        for(int i=0; i<cont.getComponentCount(); i++) {
            Component comp = cont.getComponent(i);
            if(!(comp instanceof JPanel)) {
                int x = comp.getX();
                int y = comp.getY();
                int w = comp.getWidth();
                int h = comp.getHeight();
                g.drawRect(x+4,y+4,w-8,h-8);
                g.drawString(comp.getClass().getName(),x+10,y+20);
            }
            if(comp instanceof Container) {
                rPaint((Container)comp,g);
            }
        }
    }
}

As with my other hacks, the program doesn't know about the hack, meaning there are no subclasses or custom code. All you need is a reference to the frame and the glasspane takes care of the rest.

This glass pane doesn't block the IO even though the javadocs imply it should. It seems that if you don't set any event handlers then the events will pass through. If you add one, even if it's a no-op like the commented line above, then the events are blocked and you have to forward them manually. This seems kind of odd, if convenient. Adding an event handler should add behavior but not affect existing behavior.

Anyway, have a good weekend.

Related Topics >>

Comments

Hey, nice idea, however I have a complex UI and when I tried ...

Hey, nice idea, however I have a complex UI and when I tried it gave me a wrong result, here's how I fixed it (note the offsets):

    public void paint(Graphics g) {
        g.setColor(Color.red);
        Container root = frame.getContentPane();
        g.setColor(new Color(100,100,100,100));
        rPaint(root,g,0,0);
    }
    private void rPaint(Container cont, Graphics g, int offX, int offY) {
        for(int i=0; i<cont.getComponentCount(); i++) {
            Component comp = cont.getComponent(i);
            int x = offX + comp.getX();
            int y = offY + comp.getY();
            if(!(comp instanceof JPanel)) {
                int w = comp.getWidth();
                int h = comp.getHeight();
                g.drawRect(x+4,y+4,w-8,h-8);
                String name = comp.getClass().getName().replaceAll(".*?\\.", "");
                g.drawString(name,x+4+6,y+g.getFontMetrics().getHeight());
            }
            if(comp instanceof Container) {
                rPaint((Container)comp,g,x,y);
            }
        }
    }