我试图了解可可在按下鼠标按钮时如何发送鼠标消息。
我的背景是Window的编程,所以这就是我的观点。
窗口发送鼠标按下事件
该应用程序可以调用SetCapture(hWnd)以请求将所有将来的鼠标事件传递到该窗口,而不是传递到鼠标下方的窗口
完成后,该应用将调用ReleaseCapture以恢复正常的鼠标调度
该应用可能会收到WM_CANCELMODE消息,指示其释放捕获并取消任何跟踪模式。 (例如:如果您在鼠标拖动操作中按住Alt-Tab进入,Windows会发送此消息)
所以我了解可可的方式,我的理解方式是...
NSWindow / NSView收到NSLeftMouseDown事件
当将鼠标按钮按住到单击的窗口/视图时,可可自动“捕获”鼠标并发送NSLeftMouseDragged事件。
NSWindow / NSView收到NSLeftMouseUp事件
这是我的问题:
窗口是否可以取消捕获的鼠标并将其显式重新捕获到另一个窗口?
有没有一种方法可以找出当前“捕获”了鼠标的哪个窗口(和/或视图)?
在任何情况下,视图可能会收到不平衡的mouseDown / Up事件?
我应该注意像WM_CANCELMODE这样的事件,以取消当前的跟踪操作吗?
为了说明我要达到的目的,我在应用程序中有一个滑块控件:
鼠标按下时会在弹出窗口中显示较大的版本:
弹出时,鼠标光标将自动移至较大滑块的手柄并跟踪鼠标,直到释放鼠标为止,然后删除弹出窗口并将光标移回较小滑块。 (显示为Here's a video)
我需要将捕获的鼠标事件重定向到弹出窗口NSWindow,或者在我的鼠标跟踪循环中将当前捕获了鼠标的NSWindow重定向。我可以通过跟踪鼠标事件来解决这个问题,但是认为可能会有一个API来获取它(例如:类似于Window的GetCapture()API)。
顺便说一句:我实际上已经在工作,但是我做的事情有点怪异,我想更好地理解OSX的方法-并确保我没有遗漏任何明显/容易的事情。
最佳答案
注意:我开始这个答案,它最终离我远去。很抱歉阅读了很长时间,但是我相信我涵盖了您需要了解的有关事件和响应者的所有内容,以及随着时间的推移我遇到的一些问题的提示。希望我的解释简单易懂,请告诉我是否需要更多的说明^^
我要说的是-根据您的视频,看来您可以正常使用了:)
因此,有很多文档涵盖了我将要解释的内容,但是我将尝试分享对关键点的简化理解,希望它比《苹果参考》更有用。
首先,有人可能会纠正我的较粗略的OS概念(无论如何对您的问题并不那么重要):
有“运行循环”,基本上可以侦听和处理事件。每个运行循环都与一个线程关联
与主线程关联的运行循环是接收鼠标事件和键盘事件的循环
然后,我认为更重要的概念是:
接收到事件后,它将通过消息-sendEvent:
传递到活动应用程序(在NSEvent
中检出NSApplication
类别)
应用程序确定关键窗口,并确定事件的相应消息(单击鼠标左键将获得-mouseDown:
事件)
接收事件的窗口确定“第一响应者”。注意,任何NSView
实际上都是从NSResponder
继承的,任何NSResponder
都可以位于响应者链中。 NSWindow
和其他AppKit对象也都是NSResponder
的子类,
如果事件是鼠标事件,则实际上会将事件发送到鼠标下方的最上方视图
您可以覆盖-mouseDown:
或-mouseDragged:
或任何采用该事件的参数(唯一的参数),并且可以将其传递给您想要的任何NSResponder
,只要您当然有对其的引用即可。
如果对象不响应事件,它将沿着响应者链向下传递
从字面上看,响应者链就像一个单链表。头节点为[NSWindow firstResponder]
,每个NSResponder
都有一个名为nextResponder
的属性。
最后,这是我们其中一个对象中-mouseDown:
事件断点的屏幕截图
注意我们在主线程中,在运行循环中,我们的应用程序首先获取事件,将事件传递给窗口,窗口确定第一个响应者(CanvasMaskView
,因为这是鼠标单击,所以这是最顶部的,然后我们实际上将事件手动传递给响应者链
然后枚举响应者链,直到我们最终在顶部的-mouseDown:
中找到一个处理ImageController
的对象。
最后,在调用堆栈中注意所有forwardMethods
吗?那就是事件被传递到nextResponder
的nextResponder
的nextResponder
等。
可以通过在每个堆栈帧中检出$r13
寄存器来证明这一点,该寄存器包含事件的当前接收者。请注意,在此屏幕快照中,我单击了一个堆栈框架,并在lldb中使用了po $r13
,然后单击了下一个堆栈框架,并执行了相同的操作:
这很重要,因为您可能在您不知道的链中有一个响应者,该响应者消耗了事件并且没有传递事件,除非您调查链,否则您将一无所知。通过“传递”,我的意思是您需要调用[super mouseDown]
,它将在需要时自动传递事件。
哦,是的,最后一点!如果您要覆盖-mouseDown
,我发现您必须致电super mouseDown
。否则,-mouseUp:
事件将消失。不过,这很有趣,我敢肯定[super mouseDown]
可能会在操作系统中注册视图,因此它知道将-mouseUp:
推测发送到哪里
关于objective-c - 了解NSView/NSWindow鼠标捕获和分发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44308314/