You could use the NSTextFieldDelegate
delegate method - (BOOL) control:(NSControl*) control textView:(NSTextView*) textView doCommandBySelector:(SEL) commandSelector
and watch for the paste:
selector.
Detect paste on NSTextField
-
04-06-2022 - |
Question
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?
Solution
OTHER TIPS
Override the
becomeFirstResponder
method of yourNSTextField
Use
object_setClass
to override the class of the "field editor" (which is theNSTextView
that handles text input for allNSTextField
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
- Create your
MyFieldEditor
class and override itspaste:
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.
Combining 2 answers found here I was able to find a workaround for me, but it was necessary to subclass NSTextField
, NSTextFieldCell
and NSTextView
.
Swift 5
1.
Subclass NSTextView
, this is where the actual paste is intercepted and can the content 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
final class TextField: NSTextField {
required init?(coder: NSCoder) {
nil
}
init() {
TextField.cellClass = TextFieldCell.self
super.init(frame: .zero)
}
}
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
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:
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;
}