0
votes

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.

1
I'm confused, your console log shows both messages being displayed? Are you calling [super mouseEntered:theEvent]? If not, the event is going to get swallowed up and could be preventing the draggingEntered: method from being called.theeagle
@theeagle thing is when I drag files then it should only print draggingEntered called not mouseEntered because in this case another operation gets performed of mouseEnteredVikram Sinha
Oh ok, gotcha. You might want to look into NSTrackingEnabledDuringMouseDrag. From the docs, it sounds like you can make the distinction between the two events with this flag. Hope that helps!theeagle
Your title is misleading; the drag operations are being received, just not in the order you'd prefer with mouse tracking also enabled.Jon
@vikramsingh Did the answer below work for you?Jon

1 Answers

0
votes

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;
}