1

I have a NSStatusItem with a menu attached. How can I get mouse/touch events from the status item without losing the menu? I was thinking perhaps some kind of workaround where I take in the events and manually pop the menu up, but I am unsure of the feasibility.

The following example demonstrates the problem. The only major difference from this example and my actual code is I am using a menu delegate.

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate> {
    IBOutlet NSWindow *window;
    NSStatusItem* statusItem;
    NSMenu* statusMenu;
    NSMenuItem* menuItem;
}
-(IBAction)stuffHappened:(id)sender;
@end

@implementation AppDelegate

-(void)awakeFromNib{
    statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
    
    statusMenu = [[NSMenu alloc] initWithTitle:@""];
    menuItem = [[NSMenuItem alloc] initWithTitle:@"test"
                                   action:nil
                                   keyEquivalent:@""];
    statusItem.button.title = @"\U0001F410";
    
    [statusItem setMenu:statusMenu]; //commenting out this line allows the action to fire
    [statusMenu addItem:menuItem];
    
    [[statusItem button] setTarget:self];
    statusItem.button.action = @selector(stuffHappened:);
}

-(IBAction)stuffHappened:(id)sender {
    NSLog(@"Stuff happened");
}
@end
Kyle Berezin
  • 597
  • 4
  • 20
  • 1
    What are you trying to accomplish? Left-right click stuff? – red_menace Sep 11 '20 at 23:12
  • No, I am writing an interface between native code and the Java API. The code before this was using views to control everything, but now that using views for status items is deprecated, an alternative was needed. – Kyle Berezin Sep 12 '20 at 02:52

2 Answers2

2

This solution came from a previous SO question:Highlighting NSStatusItem with attributed string and may do what you want. Unfortunately, popUpStatusItemMenu is now deprecated.

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate> {
  NSStatusItem* statusItem;
  NSMenu* statusMenu;
  NSMenuItem* menuItem;
}
-(void)statusItemHandler:(id)sender;
-(void)menuHandler:(id)sender;
@end

@implementation AppDelegate

-(void)menuHandler:(id)sender {
  NSLog(@"Menu item = %@",sender);
}

-(void)statusItemHandler:(id)sender {
 NSLog(@"StatusItem hit.");
 statusMenu = [[NSMenu alloc] init];
 menuItem = [statusMenu addItemWithTitle: @"Item 1" action:@selector(menuHandler:) keyEquivalent:@""];
 menuItem = [statusMenu addItemWithTitle: @"Item 2" action:@selector(menuHandler:) keyEquivalent:@""];
 [statusMenu addItem:[NSMenuItem separatorItem]];
 menuItem = [statusMenu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@""]; 
 [statusItem popUpStatusItemMenu:statusMenu];
}

-(void)applicationDidFinishLaunching:(NSNotification *)notification {
 statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; 
 statusItem.button.title = @"\U0001F410";      
 statusItem.button.action = @selector(statusItemHandler:);
}

@end

int main(){
 NSApplication *application = [NSApplication sharedApplication];
 [application setActivationPolicy:NSApplicationActivationPolicyRegular];
 AppDelegate *appDelegate = [[AppDelegate alloc] init];
 [application setDelegate:appDelegate];
 [application activateIgnoringOtherApps:YES];
 [application run];
 return 0;
}

apodidae
  • 1,988
  • 2
  • 5
  • 9
1

This nib-less programmatic approach will run in Xcode if you replace main.m and delete the MainMenu nib in the info.plist (uses ARC):

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate> {
  NSStatusItem* statusItem;
  NSMenuItem* menuItem;
}

-(void)stuffHappened:(id)sender;
@end

@implementation AppDelegate

//-(void)awakeFromNib{
- (void) applicationDidFinishLaunching:(NSNotification *)notification {
 NSMenu *menu = [[NSMenu alloc] init];
 statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:60]; 
 statusItem.button.title = @"foobar";   
 [statusItem setMenu:menu];
 menuItem = [menu addItemWithTitle:@"Item 1" action:@selector(stuffHappened:) keyEquivalent:@""];
 [menuItem setTarget:self];
 menuItem = [menu addItemWithTitle:@"Item 2" action:@selector(stuffHappened:) keyEquivalent:@""];
 [menuItem setTarget:self];
 [menu addItem:[NSMenuItem separatorItem]];
 menuItem = [menu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@""];    
}

-(void)stuffHappened:(id)sender {
  NSLog(@"Stuff happened : %@",sender);
}
@end

int main(){
 NSApplication *application = [NSApplication sharedApplication];
 [application setActivationPolicy:NSApplicationActivationPolicyRegular];
 AppDelegate *appDelegate = [[AppDelegate alloc] init];
 [application setDelegate:appDelegate];
 [application activateIgnoringOtherApps:YES];
 [application run];
 return 0;
}

apodidae
  • 1,988
  • 2
  • 5
  • 9
  • The issue is that I want to get events from the statusItem itself. This example only gives events for menuItems. Also sorry about the project structure, Xcode is confusing. – Kyle Berezin Sep 12 '20 at 20:42