How to disable drag-n-drop for NSTextField?

I want to disallow dropping anything into my NSTextField. In my app, users can drag and drop iCal events into a different part of the GUI. Now I've had a test user who accidentally dropped the iCal event into the text field – but he didn't realize this because the text is inserted in the lines above the one that I see in my one-line text field.

(You can reveal the inserted text by clicking into the text field and using the keyboard to go one line up – but a normal user wouldn't do this because he/she wouldn't even have realized that something got inserted in the first place!)

I tried registerForDraggedTypes:[NSArray array]] (doesn't seem to have any effect) as well as implementing the draggingEntered: delegate method returning NSDragOperationNone (the delegate method isn't even invoked).

Any ideas?

EDIT: Of course dropping something onto an NSTextField only works when it has focus, as described by ssp in his blog and in the comments to a blog entry by Daniel Jalkut.


Solution 1:

I am glad you discovered the comments in my blog post. I think they are the tip of the iceberg to discovering how to achieve what you're looking for.

You need to keep in mind that the reason dragging to an NSTextField works when it has focus, is that the NSTextField has itself been temporarily obscured by a richer, more powerful view (an NSTextView), which is called the "Field Editor."

Check out this section of Apple's documentation on the field editor:

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextEditing/Tasks/FieldEditor.html

To achieve what you're striving for, I think you might need to intercept the standard provision of the field editor for your NSTextFields, by implementing the window delegate method:

windowWillReturnFieldEditor:toObject:

This gives you the opportunity to either tweak the configuration on the NSTextView, or provide a completely new field editor object.

In the worst case scenario, you could provide your own NSTextView subclass as the field editor, which was designed to reject all drags.

Solution 2:

This might work: If you subclass NSTextView and implement -acceptableDragTypes to return nil, then the text view will be disabled as a drag destination. I also had to implement the NSDraggingDestination methods -draggingEntered: and -draggingUpdated: to return NSDragOperationNone.

@implementation NoDragTextView
- (NSArray *)acceptableDragTypes
{
    return nil;
}
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
    return NSDragOperationNone;
}
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
{
    return NSDragOperationNone;
}
@end

Solution 3:

I was able to solve this problem by creating a custom NSTextView and implementing the enter and exit NSDraggingDestination protocol methods to set the NSTextView to hidden. Once the text field is hidden the superview will be able to catch the drag/drop events, or if the superview doesn't implement or want the drag/drop they are discarded

For example:

- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {

    //hide so that the drop event falls through into superview's drag/drop view
    [self setHidden:YES];

    return NSDragOperationNone;
}

- (void)draggingExited:(id<NSDraggingInfo>)sender {

    //show our field editor again since dragging is all over with
    [self setHidden:NO];

}