在显示弹出窗口时保持 NSStatusBarButton 突出显示

Keeping an NSStatusBarButton highlighted while popover is displayed

实际上所有 NSStatusItem 都已在 10.10 中弃用,底层 NSStatusBarButton 的行为似乎令人困惑。

目前我正在开发一个菜单栏应用程序。当用户单击应用程序的菜单栏图标时,我的应用程序委托中的一个方法将通过目标操作调用,该方法显示带有一些信息的 NSPopover(如果它已经可见,则将其关闭)。

通常情况下,如果您将 NSMenu 与 NSStatusItem 相关联,那么当用户单击菜单栏图标时,该图标将一直突出显示为蓝色,直到菜单关闭。类似地,单击系统音量图标会弹出一个滑块并将其图标突出显示为蓝色,直到包含该滑块的视图消失。

但是,由于我是打开 NSPopover 的人,系统反而会在鼠标按下时突出显示蓝色图标,然后 return在调用我的方法后在鼠标抬起时将其恢复正常。这意味着我似乎无法在该循环上做任何事情来保持高光。我希望图标在鼠标向上时继续突出显示,并且只有 return 在我告诉它时(即当我关闭弹出窗口时)才正常

我不知道该怎么做。我试过使用

[self.statusItem.button setHighlighted: YES];
//or [self.statusItem.button highlight: YES];

当我在我的应用委托中收到鼠标弹起事件并打开弹出窗口时。问题是系统仍然有它,显然,从早期的鼠标按下时突出显示此 frame/loop,并且在我将其设置为突出显示后,由于鼠标抬起,它立即将其设置为未突出显示。我可以通过将其封装在一个方法中来解决这个问题,然后 运行 该方法稍后使用计时器或延迟选择器。这允许我保持图标突出显示但引入了闪烁;当鼠标向下移动时,图标会自动突出显示,当鼠标向上移动时,它会在一帧内取消突出显示,然后我的方法会重新突出显示它。

我还想也许我可以使用已弃用的 setHighlightMode: 并将其设置为 NO 以防止图标在单击时自动突出显示,然后使用 setHighlighted: / highlighted: 手动设置它但这也不管用。同样,我想也许这也行得通:

 NSButtonCell* cell = (NSButtonCell*)self.statusItem.button.cell;
cell.highlightsBy = NSNoCellMask;

但无论点击它会自动突出显示图标,并在调用我的方法后立即取消突出显示。

基本上:

  1. The undesirable automatic highlighting behavior of NSStatusBarButton interferes with manually setting the highlight state, unless I delay manually setting it which introduces a short flicker.
  2. The only thing that seems to successfully disable this automatic behavior is the deprecated setHighlightMode:, but this seems to prevent all highlighting, manual or not.
  3. The only work around seems to be to add a subview to the NSButtonCell, add an event listener for mouse up and then set the highlight state of the superview as per here: NSStatusBarButton keep highlighted but I would think there'd be a simpler way to just... disable the automatic highlighting altogether.

tl;dr:有没有一种方法可以让我轻松地完全控制何时以及何时不突出显示我的菜单栏图标,以便我可以在显示 NSPopover 时让它自然突出显示?

我最终通过不设置 NSStatusItem 的动作选择器 属性 解决了这个问题。相反,我使用了 NSEventaddLocalMonitorForEventsMatchingMask:handler:。在处理程序块中,我检查 event.locationInWindow 是否在我的状态项的 .bounds 内。如果是这样,我将手动发送 .action 的消息,然后 return nil 以防止事件被传递。如果它不在状态图标的范围内,我 return event 所以它会正常传递。在我的点击处理方法中,当我的弹出窗口为 displayed/closed 时,我使用 [self.statusItem.button highlight: YES/NO]

长话短说:

applicationDidFinishLaunching:

__block AppDelegate* appDelegate = self;
[NSEvent addLocalMonitorForEventsMatchingMask: NSEventMaskFromType(NSLeftMouseDown) handler:^NSEvent* (NSEvent* event){
    if (NSPointInRect(event.locationInWindow, appDelegate.statusItem.button.bounds)){
        [appDelegate clickedMenuBarIcon: event];
        return nil;
    }
    return event;
 }];

clickedMenuBarIcon:我就可以设置高亮状态了。因为我在我的处理程序块中 returned nil 它阻止了事件的传递,所以自动突出显示永远不会发生,我可以手动完成。


如果有任何与此相关的错误,我将不胜感激任何建议。