Search code examples
objective-ccocoansstatusitem

NSStatusItem right click menu


I'm working on a status bar app that has a left and right click. I've got the start of this working by following the tips from other posts but I'm not sure how to go about showing a menu on right click.

I use a subclassed NSView as the custom view of my NSStatusItem and have the right and left clicks executing different functions:

- (void)mouseDown:(NSEvent *)theEvent{
    [super mouseDown:theEvent];
    if ([theEvent modifierFlags] & NSCommandKeyMask){
        [self.target performSelectorOnMainThread:self.rightAction withObject:nil waitUntilDone:NO];
    }else{
        [self.target performSelectorOnMainThread:self.action withObject:nil waitUntilDone:NO];
    }
}

- (void)rightMouseDown:(NSEvent *)theEvent{
    [super rightMouseDown:theEvent];
    [self.target performSelectorOnMainThread:self.rightAction withObject:nil waitUntilDone:NO];
}

How can I show a menu on right click, the same way the standard NSStatusItem does on left click?


Solution

  • NSStatusItem popUpStatusItemMenu: did the trick. I am calling it from my right click action and passing in the menu I want to show and it's showing it! This is not what I would have expected this function to do, but it's working.

    Here's the important parts of what my code looks like:

    - (void)showMenu{
        // check if we are showing the highlighted state of the custom status item view
        if(self.statusItemView.clicked){
            // show the right click menu
            [self.statusItem popUpStatusItemMenu:self.rightClickMenu];
        }
    }
    
    // menu delegate method to unhighlight the custom status bar item view
    - (void)menuDidClose:(NSMenu *)menu{ 
        [self.statusItemView setHighlightState:NO];
    }
    
    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
        // setup custom view that implements mouseDown: and rightMouseDown:
        self.statusItemView = [[ISStatusItemView alloc] init];
        self.statusItemView.image = [NSImage imageNamed:@"menu.png"];
        self.statusItemView.alternateImage = [NSImage imageNamed:@"menu_alt.png"];    
        self.statusItemView.target = self;
        self.statusItemView.action = @selector(mainAction);
        self.statusItemView.rightAction = @selector(showMenu);
    
        // set menu delegate
        [self.rightClickMenu setDelegate:self];
    
        // use the custom view in the status bar item
        self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
        [self.statusItem setView:self.statusItemView];
    }
    

    Here is the implementation for the custom view:

    @implementation ISStatusItemView
    
    @synthesize image = _image;
    @synthesize alternateImage = _alternateImage;
    @synthesize clicked = _clicked;
    @synthesize action = _action;
    @synthesize rightAction = _rightAction;
    @synthesize target = _target;
    
    - (void)setHighlightState:(BOOL)state{
        if(self.clicked != state){
            self.clicked = state;
            [self setNeedsDisplay:YES];
        }
    }
    
    - (void)drawImage:(NSImage *)aImage centeredInRect:(NSRect)aRect{
        NSRect imageRect = NSMakeRect((CGFloat)round(aRect.size.width*0.5f-aImage.size.width*0.5f),
                                      (CGFloat)round(aRect.size.height*0.5f-aImage.size.height*0.5f),
                                      aImage.size.width, 
                                      aImage.size.height);
        [aImage drawInRect:imageRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
    }
    
    - (void)drawRect:(NSRect)rect{
        if(self.clicked){
            [[NSColor selectedMenuItemColor] set];
            NSRectFill(rect);        
            if(self.alternateImage){
                [self drawImage:self.alternateImage centeredInRect:rect];
            }else if(self.image){
                [self drawImage:self.image centeredInRect:rect];
            }
        }else if(self.image){
            [self drawImage:self.image centeredInRect:rect];
        }
    }
    
    - (void)mouseDown:(NSEvent *)theEvent{
        [super mouseDown:theEvent];
        [self setHighlightState:!self.clicked];
        if ([theEvent modifierFlags] & NSCommandKeyMask){
            [self.target performSelectorOnMainThread:self.rightAction withObject:nil waitUntilDone:NO];
        }else{
            [self.target performSelectorOnMainThread:self.action withObject:nil waitUntilDone:NO];
        }
    }
    
    - (void)rightMouseDown:(NSEvent *)theEvent{
        [super rightMouseDown:theEvent];
        [self setHighlightState:!self.clicked];
        [self.target performSelectorOnMainThread:self.rightAction withObject:nil waitUntilDone:NO];
    }
    
    - (void)dealloc{
        self.target = nil;
        self.action = nil;
        self.rightAction = nil;
        [super dealloc];
    }
    
    @end