Search code examples
objective-cmacoscocoansviewnsevent

Dragging Events Not Receiving If mouseEntered() Events Is Tracking by Cocoa App


Working on a Cocoa App where the app needs to do two things in case - :

1) mouse entered or exited

2) mouse dragged files to the app

I have view A, view A is registered for receiving dragging events and has tracking area for receiving mouse entered and exited events,

Issue is: If I drag files to the view A; view A receives the mouseEntered: event instead of draggingEntered: event.

- (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
{
   NSLog(@"draggingEntered called");
}


- (void)mouseEntered:(NSEvent *)theEvent
{
   NSLog(@"mouseEntered called");
}

2018-02-05 14:08:58.273 XXX_APP [3420:119122] mouseEntered called

2018-02-05 14:08:58.273 XXX_APP [3420:119122] draggingEntered called

Issue is whenever I drag files to the view; It goes into the mouseEntered() not into the mouseDragged(). Is there any workaround for this?

Thanks in advance.


Solution

  • I may be wrong but I believe a drag operation is dependent on the mouse entering the view so if you're tracking mouse events, the mouseEntered: method will always be called first. That said, you can delay doing something with the mouseEntered: event giving the view time to see if it is followed by a drag operation and, if so, cancel the mouseEntered: call. This can be done using performSelector:withObject:afterDelay: (using a very brief delay that is probably not noticeable to the user) and cancelPreviousPerformRequestsWithTarget: like so:

    - (void)updateTrackingAreas {
        [super updateTrackingAreas];
        for (NSTrackingArea *trackingArea in self.trackingAreas) {
            [self removeTrackingArea:trackingArea];
        }
        [self addTrackingArea:[[NSTrackingArea alloc] initWithRect:self.bounds options:(NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways) owner:self userInfo:nil]];
    }
    
    - (void)_mouseEntered:(NSEvent *)event {
        NSLog(@"mouseEntered");
        //do something...
    }
    
    - (void)mouseEntered:(NSEvent *)event {
        //pause 0.05 seconds before acting on this event,
        //giving the view time to see if a draggingEntered: event is triggered
        [self performSelector:@selector(_mouseEntered:) withObject:event afterDelay:0.05];
    }
    
    - (void)mouseExited:(NSEvent *)event {
        NSLog(@"mouseExited");
        //do something...
    }
    
    //assumes you've registered for drag events (e.g., in init):
    //  [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
    - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
        //cancel pending _mouseEntered: call:
        [NSObject cancelPreviousPerformRequestsWithTarget:self]; 
        NSLog(@"draggingEntered");
        NSPasteboard *pasteboard = [sender draggingPasteboard];
        return (([pasteboard.types containsObject:NSFilenamesPboardType]) ? NSDragOperationLink : NSDragOperationNone);
    }
    
    - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
        NSPasteboard *pasteboard = [sender draggingPasteboard];
        if ([pasteboard.types containsObject:NSFilenamesPboardType]) {
            for (NSString *filePath in [pasteboard propertyListForType:NSFilenamesPboardType]) {
                NSLog(@"dropped file: %@", filePath);
            }
        }
        return YES;
    }