Introducing Painters

One of the temptations of design is to not show your work until it's ready. Not until every edge is smoothed and every bolt is tightened should anyone be allowed to see it. While this might be okay for paintings or sculpture, in the world of software it often leads to bad APIs. An API is the user interface for other programmers. I'm a firm believer that user interfaces must be tested with real users, and as early as possible.

I had planned to wait until the Painters were more polished and my tools had their bigger bugs squashed. Sadly, finishing up Java 6, a Java 5 update, business trips, and a recent illness have kept me from working on it. Recent friendly pokes reminded me of this. (thanks fred) So today, rather than keep waiting until it's perfect I'd rather share. After all, desktop Java is about community; a community of developers who care about the desktop user experience. So with this in mind it's time to pull back the curtain, pop open the hood, and turn on some bright lights to take a first look at Painters.

If you've been following the work we've been doing in SwingLabs, the Aerith demo in particular, you may have wondered how we do all of these cool tricks. We've documented the mapping and 3D parts elsewhere, but we've never discussed the custom components. If you've seen Romain's recent presentation or read his blog you may know how we do it: Painters. Painters are the key to most of our recent graphical effects. The translucent panels in Aerith were normal panels with painters. The fade in effects where just animated painters. We hope that painters will enable developers to build better looking applications easily and quickly. Not only that, we hope to one day integrate painters into the core API. So with that in mind: just what are painters?!

What are Painters?

This is a painter

This is also a painter.

More correctly, these are components (JXPanels, to be precise) with painters attached to them. Painters are just encapsulated Java2D drawing code. Think of them like event listeners. If a listener is a component's delegate for handling events, then:

a Painter is a component's delegate for drawing

There are three ways to use painters.

  • Implement the Painter interface
  • Reuse one or more of the standard painters in SwingX or 3rd party painters
  • Build a painter set graphically

Implement the Painter interface

At it's simplest, a painter is a class which implements the Painter interface. It can then be set on any painter aware component with the setBackgroundPainter() method.

Let's start with a simple example. If you wanted to create a panel which draws blue circle in a light blue background you could do it by overriding the paintComponent method of the panel.

        JXPanel panel = new JXPanel() {
            protected void paintComponent(Graphics g) {
                g.setColor(Color.BLUE.darker());
                g.fillRect(0,0, getWidth(), getHeight());
                g.setColor(Color.BLUE);
                g.fillOval(0,0,getWidth(),getHeight());
            }
        };

painter_intro_1.png

With painters you could do the same thing by implementing the Painter interface and attaching it to the panel, like this:

        Painter bluePainter = new Painter() {
            public void paint(Graphics2D g, JComponent component, int width, int height) {
                g.setColor(Color.BLUE.darker());
                g.fillRect(0,0, width, height);
                g.setColor(Color.BLUE);
                g.fillOval(0,0, width, height);
            }
        };
        
        JXPanel panel2 = new JXPanel();
        panel2.setBackgroundPainter(bluePainter);

Why use Painters?

Since these use roughly the same amount of code, you might ask why you should use painters? Well, for the same reason you would use an event listener. You can separate the drawing code from the component. This will make your application source code cleaner. It also means you can reuse the drawing code, even across multiple components. Since painters are designed to be stateless you can attach them to several components at once. In Aerith, for example, we use the same translucent round rect painter for the tool panel and the popup waypoint editor. We also use one painter for both the Save and Preview buttons.

Aerith Waypoint Editor

There is an even better reason to use painters besides separation of concerns: You can use other people's painters! Why write your own effects when someone else can do it for you! SwingX contains built in painters for gradients, pinstripes, images, shapes, and text.

For example, if I wanted to create a blue round rectangle on a blue pinstripe background, I could use code like this:

       
// create a light blue background
Painter matte = new MattePainter(new Color(128,128,255));
        
// pinstripe with lighter blue at 45.0 angle, 8px stripes
PinstripePainter pinstripe = new PinstripePainter(
        new Color(155,155,255),  45.0);
pinstripe.setSpacing(8);
pinstripe.setStripeWidth(8);

// a rectangle with 20 pixel insets, 30 pixel rounded corners        
RectanglePainter roundRect = new RectanglePainter(
        20,20,20,20,   30,30,   true, // rounded = true
		 // gray bg with 3px dark grayborder
        Color.GRAY, 3, Color.DARK_GRAY); 
// turn on antialiasing
roundRect.setAntialiasing(AbstractPainter.Antialiasing.On);
        
// combine the three painters into one
CompoundPainter compound = new CompoundPainter(
        matte,pinstripe, roundRect);
        
// set on the panel
JXPanel panel3 = new JXPanel();
panel3.setBackgroundPainter(compound);

Each painter above is simple declarative code. The important thing to notice here is the CompoundPainter. It lets you combine multiple painters into a single object that you can set on the panel. This is the end result:

painter_intro_2.png constructed from standard painters

In the future I'd love to see us create a web-based gallery of painters where developers can share code and screenshots of their favorite painters.

What I'm presenting today is just the beginnings of an API and toolset to help you build painters. If you want to play around with it you need to check out the painter_work CVS branch from swingx.dev.java.net. I'm posting this today to open the discussion and get feedback. Please join the SwingLabs forum and dive in. We need your help.

That's it for today. Next week I'll talk about filters, shape effects, and how to use the painter builder to avoid writing any code at all. Until then, here's a teaser screenshot:

painter_intro_3.png

Talk to me about it on Twitter

Posted September 20th, 2006

Tagged: java.net