Monthly Archives: January 2011

Adding a badge to the unified toolbar on Mac OS X

Mac OS X and the applications running on it are known for being sometimes unusual in the look and feel. For some reason developer on Mac OS X seems to be more creative than on other platforms. For an application developer which deals with user input it is important to make any user interaction as useful as possible and present this information in a way which is on the one side as much less annoying as possible but also attractive and informative on the other side. Lastly I was thinking about how to show the users they are using a beta version, which should remind them that this is not production ready software, but on the same time make it easy to respond to bugs they find in this pre-release. The solution, I came up with, was a badge in the toolbar which shows this is a release which is definitely in a testing phase, but also allows the user to double-click to give a respond to this particular version. In the following post I will show how to do this.

Getting the superview

In Cocoa all user displayed content is a NSView (most of the time; forget about the Dock). This is really nice, cause you could manipulate them. Although there are no public methods for getting the NSView of the toolbar or even the titlebar, they exist as an NSView. The NSView of the toolbar could be accessed by a private method. As always in Objective C you could ask for a particular method by using the respondsToSelector statement like in the following:

NSToolbar *tb = [pWindow toolbar];
if ([tb respondsToSelector:@selector(_toolbarView)])
{
 NSView *tbv = [tb performSelector:@selector(_toolbarView)];
 if (tbv)
 {
  /* do something with tbv */
 }
}

This return the NSView of the toolbar at least until 10.6. But be warned this is a private method and of course this could be changed in a future version of Mac OS X. At least the shown method will correctly fail in the case Apple change his mind which will result in doing nothing. Anyway this will not include the area of the titlebar. To get the NSView which covers both the title bar and the toolbar you need another trick. The titlebar usual has a close, minimize or maximize button. These buttons are NSButton’s and added by the system depending of the window type. As a NSButton is also a NSView it also has a parent. This dependency allows us to access the view which is responsible for displaying the unified titlebar and the toolbar. The following code shows how to get the responsible view and how to add an additional NSImageView to it:

NSView *wv = [[pWindow standardWindowButton:NSWindowCloseButton] superview];
if (wv)
{
 /* We have to calculate the size of the title bar for the center case. */
 NSSize s = [pImage size];
 NSSize s1 = [wv frame].size;
 NSSize s2 = [[pWindow contentView] frame].size;
 /* Correctly position the label. */
 NSImageView *iv = [[NSImageView alloc] initWithFrame:
        NSMakeRect(s1.width - s.width - (fCenter ? 10 : 0),
                   fCenter ? s2.height + (s1.height - s2.height - s.height) / 2 : s1.height - s.height - 1,
  	           s.width, s.height)];
 /* Configure the NSImageView for auto moving. */
 [iv setImage:pImage];
 [iv setAutoresizesSubviews:true];
 [iv setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
 /* Add it to the parent of the close button. */
 [wv addSubview:iv positioned:NSWindowBelow relativeTo:nil];
}

This code needs a NSWindow, a NSImage as label and the flag fCenter for deciding if the image is vertically centered within the titlebar. Also the badge is pinned on the right side by setting the AutoresizingMask. The following shows how this could be look like:

Adding functionality to the badge

First we should add some information about this release by using the setToolTip method of an NSView. Simply add this call to the code:

 [iv setToolTip:@"Some info about the beta version."];

To make this beta hint more useful, we should make it clickable. Getting mouse down events in Cocoa is only possible by handling the mouseDown event of the NSResponder sub-class. Also the view must accept first responder events. How to do this is shown in the following code:

@interface BetaImageView: NSImageView
{}
- (BOOL)acceptsFirstResponder;
- (void)mouseDown:(NSEvent *)pEvent;
@end
@implementation BetaImageView
- (BOOL)acceptsFirstResponder
{
 return YES;
}
- (void)mouseDown:(NSEvent *)pEvent
{
 if ([pEvent clickCount] > 1)
  [[NSWorkspace sharedWorkspace] openURL:
        [NSURL URLWithString:@"http://www.virtualbox.org/"]];
 else
  [super mouseDown:pEvent];
}
@end

Replace NSImageView with BetaViewImage in the allocation call in listing 2 and you are done. Change the URL to one where the user can respond to the beta questions, like a bug tracker or forum.

Conclusion

This post showed how to add an nice looking badge to the unified toolbar on Mac OS X. The user is reminded he is using some pre-release software all the time, but at the same time has an easy way to report problems with this release. Of course could such a badge used for anything else.