我建立了一个测试项目像你这样的,有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。
反正我黯然不认为有将是一种方式来获得这个工作短期使用讨厌的黑客使用的是什么,现在私人API,如this guy did和冒着将来破损的(更不用提从App Store中inst)而过)。或者做些什么确实是疯狂就像monkepatching其中一个C功能的回溯,这可能会更糟糕。不过,这整个问题确实值得雷达报告。请file one with Apple并让他们知道这个问题,也许他们会在将来的版本中修复它。
编辑:实际上有一种解决方案,有点。由于附加到没有子菜单的菜单项的视图确实会收到您所期望的鼠标事件,因此您可以放弃设置submenu
,并让您的视图捕获mouseEntered:
和mouseExited:
事件并自行显示菜单,从而模拟子菜单。不是世界上最理想的解决方案,但至少是这样。
您可以发布添加菜单项时的代码吗?从你的图片看来,你正在向菜单项添加一个视图(顶部),而不是将菜单项设置为自定义视图。这似乎是一个有趣的问题。 – Farini