Reputation: 17677
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?
Upvotes: 7
Views: 3260
Reputation: 6380
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
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)
}
}
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
}
}
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.
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)
}
}
Upvotes: 2
Reputation: 19035
Override the becomeFirstResponder
method of your NSTextField
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
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.
Upvotes: 2
Reputation: 31
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
Upvotes: 0
Reputation: 873
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
Upvotes: -1
Reputation: 706
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;
}
Upvotes: -2
Reputation: 697
You could use the NSTextFieldDelegate
delegate method - (BOOL) control:(NSControl*) control textView:(NSTextView*) textView doCommandBySelector:(SEL) commandSelector
and watch for the paste:
selector.
Upvotes: 3