简体   繁体   中英

Understanding NSView/NSWindow mouse capture and dispatch

I'm trying to understand how Cocoa's dispatch of mouse messages works while the mouse button is pressed.

My background is in Window's programming so this the perspective I'm coming from...

  1. Window sends mouse down event
  2. The app can call SetCapture(hWnd) to request all future mouse events get delivered to that window, rather than the window under the mouse
  3. The app calls ReleaseCapture when done to restore normal mouse dispatch
  4. The app might receive a WM_CANCELMODE message which instructs it to release capture and cancel any tracking modes. (eg: Windows sends this if you Alt-Tab away in during the mouse drag operation)

So learning my way around Cocoa, the way I understand it is...

  1. NSWindow/NSView receives NSLeftMouseDown event
  2. Cocoa automatically "captures" the mouse and sends NSLeftMouseDragged events while the mouse button is held to the window/view that was clicked.
  3. NSWindow/NSView receives NSLeftMouseUp event

Here's my questions:

  1. Is there a way for a window to cancel the captured mouse and explicitly re-capture it to another window?
  2. Is there a way to find out which window (and/or view) currently has the mouse "captured"?
  3. Are there any scenarios in which a view might receive unbalanced mouseDown/Up events?
  4. Is there an event like WM_CANCELMODE that I should be watching for to cancel the current tracking operation?

To explain what I'm trying to achieve, I have a slider control in my app:

滑块

that on mouse down shows up a larger version in a popup window:

SliderCap

When it pops up the mouse cursor is automatically moved to the larger slider's handle and tracks the mouse until it's released after which the popup is removed and the cursor moved back to the smaller slider. ( Here's a video showing it)

I need to either redirect captured mouse events to the popup NSWindow, or in my mouse tracking loop known which NSWindow currently has the mouse captured. I could work this out by tracking mouse events but thought there might be an API to get it (eg: like Window's GetCapture() API).

btw: I actually have this working but what I've done feels a bit hacky and I'd like to understand OSX's approach better - and just make sure I'm not missing anything obvious/easier.

NOTE: I started this answer, and it ended up getting away from me. Sorry for the long read, but I believe I covered everything about events and responders that you'll need to know, as well as some tips with issues that I've experienced over time. Hopefully my explanations are simple and understandable, just let me know if you need more elaboration ^^

I was going to say-- based off your video it looks like you have it working :)

So there's plenty of documentation out there that covers what I'm about to explain, but I'm going to try to share my simplified understanding of the key points, and hopefully that is more helpful than the Apple Reference

First, the rougher OS concepts that somebody will probably correct me on (not really that important to your question anyways):

  • There are "run loops", which basically listen and handle events. Each run loop is associated with a thread
  • The run loop associated with the main thread is the one that receives the mouse events and keyboard events

Then, the more important concepts that I'm positive of:

  • After the event is received, it is passed to the active application via the message -sendEvent: (Checkout the NSEvent category in NSApplication )
  • The application determines the window that is key, as well as determine the corresponding message for the event (a left mouse click would get the -mouseDown: event)
  • The window receiving the event determines the "First Responder". Notice that any NSView actually inherits from NSResponder , and any NSResponder can be in the responder chain. NSWindow and other AppKit objects also subclass NSResponder as well [1]:https:// i。
  • If the event is a mouse event, it will actually send the event to the top-most view underneath the mouse
  • You can override -mouseDown: or -mouseDragged: or whatever to take the event (the only parameter), and you can pass it to whatever NSResponder you'd like, as long as you have a reference to it of course
  • If the object does not respond to the event, it will pass it down the responder chain
  • The responder chain is literally just like a singly-linked list. The head node would be the [NSWindow firstResponder] , and every NSResponder has a property called nextResponder

Finally, here is a screenshot of a breakpoint on a -mouseDown: event in one of our objects

在此处输入图片说明

Notice we're in the main thread, within the run loop, our application takes the event first, passes it to the window, the window determines the first responder ( CanvasMaskView Because this is a mouse click, this is the top-most view under the mouse), and we actually manually pass the event to the responder chain 在此处输入图片说明

This then enumerates over the responder chain until we finally find an object that handles the -mouseDown: , in ImageController at the top there

Last thing, notice in the call stack all the forwardMethods ? that is the event being passed down the nextResponder 's nextResponder 's nextResponder etc.

This can be proven by checking out the $r13 register at each stack frame, which contains the current receiver of the event. Note in this screenshot that I clicked on a stack frame, and used po $r13 in lldb, then clicked on the next stack frame, and did the same: 在此处输入图片说明

This is important because you could potentially have a responder in the chain you're unaware of, that consumes the event and doesn't pass it along and you'll have no idea unless you investigate the chain. And by "pass it along", I mean that you need to call [super mouseDown] , which will automatically pass the event if it needs to.

Oh yeah and final note! If you are overriding -mouseDown I have found that you MUST call super mouseDown . Otherwise the -mouseUp: event will disappear. That's anecdotal though, I'm pretty sure [super mouseDown] probably registers the view with the OS so it knows where to send the -mouseUp: speculation though

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM