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

9
我正在使用Swift编写一个简单的状态栏应用程序,并尝试使用在OS X 10.10中引入的新NSStatusItem API。
我希望实现的界面是:单击状态项左键可切换核心功能的开启和关闭,右键(或按住Control键单击)可显示设置菜单。对于此功能,我不需要自定义视图或弹出窗口。
默认情况下,如果将NSMenu分配给NSStatusItem,则它将在单击左键和右键时都显示菜单。我想改变这种行为,仅在右键单击时显示菜单,或者作为解决方法,防止在左键单击时弹出菜单。
以前,似乎要控制NSStatusItem上的鼠标事件,必须设置具有覆盖鼠标事件的自定义视图(请参见this related question)。
在10.10中引入的新NSStatusItem API 中,设置自定义视图的方法已被弃用,并且这种行为似乎不被鼓励。根据@Taylor在this answer中的说法,一些已弃用的行为应该通过statusItemObject.button()返回的NSStatusBarButton对象来使用,但是截至目前为止,没有关于NSStatusBarButton的文档,并且返回的对象是只读的,无法用具有覆盖鼠标事件处理程序的自定义按钮替换它。
是否有一种方法可以控制NSMenu附加到NSStatusItem(或NSStatusBarButton)时显示与鼠标事件相关的某个级别的控件?
2个回答

5
这是我想到的解决方案。它相当有效,但有一件事让我不满意:右键菜单中选择选项后,状态项目仍然会保持高亮显示。只要您与其他内容交互,突出显示就会消失。
此外,请注意,自 OS X 10.10(Yosemite)起,“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
一些注意事项(以下代码片段使用Swift编写):a)您可以通过为StatusItem对象设置item.highlightMode = false来解决右键单击后仍然存在的高亮问题。b)主要操作的情况应该首先出现在最终的else块中(在self.statusItemAction = JUNStatusItemActionNone;之前),以使所需的行为起作用。否则,您将设置self.statusItemAction的值,然后立即检查它是否不同。 - Kieran

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 并使其正常工作)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接