我有一个显示菜单栏状态项的LSUIElement
应用程序。应用程序可以显示包含文本字段的对话框窗口。
如果用户右键单击/按住Control键并单击文本字段,则会出现一个菜单,允许剪切,复制,粘贴等。但是,标准的Command-X,Command-C和Command-V键盘快捷键不会在该领域工作。我认为这是因为我的应用程序没有提供定义了这些快捷方式的编辑菜单。
我已尝试将“编辑”菜单项添加到我的应用程序菜单中,如Ship Some Code博客中所述,但这不起作用。可以使用“编辑”菜单中的菜单项,但键盘快捷键仍无效。
我可以想象一下破解键盘处理的几种方法,但是有一种“推荐”方法可以让它工作吗?
(有关该应用的详细信息,请参阅Menubar Countdown。)
答案 0 :(得分:42)
改进CocoaRocket解决方案:
以下保存必须继承NSTextField并记住在整个应用程序中使用子类;它还可以为处理它们的其他响应者启用复制,粘贴和朋友,例如。 NSTextView。
将它放在NSApplication的子类中,并相应地更改Info.plist中的主要类。
- (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 {}
答案 1 :(得分:28)
对我有用的是在CocoaRocket上使用Copy and Paste Keyboard Shortcuts中提供的 The View Solution 。
基本上,这意味着继承NSTextField并覆盖performKeyEquivalent:
。
更新: CocoaRocket网站显然已经消失了。这是互联网档案链接:http://web.archive.org/web/20100126000339/http://www.cocoarocket.com/articles/copypaste.html
编辑:Swift代码如下所示
class Editing: NSTextField {
private let commandKey = NSEventModifierFlags.CommandKeyMask.rawValue
private let commandShiftKey = NSEventModifierFlags.CommandKeyMask.rawValue | NSEventModifierFlags.ShiftKeyMask.rawValue
override func performKeyEquivalent(event: NSEvent) -> Bool {
if event.type == NSEventType.KeyDown {
if (event.modifierFlags.rawValue & NSEventModifierFlags.DeviceIndependentModifierFlagsMask.rawValue) == commandKey {
switch event.charactersIgnoringModifiers! {
case "x":
if NSApp.sendAction(Selector("cut:"), to:nil, from:self) { return true }
case "c":
if NSApp.sendAction(Selector("copy:"), to:nil, from:self) { return true }
case "v":
if NSApp.sendAction(Selector("paste:"), to:nil, from:self) { return true }
case "z":
if NSApp.sendAction(Selector("undo:"), to:nil, from:self) { return true }
case "a":
if NSApp.sendAction(Selector("selectAll:"), to:nil, from:self) { return true }
default:
break
}
}
else if (event.modifierFlags.rawValue & NSEventModifierFlags.DeviceIndependentModifierFlagsMask.rawValue) == commandShiftKey {
if event.charactersIgnoringModifiers == "Z" {
if NSApp.sendAction(Selector("redo:"), to:nil, from:self) { return true }
}
}
}
return super.performKeyEquivalent(event)
}
}
编辑:Swift 3代码如下所示
class Editing: NSTextView {
private let commandKey = NSEventModifierFlags.command.rawValue
private let commandShiftKey = NSEventModifierFlags.command.rawValue | NSEventModifierFlags.shift.rawValue
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if event.type == NSEventType.keyDown {
if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
switch event.charactersIgnoringModifiers! {
case "x":
if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
case "c":
if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
case "v":
if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
case "z":
if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
case "a":
if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
default:
break
}
}
else if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
if event.charactersIgnoringModifiers == "Z" {
if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
}
}
}
return super.performKeyEquivalent(with: event)
}
}
答案 2 :(得分:13)
我遇到了和你一样的问题,我想我已经找到了一个更简单的解决方案。您只需要将原始主菜单保留在MainMenu.xib中 - 它将不会显示,但所有操作都将得到正确处理。诀窍是它需要是原始的,如果你只是从库中拖动一个新的NSMenu,应用程序将不会将其识别为主菜单,我不知道如何标记它(如果你取消选中LSUIElement,你会发现它不会出现在顶部,如果它不是原始的那个)。如果您已将其删除,则可以创建一个新的示例应用并从其NIB拖动菜单,这也适用。
答案 3 :(得分:9)
当Caps Lock开启时,我已经改进了Adrian的解决方案:
- (void)sendEvent:(NSEvent *)event
{
if (event.type == NSKeyDown)
{
NSString *inputKey = [event.charactersIgnoringModifiers lowercaseString];
if ((event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask ||
(event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSAlphaShiftKeyMask))
{
if ([inputKey isEqualToString:@"x"])
{
if ([self sendAction:@selector(cut:) to:nil from:self])
return;
}
else if ([inputKey isEqualToString:@"c"])
{
if ([self sendAction:@selector(copy:) to:nil from:self])
return;
}
else if ([inputKey isEqualToString:@"v"])
{
if ([self sendAction:@selector(paste:) to:nil from:self])
return;
}
else if ([inputKey isEqualToString:@"z"])
{
if ([self sendAction:NSSelectorFromString(@"undo:") to:nil from:self])
return;
}
else if ([inputKey isEqualToString:@"a"])
{
if ([self sendAction:@selector(selectAll:) to:nil from:self])
return;
}
}
else if ((event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask) ||
(event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask | NSAlphaShiftKeyMask))
{
if ([inputKey isEqualToString:@"z"])
{
if ([self sendAction:NSSelectorFromString(@"redo:") to:nil from:self])
return;
}
}
}
[super sendEvent:event];
}
答案 4 :(得分:7)
Thomas Kilian在swift 3中的解决方案。
> main_scr
Local minimum possible.
fminunc stopped because the final change in function value relative to
its initial value is less than the selected value of the function tolerance.
<stopping criteria details>
答案 5 :(得分:4)
根据@Adrian,Travis B和Thomas Kilian的优秀答案,这里是swift的快速分步指南。
目标是子类化NSApplication,而不是NSTextField。创建此课程后,请将其链接到&#34;主要课程&#34;如Adrian所述,设置Info.plist。与Objective-C人员相反,我们的swiftlers必须在principalClass配置中添加额外的前缀。所以,因为我的项目名为&#34; Foo&#34;,我将设置&#34; Principal Class&#34; to&#34; Foo.MyApplication&#34;。您将找到未找到的&#34;类MyApplication&#34;否则运行时错误。
MyApplication的内容如下所示(复制并改编自目前为止给出的所有答案)
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)
}
}
答案 6 :(得分:4)
Swift 4.2 for Thomas Kilian解决方案
class MTextField: NSSecureTextField {
private let commandKey = NSEvent.ModifierFlags.command.rawValue
private let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if event.type == NSEvent.EventType.keyDown {
if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
switch event.charactersIgnoringModifiers! {
case "x":
if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
case "c":
if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
case "v":
if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
case "z":
if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
case "a":
if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
default:
break
}
}
else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
if event.charactersIgnoringModifiers == "Z" {
if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
}
}
}
return super.performKeyEquivalent(with: event)
}
}
答案 7 :(得分:3)
我在XCode 8 / Swift 3中解释了什么对我有用。
我在项目文件夹MyApplication.swift
中创建了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)
}
}
然后将Info.plist
Principal class
更改为MyApp.MyApplication
。构建并运行以验证我的文本字段和文本视图是否支持Cmd + X
,Cmd + C
,Cmd + V
和Cmd + A
。
答案 8 :(得分:2)
无需添加新的类,扩展名或任何代码。
这里的奖励是项目不会对用户隐藏,创建新课程并将所有现有TextField重新分配给它的时间更短。
答案 9 :(得分:1)
Xcode10 / Swift 4.2解决方案:
import Cocoa
extension NSTextView {
override open func performKeyEquivalent(with event: NSEvent) -> Bool {
let commandKey = NSEvent.ModifierFlags.command.rawValue
let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue
if event.type == NSEvent.EventType.keyDown {
if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
switch event.charactersIgnoringModifiers! {
case "x":
if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
case "c":
if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
case "v":
if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
case "z":
if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
case "a":
if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
default:
break
}
} else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
if event.charactersIgnoringModifiers == "Z" {
if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
}
}
}
return super.performKeyEquivalent(with: event)
}
}
答案 10 :(得分:0)
大约1个小时前,我偶然发现了同样的问题。 你不需要编码任何东西。我可以在Interface Builder中执行此操作:
FirstResponder
的{{1}},cut:
和copy:
功能链接到相应的菜单项这对我有用。 不幸的是,当您隐藏&#34;编辑&#34;时,这种(默认)行为似乎不起作用。菜单(刚尝试过)。
答案 11 :(得分:0)
这是特拉维斯作为C#与Xamarin.Mac一起使用的答案:
public override bool PerformKeyEquivalent (AppKit.NSEvent e)
{
if (e.Type == NSEventType.KeyDown) {
var inputKey = e.CharactersIgnoringModifiers.ToLower ();
if ( (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == NSEventModifierMask.CommandKeyMask
|| (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == (NSEventModifierMask.CommandKeyMask | NSEventModifierMask.AlphaShiftKeyMask)) {
switch (inputKey) {
case "x":
NSApplication.SharedApplication.SendAction (new Selector ("cut:"), null, this);
return true;
case "c":
NSApplication.SharedApplication.SendAction (new Selector ("copy:"), null, this);
return true;
case "v":
NSApplication.SharedApplication.SendAction (new Selector ("paste:"), null, this);
return true;
case "z":
NSApplication.SharedApplication.SendAction (new Selector ("undo:"), null, this);
return true;
case "a":
NSApplication.SharedApplication.SendAction (new Selector ("selectAll:"), null, this);
return true;
}
} else if ( (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == (NSEventModifierMask.CommandKeyMask | NSEventModifierMask.ShiftKeyMask)
|| (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == (NSEventModifierMask.CommandKeyMask | NSEventModifierMask.ShiftKeyMask | NSEventModifierMask.AlphaShiftKeyMask)) {
switch (inputKey) {
case "z":
NSApplication.SharedApplication.SendAction (new Selector ("redo:"), null, this);
return true;
}
}
}
return base.PerformKeyEquivalent(e);
}
答案 12 :(得分:0)
基于Thomas Kilian
的回答,您实际上可以为NSTextField创建扩展
let commandKey = NSEvent.ModifierFlags.command.rawValue
let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue
extension NSTextField {
func performEditingKeyEquivalent(with event: NSEvent) -> Bool {
guard event.type == NSEvent.EventType.keyDown else { return false }
if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
if let character = event.charactersIgnoringModifiers {
switch character {
case "x":
if NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self) { return true }
case "c":
if NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self) { return true }
case "v":
if NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self) { return true }
case "z":
if NSApp.sendAction(Selector(("undo:")), to: nil, from: self) { return true }
case "a":
if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self) { return true }
default:
break
}
}
} else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
if event.charactersIgnoringModifiers == "Z" {
if NSApp.sendAction(Selector(("redo:")), to: nil, from: self) { return true }
}
}
return false
}
}
在下面的示例中,实际上可以将NSTextField
替换为任何NSTextField
个继承类(例如NSSearchField
,NSSecureTextField
)以获得新功能
class SearchField: NSTextField {
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if performEditingKeyEquivalent(with: event) {
return true
}
return super.performEditingKeyEquivalent(with: event)
}
}
答案 13 :(得分:0)
谢谢您的解决方案!它极大地帮助了我,所以我决定贡献一些代码,希望对其他人有所帮助。在将其转换为Swift 4.2
之后,上述建议的解决方案可以完美地工作。然后,我对代码进行了一些重构。我认为这有点清洁。这是Swift 4.2
兼容的:
// NSEventExtensions.swift
import AppKit
extension NSEvent {
func containsKeyModifierFlags(_ flags: NSEvent.ModifierFlags) -> Bool {
switch modifierFlags.intersection(.deviceIndependentFlagsMask) {
case [flags]: return true
default: return false
}
}
}
// SearchFiled.swift
import AppKit
import Carbon
final class SearchField: NSSearchField {
override func performKeyEquivalent(with event: NSEvent) -> Bool {
switch event.type {
case .keyDown: return performKeyDownEquivalent(with: event)
default: return super.performKeyEquivalent(with: event)
}
}
// MARK: - private
private func performKeyDownEquivalent(with event: NSEvent) -> Bool {
if event.containsKeyModifierFlags(.command) {
switch Int(event.keyCode) {
case kVK_ANSI_X: return NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self)
case kVK_ANSI_C: return NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self)
case kVK_ANSI_V: return NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self)
case kVK_ANSI_Z: return NSApp.sendAction(Selector(("undo:")), to: nil, from: self)
case kVK_ANSI_A: return NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self)
default: break
}
} else if event.containsKeyModifierFlags([.command, .shift]) {
switch Int(event.keyCode) {
case kVK_ANSI_Z: return NSApp.sendAction(Selector(("redo:")), to: nil, from: self)
default: break
}
}
return false
}
}
答案 14 :(得分:0)
NSApplication
子类的Swift 5 解决方案
open override func sendEvent(_ event: NSEvent) {
if event.type == .keyDown {
if event.modifierFlags.contains(.command) && NSEvent.ModifierFlags.deviceIndependentFlagsMask.contains(.command) {
if event.modifierFlags.contains(.shift) && NSEvent.ModifierFlags.deviceIndependentFlagsMask.contains(.shift) {
if event.charactersIgnoringModifiers == "Z" {
if NSApp.sendAction(Selector("redo:"), to:nil, from:self) { return }
}
}
guard let key = event.charactersIgnoringModifiers else { return super.sendEvent(event) }
switch key {
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
}
}
}
super.sendEvent(event)
}
答案 15 :(得分:-1)
Adrian的解决方案很好,但我认为使用switch语句而不是所有字符串比较更好,例如:
uint const modifierCode = (theEvent.modifierFlags & NSDeviceIndependentModifierFlagsMask);
BOOL usingModifiers = ( modifierCode != 0 );
//BOOL const usingShiftKey = ((theEvent.modifierFlags & NSShiftKeyMask) != 0);
//BOOL const usingCommandKey = ((theEvent.modifierFlags & NSCommandKeyMask) != 0);
NSString * ch = [theEvent charactersIgnoringModifiers];
if ( ( usingModifiers ) && ( ch.length == 1 ) ) switch ( [ch characterAtIndex:0] )
{
case 'x':
if ( modifierCode == NSCommandKeyMask ) [m cut]; // <-- m = model
break;
case 'c':
if ( modifierCode == NSCommandKeyMask ) [m copy];
break;
case 'v':
if ( modifierCode == NSCommandKeyMask ) [m paste];
break;
case 'z':
if ( modifierCode == NSCommandKeyMask ) [m undo];
break;
case 'Z':
if ( modifierCode == ( NSCommandKeyMask | NSShiftKeyMask ) ) [m redo];
break;
default: // etc.
break;
}
else switch ( theEvent.keyCode ) // <-- for independent keycodes!
{
case kVK_Home:
[m moveToBeginningOfDocument:nil];
break;
case kVK_End: // etc!