Question

I have several views of class MyView (subclass of NSView) inside another NSView. MyView implements -mouseEntered:, -mouseExited:, -mouseDown:, -mouseDragged:, and -mouseUp:.

Almost always, when a MyView receives a mouse-down event, all subsequent mouse-dragged events are received by the same MyView until the next mouse-up event. Even if the cursor goes outside of the MyView. That is the expected behavior.

Occasionally, a MyView will receive a mouse-down event, but will only receive mouse-dragged and mouse-up events while the cursor remains inside the MyView. If the cursor moves onto a different MyView, then that MyView starts receiving mouse-dragged events (without first receiving a mouse-down event) and can receive the subsequent mouse-up event.

In case it matters, the mouse-down event creates a FooView (subclass of NSView) on top of the MyView, and the mouse-dragged events resize the frame of the FooView. This might be related, as I've only been able to reproduce the problem after one of these FooViews has been created. FooView does not implement any of the mouse event methods.

I've been messing with this for a while now and haven't been able to either purposely reproduce the problem or recreate the problem in a simple example. I'd be happy to answer any questions about my code, I'm just not sure what would be the relevant part to post.

Was it helpful?

Solution

Not sure what the root issue is (this Cocoa behavior seems inconsistent to me)... but here's one possible workaround:

  1. In the superview, create an instance variable tracking the MyView instance in which the -mouseDown: occurred.
  2. When you receive a -mouseDragged: in MyView, instead of operating on self, operate on the MyView instance reference stored in the superview.

...then you'll be able to consistently track which object is being dragged, without having to run your own event loop.

OTHER TIPS

You need to run you own mouse tracking loop in the view until mouse is up. You can extend it to process more types of events by passing them into nextEventMatchingMask:.

- (void)mouseDown:(NSEvent*)event
{
    CGPoint hitPoint = [self pointInViewSpaceFromEvent:event];

    BOOL isDragging = NO;
    BOOL isTracking = YES;

    while (isTracking)
    {       
        switch ([event type])
        {
            case NSLeftMouseDown:
                [self singleMouseDownAtPoint:hitPoint withModifierFlags:[event modifierFlags]];
                break;

            case NSLeftMouseUp:
                isTracking = NO;
                if (isDragging)
                    [self mouseDraggingDidEndAtPoint:hitPoint];
                else
                    [self singleMouseUpAtPoint:hitPoint withEvent:event];
                break;

            case NSLeftMouseDragged:
                if (isDragging)
                    [self mouseDraggingAtPoint:hitPoint withModifierFlags:[event modifierFlags]];
                else
                    isDragging = YES;
                break;
            default:
                break;
        }

        if (isTracking)
        {
            event = [[self window] nextEventMatchingMask:NSLeftMouseDraggedMask | NSLeftMouseUpMask];
            hitPoint = [self pointInViewSpaceFromEvent:event];
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top