当弹出窗口显示时保持NSStatusBarButton高亮

7

几乎所有NSStatusItem都已被10.10弃用,底层NSStatusBarButton的行为似乎令人困惑。

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

通常,如果您将NSMenu与NSStatusItem关联起来,当用户单击菜单栏图标时,该图标保持突出显示状态,直到菜单关闭。类似地,单击系统音量图标会弹出滑块,并使其图标保持突出显示状态,直到包含滑块的视图消失。

但是,由于我打开了NSPopover,因此系统会在鼠标按下时突出显示该图标,然后在调用我的方法后在鼠标抬起时返回正常状态。这意味着在该循环中似乎没有什么我可以做来维护突出显示状态。我希望该图标在鼠标抬起时继续保持突出显示状态,只有在我告诉它要关闭我的popover时才返回正常状态。

我不知道该如何做到这一点。我尝试过使用

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

当我在应用委托中接收到鼠标弹起事件并打开弹出窗口时,问题是系统仍然将该帧/循环从先前的鼠标按下处于高亮状态,而在我将其设置为高亮状态后,由于鼠标弹起,它立即将其设置为非高亮状态。我可以通过封装此内容在一个方法中,稍后使用定时器或延迟选择器运行该方法来解决这个问题。这使我可以保持图标高亮状态,但是会出现闪烁;当鼠标按下时,图标会自动高亮,当鼠标松开时,它会在一帧中取消高亮,然后我的方法重新将其高亮。
我还想到,也许我可以使用已弃用的setHighlightMode:并将其设置为NO,以防止图标在单击时自动高亮,然后使用setHighlighted:/highlighted:手动设置它,但这也不起作用。同样,我认为这也可能起作用:
 NSButtonCell* cell = (NSButtonCell*)self.statusItem.button.cell;
cell.highlightsBy = NSNoCellMask;

但是,无论如何,单击它都会自动突出显示图标,并在鼠标抬起后取消高亮显示,我的方法被调用。基本上来说:
1.不希望的NSStatusBarButton自动高亮行为干扰了手动设置高亮状态,除非我延迟手动设置,这会引入短暂的闪烁。
2.唯一似乎成功禁用此自动行为的是已弃用的setHighlightMode:,但这似乎阻止了所有高亮,无论是手动还是自动。
3.唯一的解决方法似乎是向NSButtonCell添加子视图,添加鼠标抬起事件侦听器,然后根据此处设置父视图的高亮状态:NSStatusBarButton keep highlighted,但我认为应该有一种更简单的方法来完全禁用自动突出显示。
总之:有没有一种简单的方法让我完全控制我的菜单栏图标何时何时不高亮,以便在显示NSPopover时可以自然突出显示它?

2
一些尝试的方法:1)将一个空菜单添加到项目中。2)使用一个空菜单调用-popUpStatusItemMenu:。3)运行内部消息循环([NSApp nextEventMatchingMask:...][NSApp sendEvent:...]),以防止您的点击处理程序在弹出窗口关闭之前返回。 - Ken Thomases
我会在有时间的时候尝试,谢谢。 - Metabble
无法使用空菜单。调用popUp似乎没有任何作用。同时,空菜单还会吞噬通常发送到状态项的.action选择器的调用。不确定内部消息循环是否有效,但与链接帖子中的子视图解决方案相比,它并不简单... - Metabble
@KenThomases 我最终实现了类似于#3的东西,但是使用NSEvent和addLocalMonitor。 - Metabble
请查看我在SO上类似问题的答案:https://dev59.com/j18e5IYBdhLWcg3wLX8-#35072577。 - Manfred Urban
1个回答

10
我最终解决了这个问题,方法是不设置NSStatusItem的动作选择器属性。我使用NSEventaddLocalMonitorForEventsMatchingMask:handler:。在处理程序代码块中,我检查event.locationInWindow是否在我的状态栏项目的.bounds内。如果是,则手动发送.action消息,然后返回nil以防止事件被传递。如果不在状态图标的边界内,则返回event以使其正常传递。在我的点击处理方法中,当我的弹出窗口显示/关闭时,我使用[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: 方法中,我可以设置高亮状态。由于在我的处理程序块中返回了 nil,它阻止了事件传递,因此自动高亮永远不会发生,我可以手动完成。


如果与此相关的有任何错误,我将非常感谢任何建议。


也许测试 e.window == statusItem.button.window 更可靠? - Nozama

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