2017-06-01 76 views
1

我想了解如何按住鼠标按钮时Cocoa的鼠标消息分派工作。了解NSView/NSWindow鼠标捕获和发送

我的背景是在Windows的编程所以这次我来自的角度...

  1. 窗口发送鼠标按下事件
  2. 应用程序可以调用SetCapture(HWND),要求今后所有的鼠标事件鼠标
  3. 的应用下得到传递到窗口,而不是窗口做恢复正常小鼠调度时调用ReleaseCapture
  4. 的应用程序可能会收到该指令它释放捕获并取消所有追踪模式的WM_CANCELMODE消息。 (例如:远离窗户的鼠标拖拽操作期间发送这个,如果你使用Alt-Tab)

所以我的学习围绕可可,我明白了事情是这样的方式......

  1. NSWindow /的NSView收到NSLeftMouseDown事件
  2. 可可自动“捕获”鼠标并发送NSLeftMouseDragged事件,同时鼠标按钮被保持到被单击的窗口/视图。
  3. NSWindow /接收的NSView事件NSLeftMouseUp

这里是我的问题:

  1. 是否有一个窗口中取消捕捉的老鼠,并明确将其重新捕捉到另一个窗口的方法吗?
  2. 有没有办法找出哪个窗口(和/或视图)当前有鼠标“捕获”?
  3. 是否有任何情况下视图可能会收到不平衡的mouseDown/Up事件?
  4. 是否有像WM_CANCELMODE这样的事件,我应该注意取消当前的跟踪操作?

要解释什么是我想要实现的,我有我的应用程序滑块控件:

Slider

上按下鼠标显示了在一个弹出窗口放大版本:

SliderCap

当弹出的鼠标光标被自动移动到较大的滑块的把手,直到它释放跟踪鼠标之后将弹出被删除,光标移回到较小的滑块。 (Here's a video显示它)

我需要将捕获的鼠标事件重定向到弹出的NSWindow,或者在我的鼠标跟踪循环中已知NSWindow当前捕获鼠标。我可以通过跟踪鼠标事件来解决这个问题,但认为可能有API来获取它(例如:像Window的GetCapture()API)。

btw:我实际上有这个工作,但是我做的感觉有点冒失,我想更好地理解OSX的方法 - 只要确保我没有漏掉任何明显/更容易的东西。

+1

滑块是'NSlider'还是自定义控件? – Willeke

+0

它必须是一个自定义控件来处理鼠标事件,对吗? –

+0

其实让我想到了一个我从未意识到的问题,AppKit控件是否有自己的'-mouseDown'实现?所以就像是如果你划分了'NSSlider',覆盖'-mouseDown',你能不小心移除滑块的原始功能(移动旋钮?) –

回答

1

注意:我开始这个答案,它结束了离我而去。对于长时间的阅读感到抱歉,但我相信我涵盖了您需要知道的关于事件和响应者的所有信息,以及一些我随时都会遇到的问题的提示。希望我的解释是简单易懂,只是让我知道如果你需要更多的阐述^^

我要say--基于关闭您的视频看起来你有它的工作:)

因此,有大量的文档覆盖了我要解释的内容,但我会尽量分享我对关键点的简单理解,并希望这比Apple参考更有帮助。首先,粗略的操作系统的概念,有人可能会纠正我(不是真的那么重要你的问题反正):

  • 有“运行循环”,它基本上监听和处理事件。每次运行循环与螺纹
  • 与主线程相关的运行循环关联的是接收鼠标事件和键盘事件

然后,我敢肯定的比较重要的概念之一:

  • 事件之后被接收,则通过消息-sendEvent:传递到活动应用(结帐在NSApplicationNSEvent类别)
  • 的应用程序确定是键,以及DET窗口貂皮事件的相应消息(鼠标左键单击将获得-mouseDown:事件)
  • 接收事件的窗口确定“First Responder”。请注意,任何NSView实际上都继承自NSResponder,并且任何NSResponder都可以位于响应者链中。 NSWindow等了AppKit对象也继承NSResponder以及[1]: https://i.
  • 如果该事件是一个鼠标事件,将事件实际发送到鼠标下方的最顶部视图
  • 您可以覆盖-mouseDown:-mouseDragged:或任何取事件(唯一参数),并且您可以将它传递给任何NSResponder,只要您有参考
  • 如果对象不响应该事件,它会将它传递给它响应者链
  • 响应者链实际上就像一个单链表。头节点将是[NSWindow firstResponder],每NSResponder有一个名为nextResponder

最后的财产,这里是一个-mouseDown:事件断点在我们的目标之一

enter image description here

通知截图我们在主线程中,在运行循环中,我们的应用程序首先接受事件,将它传递给窗口,窗口确定第一响应者(CanvasMaskView因为这是一个鼠标单击,所以这是最下面的视图鼠标),并且我们实际上手动传递了e发泄响应链 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:推测虽然

+0

感谢您的详细回复,不幸的是,它不回答我问的问题。我意识到响应者链和运行循环以及在这方面如何发送消息。我不清楚的是用于将鼠标事件路由到最初点击的窗口的机制,而不是鼠标点击/拖动时结束的窗口。即:该语句并非总是如此:“如果事件是鼠标事件,它实际上会将事件发送到鼠标下方的最顶层视图”,因为在点击/拖动时,它们将转到最初单击的视图。 –

+0

@AO我也对此感兴趣 – StefanS