I have an LSUIElement
application that displays a menubar status item. The application can display a dialog window that contains a text field.
If the u
Improving on that CocoaRocket solution:
The following saves having to subclass NSTextField and remembering to use the subclass throughout your application; it will also enable copy, paste and friends for other responders that handle them, eg. NSTextView.
Put this in a subclass of NSApplication and alter the principal class in your Info.plist accordingly.
- (void) sendEvent:(NSEvent *)event {
if ([event type] == NSKeyDown) {
if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask) {
if ([[event charactersIgnoringModifiers] isEqualToString:@"x"]) {
if ([self sendAction:@selector(cut:) to:nil from:self])
return;
}
else if ([[event charactersIgnoringModifiers] isEqualToString:@"c"]) {
if ([self sendAction:@selector(copy:) to:nil from:self])
return;
}
else if ([[event charactersIgnoringModifiers] isEqualToString:@"v"]) {
if ([self sendAction:@selector(paste:) to:nil from:self])
return;
}
else if ([[event charactersIgnoringModifiers] isEqualToString:@"z"]) {
if ([self sendAction:@selector(undo:) to:nil from:self])
return;
}
else if ([[event charactersIgnoringModifiers] isEqualToString:@"a"]) {
if ([self sendAction:@selector(selectAll:) to:nil from:self])
return;
}
}
else if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask)) {
if ([[event charactersIgnoringModifiers] isEqualToString:@"Z"]) {
if ([self sendAction:@selector(redo:) to:nil from:self])
return;
}
}
}
[super sendEvent:event];
}
// Blank Selectors to silence Xcode warnings: 'Undeclared selector undo:/redo:'
- (IBAction)undo:(id)sender {}
- (IBAction)redo:(id)sender {}
Here's a quick step-by-step guide for swift, based on the excellent answers by @Adrian, Travis B and Thomas Kilian.
The goal will be to subclass the NSApplication, instead of the NSTextField. Once you have created this class, do link it in your "principal Class" setting of the Info.plist, as stated by Adrian. As opposed to the Objective-C folks, we swiftlers will have to add an additional prefix to the principalClass configuration. So, because my Project is called "Foo", I am going to set "Principal Class" to "Foo.MyApplication". You will get an "Class MyApplication not found" runtime error otherwise.
The contents of MyApplication read as follows (copied and adapted from all the answers given so far)
import Cocoa
class MyApplication: NSApplication {
override func sendEvent(event: NSEvent) {
if event.type == NSEventType.KeyDown {
if (event.modifierFlags & NSEventModifierFlags.DeviceIndependentModifierFlagsMask == NSEventModifierFlags.CommandKeyMask) {
switch event.charactersIgnoringModifiers!.lowercaseString {
case "x":
if NSApp.sendAction(Selector("cut:"), to:nil, from:self) { return }
case "c":
if NSApp.sendAction(Selector("copy:"), to:nil, from:self) { return }
case "v":
if NSApp.sendAction(Selector("paste:"), to:nil, from:self) { return }
case "z":
if NSApp.sendAction(Selector("undo:"), to:nil, from:self) { return }
case "a":
if NSApp.sendAction(Selector("selectAll:"), to:nil, from:self) { return }
default:
break
}
}
else if (event.modifierFlags & NSEventModifierFlags.DeviceIndependentModifierFlagsMask == (NSEventModifierFlags.CommandKeyMask | NSEventModifierFlags.ShiftKeyMask)) {
if event.charactersIgnoringModifiers == "Z" {
if NSApp.sendAction(Selector("redo:"), to:nil, from:self) { return }
}
}
}
return super.sendEvent(event)
}
}
I explain what worked for me in XCode 8 / Swift 3.
I created MyApplication.swift
inside my project folder MyApp
:
import Foundation
import Cocoa
class MyApplication: NSApplication {
override func sendEvent(_ event: NSEvent) {
if event.type == NSEventType.keyDown {
if (event.modifierFlags.contains(NSEventModifierFlags.command)) {
switch event.charactersIgnoringModifiers!.lowercased() {
case "x":
if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return }
case "c":
if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return }
case "v":
if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return }
case "a":
if NSApp.sendAction(#selector(NSText.selectAll(_:)), to:nil, from:self) { return }
default:
break
}
}
}
return super.sendEvent(event)
}
}
Then change the Info.plist
Principal class
to MyApp.MyApplication
. Build, and run to validate that my text fields and text views have support for Cmd + X
, Cmd + C
, Cmd + V
and Cmd + A
.
Just about 1 hour ago I stumbled upon the same problem. You don't need to code anything. I could do this in Interface Builder:
FirstResponder
's cut:
, copy:
and paste:
functions to your corresponding menu itemsThat worked for me. Unfortunately this (default) behavior doesn't seem to work when you hide the "Edit" menu (just tried it).