How to force NSTokenField to *move* tokens when dragging to another field?

193 Views Asked by At

The default drag operation when dragging a token from one NSTokenField and dropping it into another field is NSDragOperationCopy. Holding down the Command key when performing the drag changes the operation to NSDragOperationMove.

How can I reverse this default behavior so that the tokens are moved by default, and copied only when holding down the Option key?

I'm trying to imitate the behavior from the compose window in Mail where dragging an email token from the To field to the Cc field moves the token by default, and copies it when holding the Option key.

I have tried subclassing NSTokenField and overriding the draggingEntered: to return NSDragOperationMove but that does not seem to work.


Update:

I've ended up swizzling the dragOperationForDraggingInfo:type: method on NSTextView and returning NSDragOperationMove in the implementation by default. I also check if the option key is down and return NSDragOperationCopy if true. This seems to be working as expected for now, but I'm not sure if method swizzling is the best way to go here.

1

There are 1 best solutions below

0
On

Thanks to Willeke's comment, I got this to work by subclassing NSTokenField and implementing the undocumented NSTextViewDelegate method textView:dragOperationForDraggingInfo:type:.

Here's the exact code I used in my NSTokenField subclass:

class TokenField: NSTokenField { 
   
    @objc func textView(_ textView: NSTextView, dragOperationForDraggingInfo dragInfo: NSDraggingInfo, type: NSPasteboard.PasteboardType) -> NSDragOperation {

      // if option is pressed, always return .copy
      if NSApp.currentEvent?.modifierFlags.intersection(.deviceIndependentFlagsMask) == .option {
        return .copy
      }

      // if the source and destination are the same, we want default NSTokenField behavior
      if let source = dragInfo.draggingSource as? NSTextView, textView == source {
        return .generic
      }

      // default to .move
      return .move
    }

}