Note: Internet Explorer doesn’t seem to render this post very well. I suggest using Firefox (or Safari) to read it.
I’ve been reading Romain’s blog with interest for the past few months. I’ve built my share of Swing (and SWT) based applications, and I’ve developed my share of custom controls for Swing over the past 6-7 years, but I’ve never built anything as “pretty” as what he’s been making.
Last night, I thought I would see how long it would take to create something “pretty”. I decided on trying to see what it would take to create (just draw for now) “mac-like” Aqua buttons. Here is the result of my 30 minute experiment:

I’m sure they could probably done a little bit better, or a little more authentic, but I don’t think it’s bad for a 30 minute exercise.
So, how can you create something like this? Here’s how:
Step 1: Create a buffered image to use as our canvas to draw on
Later in the process, we will want to create apply effects (blurring) to what we have created. Instead of drawing directly with the Graphics2D instance that we are handed by Swing, we will create a buffered image that we can use to “draw on”:
BufferedImage vBuffer = new BufferedImage(vWidth, vHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D bg = vBuffer.createGraphics();
bg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Step 2: Paint the background color
For right now, we’ll just paint the background white:
bg.setColor(Color.WHITE);
bg.fillRect(0, 0, vWidth, vHeight);
Step 3: Draw a rounded rectangle with a gradient fill
We want to draw a rounded rectangle with a gradient that gets lighter from top to bottom. So the first thing we need to do is figure out the start color and the end color of the gradient and use that to create the gradient paint:
Color vGradientStartColor = buttonColor.darker().darker().darker();
Color vGradientEndColor = buttonColor.brighter().brighter().brighter();
Paint vPaint = new GradientPaint(0, inset, vGradientStartColor, 0, vButtonHeight, vGradientEndColor, false);
bg.setPaint(vPaint);
Now that we have our “paint” created, we need to draw the rounded rectangle:
bg.fillRoundRect(inset, inset, vButtonWidth, vButtonHeight, vArcSize, vArcSize);
If we would run our application now, it would give us this:
Step 4: Paint the highlight (lighting effect)
Next, we need to paint a highlight on top of the rounded rectangle to make it look like a lighting effect. To do this, we are going to paint another rounded rectangle (inset 1 pixel) that fades from white to a lighter shade of the base color.
Like the last rounded rectange we painted, we first need to create the gradient paint:
// Create the paint for the second layer of the button
vGradientStartColor = Color.WHITE;
vGradientEndColor = buttonColor.brighter();
vPaint = new GradientPaint(0,inset+vHighlightInset,vGradientStartColor,0,inset+vHighlightInset+(vButtonHighlightHeight/2), buttonColor.brighter(), false);
bg.setPaint(vPaint);
Next, we need to paint the rounded rectangle:
bg.fillRoundRect(inset+vHighlightInset,inset+vHighlightInset,vButtonHighlightWidth,vButtonHighlightHeight,vHighlightArcSize,vHighlightArcSize);
If we would run our application now, it would give us this:
Step 5: Add clipping and transparency
The last step brings us pretty close to what we want, however we can make it a little better by clipping the second rounded rectangle we painted, as well as adding a little transparency to it. In order to do this, we’ll add a few more calls before we paint the rounded rectangle:
bg.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,.8f));
bg.setPaint(vPaint);
bg.setClip(new RoundRectangle2D.Float(inset+vHighlightInset,inset+vHighlightInset,vButtonHighlightWidth,vButtonHighlightHeight / 2,vButtonHighlightHeight / 3,vButtonHighlightHeight /3));
bg.fillRoundRect(inset+vHighlightInset,inset+vHighlightInset,vButtonHighlightWidth,vButtonHighlightHeight,vHighlightArcSize,vHighlightArcSize);
If we would run our application now, it would give us this:
Step 6: Blur the image
In order to make everything “softer”, we will apply a blur to the image to give it that last finishing touch:
float[] BLUR = {0.10f, 0.10f, 0.10f, 0.10f, 0.30f, 0.10f, 0.10f, 0.10f, 0.10f};
ConvolveOp vBlurOp = new ConvolveOp(new Kernel(3, 3, BLUR));
BufferedImage vBlurredBase = vBlurOp.filter(vBuffer, null);
If we would run our application now, it would give us this:
Step 7: Adding some text
Any “real” button includes text (or an image), so the next logical step would be to add some text to our Aqua buttons:
g2.setColor(foregroundColor);
Font vFont = g2.getFont().deriveFont((float)(((float)vButtonHeight) * .6));
g2.setFont(vFont);
FontMetrics vMetrics = g2.getFontMetrics();
Rectangle2D vStringBounds = vMetrics.getStringBounds(text,g2);
float x = (float)((vWidth / 2) - (vStringBounds.getWidth() / 2));
float y = (float)((vHeight / 2) + (vStringBounds.getHeight() / 2)) - vMetrics.getDescent();
g2.drawString(text,x,y);
If we would run our application now, it would give us this:
Conclusion
I think it’s a pretty good demonstration of the power of Java2D that I can create something like that in less than 30 minutes. I guess that logical next step would be to use technicque this to create a real implimentation of a button (or UI delegate for JButton) so people can add Aqua like buttons to any swing application.
If you want to play around with this yourself, you can download the source code here.
Have fun!
May 27th, 2005 at 2:34 pm
You are welcome to take a look at the substance look and feel currently being developed on java.net. The code is a little bit more messy than above, but is much more general, being used for all types of buttons (regular, toggle, combobox, check boxes, radio buttons, and so on).
May 27th, 2005 at 7:08 pm
Note: Internet Explorer doesn’t seem to render this post very well. I suggest using Firefox (or Safari) to read it.
Why not fix it?
May 27th, 2005 at 7:10 pm
Nice trick! There is a similar hack in the book Swing Hacks
Some OSX buttons use the “bezel” style. They show a bézier curve in the background as in this picture: http://www.progx.org/users/Gfx/newsfire1.png
I did a quick ripoff of them in Swing: http://www.progx.org/users/Gfx/netvideo2.png
I, for once, did implement this as UI if you are interested (http://www.jroller.com/page/gfx/20050328#packed_with_effects, if the source code is not linked from there, just ask).
I’m looking forward to see other cool stuff like this from you
May 27th, 2005 at 8:46 pm
I was thinking about implementing those buttons with the “bezel” style as well. I may have to take your code and enhance it a little bit to allow groups of buttons (similar to the backwards and forwards links in Safari).
I’ve also got a few other custom swing controls and examples I’ve done over the years that I want to release (as well as document for others how to create similar things).
May 30th, 2005 at 8:14 am
[...] « Solo is Simple, part two. Aqua button. How to create an Aqua buttons with Java2D. This entry was posted on Monday, May 30 [...]
May 31st, 2005 at 8:03 pm
Note: Internet Explorer doesn’t seem to render this post very well. I suggest using Firefox (or Safari) to read it.
Jose Sandoval Says: Why not fix it?
Let me remind you it isn’t Jon’s responsibility to fix IE. Page display’s fine in Safari btw.
Drawing buttons like this is very neat, and I like the idea of being able to change the colors programatically.
July 13th, 2005 at 1:28 pm
Hi,
Great Stuff …. and Don’t Stop…
by the way, i saw that you mentionned that you have been using Swing and SWT for 6-7 years…. Do you have some examples and tutorials for SWT….? That would be Great since i like your style….
July 13th, 2005 at 6:17 pm
Thanks!
Yes, I have some custom controls that I have developed for SWT that I could use as a basis for a tutorial.
It’s just a matter of how many hours I have free in a day… I’ve got to pay the bills with my day job.