2017-06-13 92 views
8

我正在使用菜单栏应用程序,并使用NSMenuItemview属性设置自定义视图。带自定义视图的NSMenuItem不会收到鼠标事件

该视图显示正常,但我无法接收任何种类的鼠标点击事件,菜单项具有打开的子菜单。

在此屏幕截图中,我为每个项目添加了一个按钮。最右边的3个按钮功能正常,但父菜单中的按钮根本不会收到任何点击事件。

Screenshot

我已经尝试了一堆东西,其中包括:

  • 尝试使用mouseUpmouseDown方法
  • 使得NSWindow自定义视图键拍摄鼠标事件时鼠标进入该视图
  • 添加全局和本地显示器NSEvents

...但无济于事

即使没有添加按钮的方法,我不能复制的标准NSMenuItem默认行为,为target-action回调的NSMenuItem不会被调用,如果它有一个自定义的视图。 (我不能接收任何点击事件来自己调用它)

理论上这应该是可能的,因为我可以选择使用默认NSMenuItem(无自定义视图)打开子菜单的菜单。

有人能帮忙吗?

感谢

+2

您可以发布添加菜单项时的代码吗?从你的图片看来,你正在向菜单项添加一个视图(顶部),而不是将菜单项设置为自定义视图。这似乎是一个有趣的问题。 – Farini

回答

4

我建立了一个测试项目像你这样的,有NSButton S作为view的菜单项,看见你看到相同的行为。这确实很有趣。如果您子类NSApplication并覆盖其-sendEvent:方法,添加一个日志以查看哪些事件通过该机制,您会发现-sendEvent:实际上从未实际调用,当您单击任何菜单项时,即使是做的都可以工作。这不奇怪吗?因此接下来要尝试的是子类NSButton,为-mouseDown:添加覆盖,并在那里放置一个断点。果断的是,打开子菜单的项目决不会触发断点,但其他项目会触发断点。而当我们这样做,回溯是:

(lldb) bt 
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 
    * frame #0: 0x0000000100002fa0 menutest`MyButton.mouseDown(event=0x0000608000121900, self=0x0000600000140a50) at AppDelegate.swift:33 
    frame #1: 0x000000010000303c menutest`@objc MyButton.mouseDown(with:) at AppDelegate.swift:0 
    frame #2: 0x00007fffa9f6724f AppKit`-[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 6341 
    frame #3: 0x00007fffa9f63a6c AppKit`-[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 1942 
    frame #4: 0x00007fffa9f62f0a AppKit`-[NSWindow(NSEventRouting) sendEvent:] + 541 
    frame #5: 0x00007fffa9a2328d AppKit`-[NSCarbonWindow sendEvent:] + 118 
    frame #6: 0x00007fffa9a20261 AppKit`NSMenuItemCarbonEventHandler + 10597 
    frame #7: 0x00007fffab0acd85 HIToolbox`DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) + 1708 
    frame #8: 0x00007fffab0abff6 HIToolbox`SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) + 428 
    frame #9: 0x00007fffab0c1d14 HIToolbox`SendEventToEventTarget + 40 
    frame #10: 0x00007fffab0ea7df HIToolbox`ToolboxEventDispatcherHandler(OpaqueEventHandlerCallRef*, OpaqueEventRef*, void*) + 2503 
    frame #11: 0x00007fffab0ad17a HIToolbox`DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) + 2721 
    frame #12: 0x00007fffab0abff6 HIToolbox`SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) + 428 
    frame #13: 0x00007fffab0c1d14 HIToolbox`SendEventToEventTarget + 40 
    frame #14: 0x00007fffab12e928 HIToolbox`IsUserStillTracking(MenuSelectData*, unsigned char*) + 1658 
    frame #15: 0x00007fffab255dc4 HIToolbox`TrackMenuCommon(MenuSelectData&, unsigned char*, SelectionData*, MenuResult*, MenuResult*) + 1664 
    frame #16: 0x00007fffab13a223 HIToolbox`MenuSelectCore(MenuData*, Point, double, unsigned int, OpaqueMenuRef**, unsigned short*) + 554 
    frame #17: 0x00007fffab139f66 HIToolbox`_HandleMenuSelection2 + 460 
    frame #18: 0x00007fffa97ee368 AppKit`_NSHandleCarbonMenuEvent + 239 
    frame #19: 0x00007fffa9a68702 AppKit`_DPSEventHandledByCarbon + 54 
    frame #20: 0x00007fffa9de90c5 AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 963 
    frame #21: 0x00007fffa96623db AppKit`-[NSApplication run] + 926 
    frame #22: 0x00007fffa962ce0e AppKit`NSApplicationMain + 1237 
    frame #23: 0x00000001000035fd menutest`main at AppDelegate.swift:13 
    frame #24: 0x00007fffc12fc235 libdyld.dylib`start + 1 

正如你所看到的,活动未通过可可事件调度机制,因为菜单实际上是碳出动。没错,许多那些据称在64位转换中被删除的Carbon API和子系统实际上仍然非常活跃,他们现在只是私人API。 我们不能在64位模式下使用它们,但Apple肯定可以,而整个菜单系统仍然是在Carbon事件模型的基础上实现的。因为第三方开发人员必须从头开始重写Photoshop,这是可以的,但有人在1997年编写的菜单处理代码太贵了,不能放弃,我相信你也同意。

无论如何,我在这个backtrace(最顶层的东西除外)中调用了-[NSCarbonWindow sendEvent:]这个最早的Objective-C方法做了一些测试,看看它是否在子菜单项被点击时被调用,而不是。所以如果我不得不猜测,我会说问题在于碳事件处理程序。好吧,这可能会在后端造成一点痛苦,但是,嘿,没问题!我们可以通过下降到Carbon级别并安装我们自己的Carbon事件处理程序来解决此问题。好吧,卷起​​你的袖子,让我们来做 -

哦,对。

我们不能在64位模式下使用这些API。

Picard Facepalm

反正我黯然不认为有将是一种方式来获得这个工作短期使用讨厌的黑客使用的是什么,现在私人API,如this guy did和冒着将来破损的(更不用提从App Store中inst)而过)。或者做些什么确实是疯狂就像monkepatching其中一个C功能的回溯,这可能会更糟糕。不过,这整个问题确实值得雷达报告。请file one with Apple并让他们知道这个问题,也许他们会在将来的版本中修复它。

编辑:实际上有一种解决方案,有点。由于附加到没有子菜单的菜单项的视图确实会收到您所期望的鼠标事件,因此您可以放弃设置submenu,并让您的视图捕获mouseEntered:mouseExited:事件并自行显示菜单,从而模拟子菜单。不是世界上最理想的解决方案,但至少是这样。

相关问题