I can get a mouseExited event sent to different NSView than the mouseEntered event
Scrolling with the scroll ball (or wheel) over a collection of subviews within a scrollview I sometimes do not get a mouseExited event sent to the view that the mouseEntered event was sent to.
I'm trying to show a button only when the mouse is over the view. When I scroll slowly I get matched mouseEntered and mouseExited events. Scrolling quickly the mouseExited is sent, but sent to a different view, so I don't hide the button as I do not know that the mouse exited
Notes: I've bound the hidden flag on a button inside the view to the controllers mouseOver property I set a tracking area to the view bounds inset by 2 pixels. on mouseEntered I set the controllers mouseOver to YES on mouseExited I set the controllers mouseOver to NO
Looking at logs after I run and see when child view is out of sync I can easily see that I get a mouseEntered and do get the mouseExited, but the mouseExited is sent to a different child (sub) view.
I have a full working sample to show this and sent to Apple, but thought I'd ask for any ideas, known issues or work-arounds or answers to my problem.
Very simple sample, just alloc and init 45 controllers and add to a NSView parent set as the documentView inside a scrolliew
Thanks
@implementation MouseOverSampleChildViewController
- (BOOL)mouseOver
{
return m_bMouseOver;
}
- (void)setMouseOver:(BOOL)bMouseOver
{
m_bMouseOver = bMouseOver;
}
- (int)child
{
return m_nChild;
}
- (void)setChild:(int)nChild
{
m_nChild = nChild;
}
@end
@implementation MouseOverSampleView
- (void)awakeFromNib
{
NSString *strLabel = [[NSString alloc] initWithFormat:@"Child View [%d]", [controller child]];
[textLabel setStringValue:strLabel];
[strLabel release];
}
- (void)updateTrackingAreas
{
[super updateTrackingAreas];
NSRect rectMouseOver = NSInsetRect([self bounds], 0, 2);
if (m_trackingMouseOverArea == nil || !NSEqualRects(rectMouseOver, m_rectMouseOver))
{
// remove old tracking area if we have one to remove
if (m_trackingMouseOverArea)
{
[self removeTrackingArea:m_trackingMouseOverArea];
[m_trackingMouseOverArea release];
m_trackingMouseOverArea = nil;
}
// Allocate and add new tracking area
m_trackingMouseOverArea = [[NSTrackingArea alloc] initWithRect:rectMouseOver options:NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp | NSTrackingAssumeInside owner:self userInfo:nil];
[self addTrackingArea:m_trackingMouseOverArea];
m_rectMouseOver = rectMouseOver;
}
}
- (void)mouseEntered:(NSEvent *)theEvent
{
NSLog(@"mouseEntered child[%d]",[controller child]);
[controller setMouseOver:YES];
[super mouseEntered:theEvent];
}
- (void)mouseExited:(NSEvent *)theEvent
{
NSLog(@"mouseExited child[%d]",[controller child]);
[controller setMouseOver:NO];
[super mouseExited:theEvent];
}
@end
@implementation MouseOverSampleWindowView
- (void)awakeFromNib
{
// Add a bunch of child views to the scroll view
int nChildren = 45;
float fChildHeight = 37;
float fChildYOffset = 0;
NSMutableArray *aChildren = [NSMutableArray arrayWithCapacity:nChildren];
for (int nChild = 0; nChild < nChildren; nChild++)
{
MouseOverSampleChildViewController *controller = [[[MouseOverSampleChildViewController alloc] initWithChild:nChild] autorelease];
NSRect childRect = NSMakeRect(0, fChildYOffset, 315, fChildHeight);
[[controller view] setFrame:childRect];
[aChildren addObject:[controller view]];
fChildYOffset += fChildHeight;
}
NSRect documentRect = NSMakeRect(0, 0, 315, ([aChildren count] * fChildHeight));
NSView *newDocumentView = [[NSView alloc] initWithFrame:documentRect];
[newDocumentView setSubviews:aChildren];
[scrollView setDocumentView:newDocumentView];
[newDocumentView release];
newDocumentView = nil;
}
@end