Sexy Swing App – The Bottom Bar
June 19, 2008
In the last installment of “Sexy Swing App”, I’ll show you how to create a bottom bar component as defined by Apple in their Human Interface Guidelines here.
There’s not much to this component, just a gradient and a compound line border that changes based on the focus state of the parent window. Below I’ve provided only the code related to the rendering of the bottom bar (i.e. not the code that lays out the bottom bar’s components). Here’s the custom JPanel that handles this rendering:
public class BottomBarPanel extends JPanel { private static final Color OS_X_BOTTOM_BAR_ACTIVE_TOP_COLOR = new Color(0xbbbbbb); private static final Color OS_X_BOTTOM_BAR_ACTIVE_BOTTOM_COLOR = new Color(0x969696); private static final Color OS_X_BOTTOM_BAR_INACTIVE_TOP_COLOR = new Color(0xe3e3e3); private static final Color OS_X_BOTTOM_BAR_INACTIVE_BOTTOM_COLOR = new Color(0xcfcfcf); private static final Color OS_X_BOTTOM_BAR_BORDER_HIGHLIGHT_COLOR = new Color(0xd8d8d8); private static final Color OS_X_UNIFIED_TOOLBAR_FOCUSED_BOTTOM_COLOR = new Color(64, 64, 64); private static final Color OS_X_UNIFIED_TOOLBAR_UNFOCUSED_BORDER_COLOR = new Color(135, 135, 135); public BottomBarPanel() { setBorder(BorderFactory.createMatteBorder(1,0,0,0, OS_X_UNIFIED_TOOLBAR_FOCUSED_BOTTOM_COLOR)); } @Override protected void paintComponent(Graphics g) { Graphics2D graphics = (Graphics2D) g.create(); Window window = SwingUtilities.getWindowAncestor(this); boolean hasFoucs = window != null && window.isFocused(); Color topColor = hasFoucs ? OS_X_BOTTOM_BAR_ACTIVE_TOP_COLOR : OS_X_BOTTOM_BAR_INACTIVE_TOP_COLOR; Color bottomColor = hasFoucs ? OS_X_BOTTOM_BAR_ACTIVE_BOTTOM_COLOR : OS_X_BOTTOM_BAR_INACTIVE_BOTTOM_COLOR; Paint paint = new GradientPaint(0, 0, topColor, 0, getHeight(), bottomColor); graphics.setPaint(paint); graphics.fillRect(0,0,getWidth(),getHeight()); graphics.dispose(); } @Override public Border getBorder() { Window window = SwingUtilities.getWindowAncestor(this); Border outterBorder = window != null && window.isFocused() ? BorderFactory.createMatteBorder(1,0,0,0, OS_X_UNIFIED_TOOLBAR_FOCUSED_BOTTOM_COLOR) : BorderFactory.createMatteBorder(1,0,0,0, OS_X_UNIFIED_TOOLBAR_UNFOCUSED_BORDER_COLOR); Border innerBorder = BorderFactory.createMatteBorder(1,0,0,0, OS_X_BOTTOM_BAR_BORDER_HIGHLIGHT_COLOR); return BorderFactory.createCompoundBorder(outterBorder, innerBorder); } } public enum Size { SMALL(22), LARGE(32); private int fHeight; Size(int height) { fHeight = height; } public int getHeight() { return fHeight; } }
Notice the top border is actually two single pixel lines – the bottom line acts as an inverse shadow, providing a slight etched look. As I’ve said before, it’s these subtle details that make or break the believability of your user interface.
Also notice that I’ve encapsulated the two size variants of a bottom bar in an enum, which isn’t yet used here. Ideally the bottom bar’s preferred height would be derived from the fHeight property of Size enum.
One parting note – if you prefer, as I do, to reduce the amount of extending your code does, I’d recommend using SwingX. Much of the extending I’ve done in this series can be reduced to Painters in SwingX.
June 20, 2008 at 2:12 am
Nice work! The one thing this doesn’t implement from the HIG is that the corners should be slightly rounded. This is nasty since it requires a transparent window, but you can probably fake the effect (since it’s such a tiny round anyway) by using a gradient color rather than a hard stroke, thus filling the space from the rounding to the actual corner and subtly tricking the eye. It won’t pass any sort of really close examination, but it might fool the eye in passing.
June 20, 2008 at 12:18 pm
Thanks Daniel! It kills me to see the square corners at the bottom of the “textured” window in Java.
I’ve thought about trying to implement the rounded corners using a shaped window, but thus far it’s fallen into the too much effort bucket. If I get sufficiently motivated to tackle this issue, I’ll be sure to post about it here.
I’ll also submit an enhancement request to Apple asking for a client property to draw the rounded corners in order to spare us from this hackery.
-Ken
June 20, 2008 at 1:20 pm
The roundness of the status bar is not the responsibility of the status bar itself, it is the responsibility of the windowing system, look&feel or similar.
June 20, 2008 at 1:55 pm
Right…but in the case of a work-around, I’d put the code wherever was easiest in hopes that a supported solution would be introduced.
June 20, 2008 at 3:06 pm
Just some other things:
The fields OS_X_BOTTOM_BAR_BORDER_HIGHLIGHT_COLOR and OS_X_UNIFIED_TOOLBAR_FOCUSED_BOTTOM_COLOR are duplicate. Works though when you comment them out.
And the constructor is named BottomBarGradientPanel, but the class itself BottomBarPanel.
Additionally, the class somehow may not be “static” (I don’t know what that would do and why it’s not allowed).
When I resolve all that, it gives a window a cool bottom panel. Looks great!
:-),
Felix
June 20, 2008 at 3:41 pm
Good catches Felix! Those were some copy and paste errors. I’ve updated the entry.
-Ken
March 24, 2009 at 6:50 pm
Is there any chance you will update the bar so it will be slightly rounded? Thanks for the awesome work, though!
March 24, 2009 at 7:10 pm
Hi Daniel,
I probably won’t be able make the bottom window edges rounded, as that would require installing a window mask, which I’d have to do via JNI. We could put in an enhancement request to Apple, though, requesting a client property to make the rounded corners.
-Ken