Sunday, October 26, 2008

Cocoa: System StatusBar Item (Aka TrayBar)

Today's example is "How to add your own Item inside System Status Bar". It's really easy but the Apple OS X Human Interface Guidelines discourage it, at the end of the post you can find the Apple HIG notes.


Something about Names... Apple HIG call this MenuBar part "MenuBar Extra" (See Apple HIG Part III - The Menu Bar and Its Menus) using Cocoa you can get it from NSStatusBar systemStatusBar.


Now the Example. As you can see in the picture we've our icon "M" with a Menu that do something...

When applicationDidFinishLaunching we initialize the NSStatusItem object we build the menu items and conntect with the related selector. In this example you can see the usage of NSWorkspace to run Application like finder and URL that will be opened with your default related app.
@interface TrayMenu : NSObject {
  @private
    NSStatusItem *_statusItem;
}
@end

@implementation TrayMenu

- (void) openWebsite:(id)sender {
  NSURL *url = [NSURL URLWithString:@"http://th30z.netsons.org"];
  [[NSWorkspace sharedWorkspace] openURL:url];
  [url release];
}

- (void) openFinder:(id)sender {
  [[NSWorkspace sharedWorkspace] launchApplication:@"Finder"];
}

- (void) actionQuit:(id)sender {
  [NSApp terminate:sender];
}

- (NSMenu *) createMenu {
  NSZone *menuZone = [NSMenu menuZone];
  NSMenu *menu = [[NSMenu allocWithZone:menuZone] init];
  NSMenuItem *menuItem;

  // Add To Items
  menuItem = [menu addItemWithTitle:@"Open Website"
                        action:@selector(openWebsite:)
                          keyEquivalent:@""];
 [menuItem setTarget:self];

  menuItem = [menu addItemWithTitle:@"Open Finder"
                      action:@selector(openFinder:)
                         keyEquivalent:@""];
 [menuItem setTarget:self];

 // Add Separator
 [menu addItem:[NSMenuItem separatorItem]];

  // Add Quit Action
  menuItem = [menu addItemWithTitle:@"Quit"
                     action:@selector(actionQuit:)
                      keyEquivalent:@""];
  [menuItem setToolTip:@"Click to Quit this App"];
  [menuItem setTarget:self];

  return menu;
}

- (void) applicationDidFinishLaunching:(NSNotification *)notification {
  NSMenu *menu = [self createMenu];

  _statusItem = [[[NSStatusBar systemStatusBar]
  statusItemWithLength:NSSquareStatusItemLength] retain];
  [_statusItem setMenu:menu];
  [_statusItem setHighlightMode:YES];
  [_statusItem setToolTip:@"Test Tray"];
  [_statusItem setImage:[NSImage imageNamed:@"trayIcon"]];

  [menu release];
}

@end

int main(int argc, char *argv[]) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  [NSApplication sharedApplication];

  TrayMenu *menu = [[TrayMenu alloc] init];
  [NSApp setDelegate:menu];
  [NSApp run];

  [pool release];
  return EXIT_SUCCESS;
}

Apple OS X Human Interface Guidelines

Reserved for use by Apple, the right side of the menu bar may contain items that provide feedback on and access to certain hardware or network settings. Menu bar extras display some type of status in the menu bar and have a menu to change settings. The icon for the battery strength indicator, for example, dynamically displays the current state of the battery for a portable computer, and the menu has common battery settings. Users can display or hide a menu bar extra in the appropriate preferences pane.

Important: Don’t create your own menu bar extras. Use the Dock menu functions to open a menu from your application’s icon in the Dock.

If there is not enough room in the menu bar to display all menus, menu bar extras are removed automatically by Mac OS X to make room for application menus, which take precedence. Because of this, and because users can choose to hide menu bar extras, you should not rely on their presence.

11 comments:

  1. Great post -- exactly what I was looking for. Thanks!

    I wanted the App to not be visible in the dock or when using the task-switcher (alt-tab). To achieve this, simply enable "LSBackgroundOnly" in your app's Info.plist.

    see:
    Apple Docs

    ReplyDelete
  2. [...] plan was to make the app accessible from the global menu bar, but then I learned that Apple’s Human Interface Guidelines discourage it, and you know how I love guidelines. And humans. So instead I’m going to use a Dock menu. The [...]

    ReplyDelete
  3. This is my first time comment at your blog.
    Good recommended website.

    ReplyDelete
  4. Hi,

    Could you post source code for this project please?

    Thank you

    ReplyDelete
  5. [...] want to thank Matteo Bertozzi for teaching me how to create the system bar items, and P.J. Onori for the icon from the Iconic set which I used. I’m using this player daily, [...]

    ReplyDelete
  6. This is my first time comment at your blog.
    Good recommended website.

    ReplyDelete
  7. This is a nice post, I like it, thank you.

    ReplyDelete
  8. Very nice Post!
    bravo davvero ;) sei così giovane poi!

    ReplyDelete
  9. Great article. It was just what I was looking for. I am trying to add actions to when a menu item is selected, but I can't get it to work right. How would you do that?

    Thank!

    ReplyDelete
  10. Really great post. Exactly what I was looking for. It works great.

    ReplyDelete
  11. To any readers: the quote from Apple HIG (which is a quote but does not have quote marks?) is outdated. See the latest HIG, which to paraphrase, says an app may use the system tray ('menu extras' is Apple terminology) but it should be a user choice in the app's preferences. And so forth.

    ReplyDelete