 |
Introducing Painters
Posted by joshy on September 20, 2006 at 05:36 PM | Comments (33)
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());
}
};
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:

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:
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
I have other examples of complex painters on a blog entry I wrote months ago: http://www.jroller.com/page/gfx/?anchor=swingx_painters_demo
These demos also show how to mix Painters and Effects (basically image filters.)
Posted by: gfx on September 20, 2006 at 06:45 PM
-
Seeing as how Painters have been described before (your blog, Romain's, Richards), it would be more useful if you could get the momentum started on a Painter tutorial or a wiki page.
Posted by: sumitkishore on September 20, 2006 at 08:22 PM
-
Also, a question - should painters properly be attached to a Swing(X) component or its UI delegate?
Posted by: sumitkishore on September 20, 2006 at 08:24 PM
-
I'm wondering about the relationship between painters and UI delegates, especially if the UI delegates are not Painter-aware. Aren't the UIDs solely responsible for paint operations? How do these interact? Thanks, Patrick
Posted by: pdoubleya on September 21, 2006 at 12:39 AM
-
Patrick, Sumit
How do these interact?
In the same way subclassing JComponent and overriding paintComponent interacts with UI delegates: not at all :-). This method completely blows away any painting from the UI delegates.
We've been thinking that these might be useful on the component level, like we have them now, but that they'd *really* be useful as extensions of Synth, or of UI delegates in general. However, this would require extensive changes to the current UI delegate code. For example, it would be nice if BasicButtonUI supported a "backgroundPainter" and "foregroundPainter". Right now all Button painting code is tied up in one method.
So, for the time being, attaching Painters to the component is the easiest way to get started. Also, if not using Synth, when you alter the painter for the UI delegate, you do so for the component globally rather than a specific instance (unless the ButtonUI was changed to have Painter API, then you could do button.getUI().setBackgroundPainter() on a specific instance).
Hope that helps.
Posted by: rbair on September 21, 2006 at 07:43 AM
-
Is there any way to take advantage of the apparent relationship between UIDelegates vs. Components and CSS class vs. element selectors? I haven't written L&Fs before, but it at least _seems_ more like voodoo than it ought to seem compared to how easily we do HTML and CSS.
Posted by: tompalmer on September 21, 2006 at 08:11 AM
-
Richard,
So, for the time being, attaching Painters to the component is the easiest way to get started.
So, are you saying that eventually, at some point down the road the UI delegate code will undergo these needed extensive changes to properly support painters? It would be really nice if they would.
Posted by: mikeazzi on September 21, 2006 at 09:01 AM
-
Richard,
That's the point I was trying to raise. I'm aware of being able to override painting behavior in a JComponent's paintComponent, but I (and others I guess) perceive that as a hack, a one-off thing. Tying Painters directly to Components kind of institutionalizes an antipattern.
Posted by: sumitkishore on September 21, 2006 at 09:09 AM
-
Well, it's not a hack if it's standard, right? :)
A painter is a painting delegate, just like the UI classes. But they are delegates on a per component basis instead of a per class basis. It is a different model of paint delegation. Neither of them is right or wrong. They both have their uses and should be used when appropriate.
Posted by: joshy on September 21, 2006 at 09:20 AM
-
So, Painters are intended as a way of extending/overriding the current LAF? i.e. are Painters supposed to be used directly by the application, as opposed to being used by a LAF? Could LAFs be implemented using Painters?
Posted by: afishionado on September 21, 2006 at 09:21 AM
-
mikeazzi: yes, we hope to change core Swing to work with Painters in the future. This is a very tricky thing, of course, and will require some deep changes, but it *is* something we are considering.
Posted by: joshy on September 21, 2006 at 09:21 AM
-
afishionado: Yes, they are intended to be used directly by your application. A L&F could be implemented using Painters, though that's really what Synth is for. I'll let Rich talk more about the possibly synergy there.
Posted by: joshy on September 21, 2006 at 10:27 AM
-
I hope when Painters are implemented that they do not just replace core painting but offer the user the ability to supplement painting. I was looking at a JTable problem not that long ago and there was a painting problem. It seemed that one answer was to use a Painter and do the specific painting after the JTable's children were painted.
leouser
Posted by: leouser on September 21, 2006 at 10:27 AM
-
Interleaving with platform look and feel painting is something we'd like to do, but it's tricky because each LnF paints in it's own way and the UI delegate APIs do not define a mechanism for separating the painting. For example, if I want to paint the text of a checkbox but do my own painting for the check part, there is no way to separate them. This is something we will have to tackle when it goes into core. In the mean time we are supporting only panels, labels, and buttons, which are easier to separate.
Posted by: joshy on September 21, 2006 at 10:37 AM
-
No no no no no no no! Please don't include painters in the core API. It is bloated enough without adding yet more means of doing the same thing that you can already accomplish!
There are, as I see it, 2 ways to look at painters and you have covered both of them: the first, as you mention in the comments, is as a kind of per-component UI delegate. Well, if this is what you are going to use them for why not just call JComponent#setUI(ComponentUI) on it with a custom UI delegate.
The other way to look at painters, as mentioned in the article, is as a way of bundling up some painting functionality. If I want to bundle up some painting functionality like this I seem to recall a keyword by the name of static that works wonders for this kind of thing when used with utility classes.
Please try to think very hard about what, if anything, painters make easier than the existing API.
Posted by: iphillips on September 21, 2006 at 11:52 AM
-
iphillips - If you have't already, you should actually try using both the Painter pipeline and custom UI delegates to see which mechanism is more concise and resuable...especially for complex effects.
Posted by: wsnyder6 on September 21, 2006 at 12:25 PM
-
iphillips,
With a painter you don't need to replace the delegate (which you are decorating) and you don't have to subclass. This is essential since with both your examples you really can't _add_ a decoration to component you don't know about at compile time.
Posted by: mgrev on September 21, 2006 at 12:36 PM
-
Hi wsnyder6!
If I were going to try to do complex effects on a small number of components I would probably use static utility methods rather than creating a custom UI delegate. I don't see that I gain much by using the painter mechanism here.
I'm not saying that there is nothing to be gained by the painter system, just that adding new - redundant - API, should be an abolute last resort, and I don't see any compelling evidence that it is the best or only option in this case.
Posted by: iphillips on September 21, 2006 at 12:38 PM
-
Hi mgrev,
Hmm, adding UI to a component that is not known at compile time is something that I hadn't thought of. You're right that this couldn't be done with utility methods as this requires subclassing to hook the custom code into the painting routine. An simple API addition could allow this to be done with UI delegates however:
public static void setComponentUI(JComponent component, ComponentUI ui) {
component.setUI(ui);
}
this could be added to SwingUtilities. Basically, it's just a way of making the UI setter public without changing the actual method on JComponent.
I think that needing to dynamically skin components at run time in this was should be rare enough that forcing people to use the more difficult approach (i.e. custom UI delegate) should be acceptable.
Posted by: iphillips on September 21, 2006 at 12:45 PM
-
BTW, as for trying out code like this myself in anything more that toy examples, I have a hobby project which looks like it may soon be safe to move from 1.4.2 to 1.5, so when it does I'm going to jump into SwingX with gusto! I'm really looking forward to being able to use a bunch of the components there.
Posted by: iphillips on September 21, 2006 at 12:49 PM
-
Awesome !!
Honestly, I've been waiting for these Painters for a long time. I've asked Romain Guy for a Java Animation API (just like Apple's Core Animation Framework), and this is perhaps one of the first answers! If the API is finalized and bundled into next JREs, it will certainly boost Swing development.
Regards
Posted by: tanhnhi on September 21, 2006 at 05:30 PM
-
One way painters could truly be useful is if they are used in a predictable way such that developers can augment either end of the chain. Consider UI delegates developed as a chain of painters, possibly split into a foreground and background chain. Add a painter to the head to draw under, add a painter to the end to draw over. You could potentially steal a UI delegate's painter chain to decorate a new component in a consistent fashion (think Apple's lozenges).
Currently the only real way to add a "paint listener" is to position another component on top of the original, but you can't necessarily get access to the underlying component's pixels. You have to hook up a bunch of stuff to make sure the overpainter stays consistent relative to its target. A true paint listener would make things easier, although I doubt that in itself is sufficient argument for new core API.
Posted by: twalljava on September 21, 2006 at 06:34 PM
-
iphillips,
The idea is you have pre-built painters. You just combine them to display cool graphics without doing much work. You can re-use them amongst components, on the contrary to UI delegates for instance. And while static utility methods are nice, they certainly don't match the power of a collection of well-defined painting classes. We did use the painters in several demos (Aerith and the mail client from Extreme GUI Makeover at JavaOne 2006) and it makes the code easier, simpler, cleaner.
Posted by: gfx on September 21, 2006 at 06:43 PM
-
tanhnhi: This API exists, it's the Timing Framework (I think I already gave you this answer.) It's a BSD library at timingframework.dev.java.net.
Posted by: gfx on September 21, 2006 at 06:44 PM
-
tanhnhi , Romain, I don't see the TimingFramework as an AnimationFramework. The latter should be something more that integrates the TimingFramework with Painters and Components. A good starting point would be extracting the animation utilities from Aerith and creating a basic framework that simplifies fades, dissolves, component movement, etc. (and making it something that is NOT an inherent part of a particular LAF)
Posted by: wsnyder6 on September 22, 2006 at 07:11 AM
-
wsnyder6 - the laf-widget that contains the animation layer used by Substance (mentioned in few of my latest posts) is not particular to Substance, but it is very LAF-oriented. So, any LAF that requires 1.4.2+ can use it at will (the license is BSD).
Posted by: kirillcool on September 22, 2006 at 08:38 AM
-
wsnyder6: That's on our todo list :) It's what we call canonical effects.
Posted by: gfx on September 22, 2006 at 10:52 AM
-
Romain: As you've suggested, I'm trying to integrate the timing framework to my project, but animation is still not as simple as it can be (in my opinion). People still have to deal with graphic2d codes, and with its performance problems too. I think we need a real Animation Framework, with pre-built effects that avoid people from writing drawing codes. And I'm sure that such a framework will attract lots, lots of begginner, intermediate java developpers to create swing apps. Animation is becoming the standard of modern applications nowaday. If we had had a good animation framework sooner, Flash would not have dethroned Applet easily!!
Posted by: tanhnhi on September 22, 2006 at 04:01 PM
-
....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....
Painter Editor will defenetly bring more Painters.,
I belive if Painter Editor is easy enough for a Artists to make Painters, many artists also can generate Painters.
What is after Paint Editor, LookAndFeel Editor ?
Posted by: kishoresjava on September 26, 2006 at 03:18 AM
-
So nice to see my work appreciated (or is that appropriated)?
http://minnow.cc.gatech.edu/squeak/3186
Posted by: tblanchard on October 19, 2006 at 08:39 PM
-
Interesting. Did you by chance work with Scott Hudson when he was at Tech? I did my senior research project under him in the winter and spring of 97. He created one of the coolest UI toolkits ever, SubArctic. My project was a series of extensions that did some painter like things. In fact, a good portion of my book, Swing Hacks, was inspired by things we worked on in SubArctic. I think the demos are still online. Very cool stuff. Did any of that work ever move to the Squeak side of Tech?
Posted by: joshy on October 19, 2006 at 09:56 PM
-
Did you ever released the code of the Painter Editor? Painters are great, but we need more examples, you have various examples in your blog, can you release the code for those? That would be a lot of help for many people.
Posted by: odoremieux on February 09, 2008 at 12:08 AM
-
That is a good point. I stopped working on Painters when I moved to the JavaFX team. If you are interested please join the swinglabs mailing list (jdnc) and ask for the code. If someone is interested in working on it then I'll hand it over. :)
Posted by: joshy on February 12, 2008 at 09:33 AM
|