7

I'm trying to handle a paste operation on a NSTextField.

I found a similar article but for NSTextView. I tried code similar to this by overriding NSTextField and putting:

- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard
{
    return [super readSelectionFromPasteboard: pboard];
}

But this method seems to never be called.

Any suggestions on how to detect a past on NSTextField?

Kyle
  • 17,317
  • 32
  • 140
  • 246

6 Answers6

3

You could use the NSTextFieldDelegate delegate method - (BOOL) control:(NSControl*) control textView:(NSTextView*) textView doCommandBySelector:(SEL) commandSelector and watch for the paste: selector.

Mark Alldritt
  • 597
  • 1
  • 4
  • 9
2
  1. Override the becomeFirstResponder method of your NSTextField

  2. Use object_setClass to override the class of the "field editor" (which is the NSTextView that handles text input for all NSTextField instances; see here)

#import <AppKit/AppKit.h>
#import <objc/runtime.h>

@interface MyTextField : NSTextField
@end

@implementation MyTextField

- (BOOL)becomeFirstResponder
{
  if ([super becomeFirstResponder]) {
    object_setClass(self.currentEditor, MyFieldEditor.class);
    return YES;
  }
  return NO;
}

@end
  1. Create your MyFieldEditor class and override its paste: method
@interface MyFieldEditor : NSTextView
@end

@implementation MyFieldEditor

- (void)paste:(id)sender
{
  // Get the pasted text.
  NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
  NSString *text = [pasteboard stringForType:NSPasteboardTypeString];
  NSLog(@"Pasted: %@", text);

  // Set the pasted text. (optional)  
  [pasteboard clearContents];
  [pasteboard setString:@"Hello world" forType:NSPasteboardTypeString];

  // Perform the paste action. (optional)
  [super paste:sender];
}

@end

All done! Now you can intercept every paste action.

aleclarson
  • 18,087
  • 14
  • 64
  • 91
2

Combining 2 answers found here I was able to find a workaround for me, but it was necessary to subclass NSTextField, NSTextFieldCell and NSTextView.

This solution consists of 3 steps and it is using Swift, but could be adapted to Objective-C.

Swift 5

1.

Subclass NSTextView, this is where the actual paste is intercepted so the content can be replaced.

final class TextView: NSTextView {
    override func paste(_ sender: Any?) {
        var content = NSPasteboard.general.string(forType: .string) ?? ""

        // modify content

        NSPasteboard.general.clearContents()
        NSPasteboard.general.setString(content, forType: .string)
        super.paste(sender)
    }
}

2.

Subclass NSTextFieldCell and override fieldEditorFor with your own NSTextView. Important to set to true the property isFieldEditor of the NSTextView.

final class TextFieldCell: NSTextFieldCell {
    private let textView = TextView()
    
    required init(coder: NSCoder) {
        super.init(coder: coder)
    }

    override init(textCell: String) {
        super.init(textCell: textCell)
        textView.isFieldEditor = true
    }
    
    override func fieldEditor(for: NSView) -> NSTextView? {
        textView
    }
}

3.

Subclass NSTextField and assign to the static property cellClass your own NSTextFieldCell. This last step could be avoided if you simply assign your own NSTextFieldCell to all NSTextField, i.e. NSTextField.cellClass = yourCellClass.

But to avoid unforeseen side effects it is better to subclass it.

– Update

I added init(frame frameRect: NSRect), you can use both inits in your case or just the one you use to initialise your NSTextField subclass.

final class TextField: NSTextField {
    required init?(coder: NSCoder) {
        nil
    }

    init() {
        TextField.cellClass = TextFieldCell.self
        super.init(frame: .zero)
    }

    override init(frame frameRect: NSRect) {
        TextField.cellClass = TextFieldCell.self
        super.init(frame: frameRect)
    }
}

vicegax
  • 4,709
  • 28
  • 37
  • Can you please tell me how this works and where can I place this code – MacDeveloper Jul 25 '22 at 16:30
  • @MacDeveloper I was under the impression my answer was detailed enough. Could you be more specific on what part of this answer are you having trouble understanding? – vicegax Jul 25 '22 at 17:06
  • How can I link my NSTextfield subview I added to my NSTableViewCell to this TextField subclass. I might be asking a dumb question but I couldn't understand how it links my textfield to this – MacDeveloper Jul 26 '22 at 08:17
  • 1
    @MacDeveloper thank you for clarifying your question, not dumb question at all, maybe I could improve my answer to make it clearer. But answering your specific question: your need to subclass that `NSTextField` you added to the `NSTableViewCell`. In my example I subclassed it and named it `TextField`. Use a name of your choice, but it is important to subclass it so that you can override it's cell class, i.e.: `MyNSTextField.cellClass = MyNSTextFieldCell.self`. – vicegax Jul 26 '22 at 08:29
  • I made below change to my cell class. class MyTableCellView: NSTableCellView { var aTextField: TextField? override init(frame frameRect: NSRect) { super.init(frame: frameRect) aTextField = TextField(frame: frameRect) aTextField?.drawsBackground = false aTextField?.isBordered = false self.addSubview(aTextField!) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } But still I am missing something – MacDeveloper Jul 26 '22 at 08:45
  • @MacDeveloper It seems the problem may be you are using a different `init()` for `TextField`. On my example I use `init()` but your code needs `init(frame frameRect: NSRect)`. I updated my answer addressing this issue, specially point 3, check the new `init` and try updating your code with it. – vicegax Jul 26 '22 at 09:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/246768/discussion-between-macdeveloper-and-vauxhall). – MacDeveloper Jul 26 '22 at 09:15
  • I have already updated with new init, But still its not working. I just want to check if my subclassing part in NSTableViewCell is correct. – MacDeveloper Jul 26 '22 at 09:16
  • Hi @vauxhall I have just added one MainMenu file and copy paste started working seamlessly. Will that cause any issues or its fine – MacDeveloper Aug 15 '22 at 13:46
0

Overriding NSTextFieldCell and putting.

 ///////////////////////////////////////////
 //BPastTextFieldCell.h
 //
 @interface BPastTextView : NSTextView <NSTextViewDelegate>
 @end

 @class BPastTextFieldCell ;
 @interface BPastTextFieldCell : NSTextFieldCell

 @end




  //////////////////////////////////////////
  //
  //BPastTextFieldCell.m 
  //

  #import "BPastTextFieldCell.h"

  @implementation BPastTextFieldCell

  - (NSTextView *)fieldEditorForView:(NSView *)controlView{
     BPastTextView *textView = [[BPastTextView alloc] init];
      return textView;
  }
  @end




  @implementation BPastTextView

  - (void)keyDown:(NSEvent *)theEvent {

      bool bHandled = false;
      if ([theEvent modifierFlags] & NSEventModifierFlagCommand)
      {
          NSResponder * responder = [[self window] firstResponder];
          if ((responder != nil) && [responder isKindOfClass[NSTextView class]])
          {
              NSTextView * textView = (NSTextView *)responder;
              NSRange range = [textView selectedRange];
              bool bHasSelectedTexts = (range.length > 0);
              unsigned short keyCode = [theEvent keyCode];

              if (keyCode == 6)  //command + Z
              {
                  if ([[textView undoManager] canUndo])
                  {
                      [[textView undoManager] undo];
                      bHandled = true;
                  }
              }
              else if (keyCode == 7 && bHasSelectedTexts) // command + X
              {
                  [textView cut:self];
                  bHandled = true;
              }
              else if (keyCode== 8 && bHasSelectedTexts)  // command + C
              {
                  [textView copy:self];
                  bHandled = true;
              }
              else if (keyCode == 9)   // command + V
              {
                  [textView paste:self];
                  bHandled = true;
              }
          }
      }
      if(bHandled)
          return;

      [super keyDown:theEvent];

  }

  @end
-1

A nstextfield does not have copy and paste functions. Those are only found in nstextview. the catch is that when a textfield is edited it opens up a textview called a fieldeditor during the editing.

See my answer here:

NSTextField: exposing its Copy and Paste methods

Community
  • 1
  • 1
jiminybob99
  • 857
  • 1
  • 8
  • 15
-2

Here is what i use to detect paste in UITextField:

// Set this class to be the delegate of the UITextField. Now when a user will paste a text in that textField, this delegate will be called.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

    // Here we check if the replacement text is equal to the string we are currently holding in the paste board
    if ([string isEqualToString:[UIPasteboard generalPasteboard].string]) {

        // code to execute in case user is using paste

    } else {

        // code to execute other wise
    }

    return YES;
}
carlos16196
  • 706
  • 1
  • 7
  • 10