Showing a menu in Cocoa is supposedly easy. Go ahead, consult the docs. Why, as early as on NSMenu will you find a promising option called + popUpContextMenu:withEvent:forView:! Surely this is a prime candidate. And you may be right. In many cases, if you were to do it on a fresh install, this would be indistinguishable from showing a menu. But there’s more to it than that.
The method’s very selector exposes two significant facts: that it’ll pop up a context menu and that it needs an event. So what’s so special about a context menu, then? Apple’s Displaying a Contextual Menu chapter details: “Contextual menus, including any menu you pop up with popUpContextMenu:withEvent:forView:, automatically insert menu items from any contextual menu plug-ins that the user has installed into the menu. A contextual menu plug-in, which is CFPlugIn bundle installed in a Library/Contextual Menu Items directory at the appropriate level of the system, enables applications and other forms of software to extend the list of commands found on contextual menus such as the Finder’s.”
So, in other words, whenever I get a rashly coded menu shown to me, it also gets the invaluable, or not, “Add to Google Calendar” menu item affixed to it, on account of me having the Google Notifier installed.
Ouch.
The second part is not that bad - you’ll need an event to legitimately pop up a (context!) menu. Still, you don’t always have events, and they are hard to accurately synthesize. I mean, look at the selector for a mouse event convenience class method! Event number, timestamp - where are you supposed to get that?
Well then, what sort of alternative does Apple give you? Let’s go back to that article again: ah! “The preferred approach for programmatically displaying a non-contextual menu is to create an NSPopUpButtonCell object, set its menu, and then call send a attachPopUpWithFrame:inView: message to the pop-up button cell.”
Um. Er.
It’s not actually as hard as it sounds. I’ve taken the liberty to write it up as a short category on NSMenu to provide a similar class method. It takes three arguments - the menu, the view (for which it Does The Right Thing, in almost all instances, and zeroes in on the (0,0) corner) and a BOOL equal to the usage of the NSPopUpButtonCell initializer, declaring if you want a pull-down menu (pulls down from below the view, steals the first menu item as its label, sorta, and doesn’t maintain selection state) or a popup menu (pulls down with the selected or top menu item ‘on’ the view). It’s useful, but trivial and short enough to not be of tremendous value to me - therefore I’m dropping any possible copyright claims on this and releasing it under public domain. And since it’s so short, it’s inline here below:
@interface NSMenu (PopUpRegularMenuAdditions)
+ (void)popUpMenu:(NSMenu *)menu forView:(NSView *)view pullsDown:(BOOL)pullsDown;
@end
@implementation NSMenu (PopUpRegularMenuAdditions)
+ (void)popUpMenu:(NSMenu *)menu forView:(NSView *)view pullsDown:(BOOL)pullsDown {
NSRect frame = [view frame];
frame.origin.x = 0.0;
frame.origin.y = 0.0;
if (pullsDown) [menu insertItemWithTitle:@"" action:NULL keyEquivalent:@"" atIndex:0];
NSPopUpButtonCell *popUpButtonCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:pullsDown];
[popUpButtonCell setMenu:menu];
if (!pullsDown) [popUpButtonCell selectItem:nil];
[popUpButtonCell performClickWithFrame:frame inView:view];
}
@end
This method drops the automatic tick mark selection on popup menus; edit it to perfection according to your own wishes. It also actually uses performClickWithFrame:inView: rather than attachPopUpWithFrame:inView:, since I had trouble making attach... work when inside some event methods.
Share and enjoy!
[…] waffle → Popping Up a Menu in Cocoa (tags: cocoa mac osx programming) […]
By raoli.com » Blog Archive » links for 2008-02-05 · 2008.02.05 14:19