Swift:10.10中的NSStatusItem菜单行为(例如,只在鼠标右键单击时显示)

时间:2014-11-14 06:48:01

标签: swift osx-yosemite right-click nsmenu nsstatusitem

我正在Swift中编写一个简单的状态栏应用程序,并尝试使用OS X 10.10中引入的新NSStatusItem API。

我所瞄准的界面是在statusItem上单击鼠标左键,可以打开和关闭核心功能,只需右键单击(或按住Control键单击)选项即可显示设置菜单。我不需要自定义视图或弹出窗口来实现此功能。

默认情况下,如果将NSMenu分配给NSStatusItem,它将同时显示左右两侧的菜单。我想将行为更改为仅在右键单击时显示菜单,或者作为解决方法,防止左键单击菜单弹出

以前,似乎要控制NSStatusItem上的鼠标事件,必须使用重写的鼠标事件设置自定义视图(请参阅this related question)。

在10.10中引入的新NSStatusItem API中,不推荐使用设置自定义视图的方法,并且看起来不鼓励这种行为。根据{{​​3}}中的@Taylor,应该通过statusItemObject.button()返回的NSStatusBarButton对象使用一些不赞成的行为,但是在编写时没有NSStatusBarButton的文档,并且返回的对象是只读的,不能用带有重写鼠标事件处理程序的自定义按钮替换。

是否有办法对鼠标事件中是否显示附加到NSStatusItem(或NSStatusBarButton)的NSMenu进行某种程度的控制?

2 个答案:

答案 0 :(得分:5)

这是我提出的解决方案。它工作得相当好,虽然有一件事我不满意:在右键菜单中选择一个选项后,状态项会保持高亮显示。一旦你与其他东西互动,亮点就会消失。

另请注意,自OS X 10.10(优胜美地)起,popUpStatusItemMenu:被“轻度弃用”,并将在以后的版本中正式弃用。目前,它有效,不会给你任何警告。希望在正式弃用之前,我们会有一个完全支持的方式来执行此操作 - 如果您同意,我建议您filing a bug report

首先,您需要一些属性和枚举:

typedef NS_ENUM(NSUInteger,JUNStatusItemActionType) {
    JUNStatusItemActionNone,
    JUNStatusItemActionPrimary,
    JUNStatusItemActionSecondary
};

@property (nonatomic, strong) NSStatusItem *statusItem;
@property (nonatomic, strong) NSMenu *statusItemMenu;
@property (nonatomic) JUNStatusItemActionType statusItemAction;

然后在某些时候你会想要设置状态项:

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;

然后你只需要处理状态项按钮发送的动作:

- (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
        }
    }

}

所以基本上,在鼠标按下时调用handleStatusItemAction:并在两个鼠标按钮上按下鼠标。当按钮关闭时,它会跟踪它是应该执行主要操作还是辅助操作。如果它是次要操作,则会立即处理,因为菜单通常会在鼠标按下时出现。如果这是一个主要动作,那就是在鼠标上进行处理。

答案 1 :(得分:0)

这在10.10中已弃用,但将继续有效:

[self.statusItem setTarget:self]; // Otherwise this goes to the first responder
[self.statusItem setAction:@selector(statusItemClicked:)];
[self.statusItem sendActionOn:(NSRightMouseUpMask)];

您可以通过对它们进行位掩码来设置setActionOn中的其他事件。例如,如果您想要左右键单击:

[self.statusItem sendActionOn:(NSLeftMouseUpMask | NSRightMouseUpMask)];

(请原谅objC,你应该能够把它翻译成swift并且应该有效)