Implementing right-click context menu in Cocoa as a function call

Implementing right-click context menu in Cocoa as a function call

I was porting some Windows code to Cocoa that used a synchronous call to TrackMenuPopup (TPM_RETURNCMD | TPM_NONOTIFY) to display a context menu and immediately return the menu item selected.  I didn't want the menu to post a notification because that would require a more complicated architecture that could port between Mac and Windows.  I posted the question titled "Is there an equivalent technique in Cocoa for the synchronous TrackPopupMenu in Windows?" to

I couldn't decouple the NSMenu notification to an NSView so I came up with a solution using a dummy NSView.  This way I could implement a popup menu as a function call that returns the selected value.  Apparently popUpContextMenu is synchronous:

I ultimately came up with the following solution:

// Dummy View class used to receive Menu Events

@interface DVFBaseView : NSView
    NSMenuItem* nsMenuItem;
– (void) OnMenuSelection:(id)sender;
– (NSMenuItem*)MenuItem;

@implementation DVFBaseView
– (NSMenuItem*)MenuItem
    return nsMenuItem;
– (void)OnMenuSelection:(id)sender
    nsMenuItem = sender;

// Calling Code:

void HandleRButtonDown (NSPoint pt)
    NSRect    graphicsRect;  // contains an origin, width, height
    graphicsRect = NSMakeRect(200, 200, 50, 100);
    // Create Menu and Dummy View
    nsMenu = [[[NSMenu alloc] initWithTitle:@"Contextual Menu"] autorelease];
    nsView = [[[DVFBaseView alloc] initWithFrame:graphicsRect] autorelease];

    NSMenuItem* item = [nsMenu addItemWithTitle:@"Menu Item# 1" action:@selector(OnMenuSelection:) keyEquivalent:@""];
    [item setTag:ID_FIRST];

    item = [nsMenu addItemWithTitle:@"Menu Item #2" action:@selector(OnMenuSelection:) keyEquivalent:@""];
    [item setTag:ID_SECOND];

    // Providing a valid windowNumber is key in getting the Menu to display in the proper location
    int windowNumber = [(NSWindow*)myWindow windowNumber];
    NSRect frame = [(NSWindow*)myWindow frame];
    NSPoint wp = {pt.x, frame.size.height – pt.y};  // Origin in lower left
    NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined
                        timestamp: (NSTimeInterval) 0
                        windowNumber: windowNumber
                        context: [NSGraphicsContext currentContext]
                        data1: 0
0;           data2: 0];   

    [NSMenu popUpContextMenu:nsMenu withEvent:event forView:nsView];
    NSMenuItem* MenuItem = [nsView MenuItem];
    switch ([MenuItem tag])
    case ID_FIRST: HandleFirstCommand(); break;
    case ID_SECOND: HandleSecondCommand(); break;

Leave a Reply