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 stackoverflow.com

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;
@end

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

// 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
                        location:wp
                        modifierFlags:NSApplicationDefined
                        timestamp: (NSTimeInterval) 0
                        windowNumber: windowNumber
                        context: [NSGraphicsContext currentContext]
                        subtype:0
                        data1: 0
            &#016
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