Swift: NSStatusItem menu behaviour in 10.10 (e.g. show only on right mouse click)

前端 未结 2 2158
夕颜
夕颜 2021-02-14 22:36

I am writing a simple status bar app in Swift, and attempting to use the new NSStatusItem API introduced in OS X 10.10.

The interface I\'m aiming for is a simple left mo

2条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-02-14 22:57

    Here's the solution I came up with. It works fairly well, though there's one thing I'm not happy with: the status item stays highlighted after you choose an option in the right-click menu. The highlight goes away as soon as you interact with something else.

    Also note that popUpStatusItemMenu: is "softly deprecated" as of OS X 10.10 (Yosemite), and will be formally deprecated in a future release. For now, it works and won't give you any warnings. Hopefully we'll have a fully supported way to do this before it's formally deprecated—I'd recommend filing a bug report if you agree.

    First you'll need a few properties and an enum:

    typedef NS_ENUM(NSUInteger,JUNStatusItemActionType) {
        JUNStatusItemActionNone,
        JUNStatusItemActionPrimary,
        JUNStatusItemActionSecondary
    };
    
    @property (nonatomic, strong) NSStatusItem *statusItem;
    @property (nonatomic, strong) NSMenu *statusItemMenu;
    @property (nonatomic) JUNStatusItemActionType statusItemAction;
    

    Then at some point you'll want to set up the status item:

    NSStatusItem *item = [[NSStatusBar systemStatusBar] statusItemWithLength:29.0];
    NSStatusBarButton *button = item.button;
    button.image = [NSImage imageNamed:@"Menu-Icon"];
    button.target = self;
    button.action = @selector(handleStatusItemAction:);
    [button sendActionOn:(NSLeftMouseDownMask|NSRightMouseDownMask|NSLeftMouseUpMask|NSRightMouseUpMask)];
    self.statusItem = item;
    

    Then you just need to handle the actions sent by the status item button:

    - (void)handleStatusItemAction:(id)sender {
    
        const NSUInteger buttonMask = [NSEvent pressedMouseButtons];
        BOOL primaryDown = ((buttonMask & (1 << 0)) != 0);
        BOOL secondaryDown = ((buttonMask & (1 << 1)) != 0);
        // Treat a control-click as a secondary click
        if (primaryDown && ([NSEvent modifierFlags] & NSControlKeyMask)) {
            primaryDown = NO;
            secondaryDown = YES;
        }
    
        if (primaryDown) {
            self.statusItemAction = JUNStatusItemActionPrimary;
        } else if (secondaryDown) {
            self.statusItemAction = JUNStatusItemActionSecondary;
            if (self.statusItemMenu == nil) {
                NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
                [menu addItemWithTitle:NSLocalizedString(@"Quit",nil) action:@selector(terminate:) keyEquivalent:@""];
                self.statusItemMenu = menu;
            }
            [self.statusItem popUpStatusItemMenu:self.statusItemMenu];
        } else {
            self.statusItemAction = JUNStatusItemActionNone;
            if (self.statusItemAction == JUNStatusItemActionPrimary) {
                // TODO: add whatever you like for the primary action here
            }
        }
    
    }
    

    So basically, handleStatusItemAction: is called on mouse down and mouse up for both mouse buttons. When a button is down, it keeps track of whether it should do the primary or secondary action. If it's a secondary action, that's handled immediately, since menus normally appear on mouse down. If it's a primary action, that's handled on mouse up.

提交回复
热议问题