user1548418
user1548418

Reputation: 497

NSTrackingArea registers mouseMove through overlapping window, when it shouldn't

I created a tracking area in NSView that should only track mouse moved events when the window is the key window. However, I noticed that sometimes, when the window is overlapped by another window which is currently key, the background window which is no longer the key window, still receives mouseMoved: events.

Here is my code in NSView subclass:

if (_trackingArea != nil) {
    [self removeTrackingArea:_trackingArea];
}
_trackingArea = [[NSTrackingArea alloc]
                 initWithRect:trackingFrame
                 options:(NSTrackingAreaOptions)(NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
                 owner:self
                 userInfo:nil];
[self addTrackingArea:_trackingArea];

I added NSLog(@"is window key %d", [[self window] isKeyWindow]) in -mouseMoved: and it clearly shows that the background window is NOT key, though it still receives the mouse moved events.

This artifact disappears after I click the background window to make it key and then click the foreground window back again to make it key. Then the background window stops receiving mouse moved events.

Is this a bug of NSTrackingArea, is there any way to work around it?

UPDATE: I noticed that this bug only appears when the background window gets programmatically resized, while the foreground window has keyboard focus.

Upvotes: 0

Views: 382

Answers (1)

Kentzo
Kentzo

Reputation: 3971

The way I interpret the documentation matches with yours and I'd expect the TA to get active only when the parent window is key. However, it's obviously is not the case and it's not a new bug as I was able to reproduce it on 10.14

Internally there are a number of private methods that control what and when gets installed:

  • -[NSView _installTrackingArea:] / -[NSView _uninstallTrackingArea:]
  • -[NSView _enableTrackingArea:] / -[NSView disableTrackingArea:]
  • -[NSWindow _addMouseMovedListener:] / -[NSWindow _removeMouseMovedListener:]

If you add symbolic breakpoints to them and log the object being passed you'll see the following after pressing the button:

-updateTrackingAreas 0x600002148c80
Uninstall TA 0x60000213f3e0
Remove MML 0x60000213f3e0
Install TA 0x600002148c80
Disable TA 0x600002148c80
Remove MML 0x600002148c80
Add MML 0x600002148c80
Uninstall TA 0x6000021447d0
Install TA 0x60000212d7c0

0x600002148c80 is the Tracking Area being added to the view. As you can see during installation it is disabled and associated mouse-moved-listener (MML) is removed to be immediately added back.

Either this is a bug or documentation is lacking.

I suggest to rewrite -[NSView updateTrackingAreas]:

  • Call [super updateTrackingAreas] because the documentation suggests it
  • Only re-add TA to reflect changes of frame, options or owner
  • Do not rely on TA's options alone and double check that conditions are right (window is key, app is active etc). In other words treat TA's options to act upon future changes in state.
  • Check (or assert) state inside the event handlers: chances are they may get called in response to something else

Upvotes: 0

Related Questions