可可:拖动时不会触发键盘事件(NSEventTrackingRunLoopMode) [英] Cocoa: No keyboard events will fire while dragging (NSEventTrackingRunLoopMode)
问题描述
我可以通过我的窗口控件的keyDown:方法成功地对键盘事件作出反应。
执行鼠标拖动时出现问题:
键盘事件似乎被延迟,只会在鼠标上起火。要清楚,我的意思是:
•在你的窗口控制器的keyDown:方法中放置一个日志语句
•启动您的应用程序,执行一些拖动操作(例如NSSlider)
•同时保持拖动,按任意键:无任何日志到控制台。
•释放拖动:日志出现,yay ...
我拖动的控件是一个自定义NSSlider。
我已经实现了拖动机制,使用鼠标跟踪循环方法
为了理解,拖动时,NSApplication的主运行环路模式正在切换到NSEventTrackingRunLoopMode,从而限制传入事件。
所以,我只是添加了NSKeyDownMask& ; NSKeyUpMask在我的跟踪循环中,遇到时,相应地调用self.nextResponder keyDown / up:方法。
我的问题解决了这个特定的自定义子类。
但是可可的本机控件呢?我不能编写这个例外...
我曾经希望NSEvent的addLocalMonitorForEventsMatchingMask:方法
但唉,说:不会被调用对于由嵌套事件跟踪循环(如控制跟踪,菜单跟踪或窗口拖动)消耗的事件。
所以,没有一个直接的解决方案来接收键盘事件无论应用程序的runloop模式如何?
正如您在 NSEvent
class' addGlobalMonitorForEventsMatchingMask:handler:
,这个限制是设计的。
但是,你可以使用IOKit框架(特别是IOHID部分)来接收低级别的设备事件/中断。我最近只需要在鼠标拖动期间跟踪一些特定的按键。
基本要点是使用 IOHIDManagerCreate创建一个IOHID管理器)
,然后将设备类型添加到管理器中,该应用程序应该使用 IOHIDManagerSetDeviceMatchingMultiple()
监控注册一个回调经理通过 IOHIDManagerRegisterInputValueCallback()
,为 IOHIDManagerScheduleWithRunLoop()为管理员安排正确的运行循环,最后打开经理与 IOHIDManagerOpen()
。
要在鼠标拖动期间获取这些低级别的事件,请在单独的线程。在调度管理器的运行循环时,使用 CFRunLoopGetCurrent()
获取当前线程的运行循环,并调用 CFRunLoopRun() code> after
IOHIDManagerOpen()
。
本指南来自Apple 可以帮助您开始使用这个问题在这里Stack Overflow 。
I am successfully able to react to keyboard events through my window controller's keyDown: method.
The problem arises while performing a mouse drag:
Keyboard events seem to be delayed and will only fire on mouse up.
To be clear, what I mean is:
• place a log statement in you window controller's keyDown: method
• launch your app, perform some drag operation (on a NSSlider for ex.)
• while maintaining the drag, press any key: nothing logs to the console.
• release drag : logs appear, yay…
The control i am dragging is a custom NSSlider.
I have implemented the dragging mechanism using a 'Mouse-Tracking Loop' Approach.
for what I understand, when dragging, NSApplication's main run loop mode is being switched to NSEventTrackingRunLoopMode, thus restricting incoming events.
So, i simply added NSKeyDownMask & NSKeyUpMask in my tracking loop and when encoutered, called self.nextResponder keyDown/up: method accordingly. My problem is solved for this particular custom subclass.
But what about cocoa's native controls ? I can't code that exception...
I had hoped for NSEvent's "addLocalMonitorForEventsMatchingMask:" method but alas, says doc : "will not be called for events that are consumed by nested event-tracking loops such as control tracking, menu tracking, or window dragging".
So, isn't there a straightforward solution to receive keyboard events regardless of the app's runloop mode ?
As you found in the documentation for the NSEvent
class' addGlobalMonitorForEventsMatchingMask:handler:
, this limitation is by design.
However, you can work around it by using the IOKit framework (specifically the IOHID portions) to receive low level device events/interrupts. I just had to do this recently for tracking some specific keypresses during mouse-drags.
The basic gist is to create an IOHID manager with IOHIDManagerCreate()
, then add the type(s) of devices to the manager that it should "monitor" with IOHIDManagerSetDeviceMatchingMultiple()
, register a callback with the manager via IOHIDManagerRegisterInputValueCallback()
, schedule the proper run-loop for the manager with IOHIDManagerScheduleWithRunLoop()
, and finally open the manager with IOHIDManagerOpen()
.
To get these low level events during mouse-drags, perform this setup in a separate thread. When scheduling the run-loop for the manager, use CFRunLoopGetCurrent()
to get the run loop for the current thread, and call CFRunLoopRun()
after IOHIDManagerOpen()
.
This guide from Apple can help you get started, along with this Q&A here on Stack Overflow.
这篇关于可可:拖动时不会触发键盘事件(NSEventTrackingRunLoopMode)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!