You know that effect when you drag out an item from the dock and that cloud drag cursor appears and when you let go it disappears with a poof effect? Similarly, in Xcode when you drag a breakpoint outside the line number gutter the same happens.
I would like to implement the same effect in my application but can't find the right way.
I have an NSImageView descendant to implement the NSDraggingSource and NSDraggingDestination protocols. I have several instances of this view which allow to drag their content between the others (a copy operation takes place in this scenario, but that's only relevant to show I have drag'n drop implmented and fully working for standard tasks).
Now, when I drag out an image from its view to anywhere (except another view instance) I want to have the delete operation taking place on drop. However the drag operation is fully controlled by the target view. I could manage to make them respond the way I want (even though this would be a lot of work), but it fails completely if I'm dragging outside my application.
If I could get the delete drag operation I could handle this however easily by:
- (void)draggedImage: (NSImage *)image
endedAt: (NSPoint)screenPoint
operation: (NSDragOperation)operation
{
if (operation == NSDragOperationDelete) {
NSRect rect = [self.window convertRectToScreen: [self convertRect: self.frame fromView: nil]];
NSShowAnimationEffect(NSAnimationEffectPoof, rect.origin, self.bounds.size, nil, nil, NULL);
}
}
I tried already to set the delete cursor like this:
- (void)draggingSession: (NSDraggingSession *)session
movedToPoint: (NSPoint)screenPoint
{
if (!NSPointInRect(screenPoint, self.window.frame)) {
[[NSCursor disappearingItemCursor] set];
}
}
(for simplicity this is for the entire windw at the moment). This works as long as I don't hit the desktop or a finder window. In starts flickering, probably because the Finder concurrently sets its own drag cursor. It is completely without effect when I hit the dock. This also happens when I define my own pasteboard data type.
Additionally, any other drop enabled view in my application will still accept my drag data (e.g. NSTextView) which I don't want to happen (I'm writing an NSURL to the dragging pasteboard with a custom scheme).
Update:
I've come a few steps further. As Peter already indicated it is essential to handle draggingSession:sourceOperationmaskForDraggingContext:
which looks so in my code:
- (NSDragOperation) draggingSession: (NSDraggingSession *)session
sourceOperationMaskForDraggingContext: (NSDraggingContext)context;
{
switch(context) {
case NSDraggingContextOutsideApplication:
return NSDragOperationDelete;
break;
case NSDraggingContextWithinApplication:
default:
return NSDragOperationDelete | NSDragOperationMove;
break;
}
}
This solves 2 problems: 1) outside the application the drag operation is not accepted at all, 2) it keeps all standard views from accepting this operation too (because NSOutlineView, NSTextView etc. don't handle the given drag operations). Additionally, I created an own pasteboard datatype, but this doesn't seem to be necessary. Still it's clearer to have an own one.
Unfortunately, dropping outside of my NSImageView descendant (both within and outside the application) does not give me NSDragOperationDelete in draggedImage:endedAt:operation:
(what I specified above) but NSDragOperationNone. Additionally the drag cursor when moving the mouse outside the application is the not-allowed one, not the disappearing-item. So, if someone could solve these two things I'd accept it as answer to my question.
draggingSession:sourceOperationMaskForDraggingContext:
method. – Peter Hosey