objective-c - 了解NSView/NSWindow鼠标捕获和分发

标签 objective-c macos cocoa nsview nswindow

我试图了解可可在按下鼠标按钮时如何发送鼠标消息。

我的背景是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的子类,[1]: https://i.
如果事件是鼠标事件,则实际上会将事件发送到鼠标下方的最上方视图
您可以覆盖-mouseDown:-mouseDragged:或任何采用该事件的参数(唯一的参数),并且可以将其传递给您想要的任何NSResponder,只要您当然有对其的引用即可。
如果对象不响应事件,它将沿着响应者链向下传递
从字面上看,响应者链就像一个单链表。头节点为[NSWindow firstResponder],每个NSResponder都有一个名为nextResponder的属性。


最后,这是我们其中一个对象中-mouseDown:事件断点的屏幕截图

enter image description here

注意我们在主线程中,在运行循环中,我们的应用程序首先获取事件,将事件传递给窗口,窗口确定第一个响应者(CanvasMaskView,因为这是鼠标单击,所以这是最顶部的,然后我们实际上将事件手动传递给响应者链
enter image description here

然后枚举响应者链,直到我们最终在顶部的-mouseDown:中找到一个处理ImageController的对象。

最后,在调用堆栈中注意所有forwardMethods吗?那就是事件被传递到nextRespondernextRespondernextResponder等。

可以通过在每个堆栈帧中检出$r13寄存器来证明这一点,该寄存器包含事件的当前接收者。请注意,在此屏幕快照中,我单击了一个堆栈框架,并在lldb中使用了po $r13,然后单击了下一个堆栈框架,并执行了相同的操作:
enter image description here

这很重要,因为您可能在您不知道的链中有一个响应者,该响应者消耗了事件并且没有传递事件,除非您调查链,否则您将一无所知。通过“传递”,我的意思是您需要调用[super mouseDown],它将在需要时自动传递事件。

哦,是的,最后一点!如果您要覆盖-mouseDown,我发现您必须致电super mouseDown。否则,-mouseUp:事件将消失。不过,这很有趣,我敢肯定[super mouseDown]可能会在操作系统中注册视图,因此它知道将-mouseUp:推测发送到哪里

关于objective-c - 了解NSView/NSWindow鼠标捕获和分发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44308314/

相关文章:

ios - 多个 KVO 键 : Why call willChangeValueForKey: twice before didChangeValueForKey:?

Objective-C 字符串差异

ios - 在 iOS 中访问用户联系人的问题

ios - 如何识别无法识别的选择器和无法识别的对象?

linux - *nix 配置文件存储约定?

SVN 不会提交未版本控制的文件,即使它们显示为 svn 状态

Objective-C 网络 - 最佳实践?

ios - 从 Objective-C 中的描述字符串中获取日期

windows - 远程访问 Windows Vista 到 Mac OSX?

objective-c - 网络广播的流媒体基本上是如何工作的?