Cocoa 不带编辑菜单的对话框中的快捷键
我有一个显示菜单栏状态项的Cocoa 不带编辑菜单的对话框中的快捷键,cocoa,macos,nsstatusitem,Cocoa,Macos,Nsstatusitem,我有一个显示菜单栏状态项的LSUIElement应用程序。应用程序可以显示包含文本字段的对话框窗口 如果用户右键单击/控件单击文本字段,则会出现一个菜单,允许剪切、复制、粘贴等操作。但是,标准的Command-X、Command-C和Command-V键盘快捷键在该字段中不起作用。我认为这是因为我的应用程序没有提供定义了这些快捷方式的编辑菜单 我已经尝试过向我的应用程序的菜单中添加一个编辑菜单项,正如在博客中所建议的那样,但是没有成功。可以使用“编辑”菜单中的菜单项,但键盘快捷键仍然不起作用 我
LSUIElement
应用程序。应用程序可以显示包含文本字段的对话框窗口
如果用户右键单击/控件单击文本字段,则会出现一个菜单,允许剪切、复制、粘贴等操作。但是,标准的Command-X、Command-C和Command-V键盘快捷键在该字段中不起作用。我认为这是因为我的应用程序没有提供定义了这些快捷方式的编辑菜单
我已经尝试过向我的应用程序的菜单中添加一个编辑菜单项,正如在博客中所建议的那样,但是没有成功。可以使用“编辑”菜单中的菜单项,但键盘快捷键仍然不起作用
我可以想象一些破解键盘操作的方法,但是有没有一种“推荐”的方法可以让它工作呢
(有关该应用程序的详细信息,请参阅。)
相关问题:对我有效的方法是使用CocoaRocket中提供的视图解决方案 基本上,这意味着子类化NSTextField并重写
performkeyeequivalent:
更新:CocoaRocket网站显然不见了。以下是Internet存档链接:
编辑: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)
}
}
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)
}
}
编辑:Swift 3代码如下所示
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)
}
}
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)
}
}
改进CocoaRocket解决方案:
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)
}
}
下面省去了在整个应用程序中使用子类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 {}
我和你有同样的问题,我想我已经找到了一个更简单的解决办法。您只需将原始主菜单保留在MainMenu.xib中-它不会显示,但所有操作都将得到正确处理。诀窍是它必须是原始的,如果你只是从库中拖动一个新的NSMenu,应用程序将无法识别它是主菜单,我也不知道如何将其标记为主菜单(如果你取消选中LSUIElement,你会看到它不会显示在顶部,如果它不是原始的)。如果您已经删除了它,您可以创建一个新的示例应用程序,并从其NIB中拖动一个菜单,这也很有效。我改进了Adrian的解决方案,使其在Caps Lock打开时也能工作:
- (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];
}
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!
uint const modifierCode=(theEvent.modifierFlags&nsDeviceIndependent ModifierFlagsMask);
BOOL usingModifiers=(modifierCode!=0);
//BOOL const usingShiftKey=((theEvent.modifierFlags&NSShiftKeyMask)!=0);
//BOOL const usingCommandKey=((theEvent.modifierFlags&NSCommandKeyMask)!=0);
NSString*ch=[事件字符签名修饰符];
if((使用修饰符)和(&(ch.length==1))开关([ch characterAtIndex:0])
{
案例“x”:
如果(modifierCode==NSCommandKeyMask)[m cut];//就在大约1小时前,我偶然发现了同样的问题。
您不需要编写任何代码。我可以在Interface Builder中执行此操作:
- 创建包含剪切/复制/粘贴菜单项的菜单(例如“编辑”)
- 将CMD键的等效键添加到“编辑”菜单(不知道,如果真的需要,我只是从另一个项目复制了该结构)
- 向这些菜单项添加等价键(CMD+X等)
- 将第一响应者的
剪切:
、复制:
和粘贴:
功能链接到相应的菜单项
这对我很管用。
不幸的是,当你隐藏“编辑”菜单时(只是尝试了一下),这种(默认)行为似乎不起作用。这里有一个关于swift的快速分步指南,它基于@Adrian、Travis B和Thomas Kilian的优秀答案
目标是将NSApplication子类化,而不是NSTextField。创建此类后,请将其链接到“主体类”中设置Info.plist,正如Adrian所说。与Objective-C的人不同,我们速战员将不得不在principalClass配置中添加一个额外的前缀。因此,由于我的项目名为“Foo”,我将把“Principal Class”设置为“Foo.MyApplication”。您将得到一个“Class MyApplication not found”否则运行时错误
我的申请书内容如下(抄袭并改编自迄今为止给出的所有答案)
下面是Travis的答案,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);
}
我解释了在XCode 8/Swift 3中对我有用的东西
我在我的项目文件夹中创建了MyApplication.swift
:
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
主体类
更改为MyApp.MyApplication
.Build,并运行以验证我的文本字段和文本视图是否支持swift 3中的Cmd+X
、Cmd+C
、Cmd+V
和Cmd+A
托马斯·基利安解决方案
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)
}
根本不需要添加新的类、扩展或任何代码
只需在其中一个菜单中添加一个新菜单项,并将其命名为“复制”、“剪切”和“粘贴”
为每个项目添加正确的快捷键
Control+拖动以将它们连接到first responder下列出的相应方法
这里的好处是,这些项目不会对用户隐藏,这比创建一个新类并将所有现有文本字段重新分配给它所需的时间要少。根据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
}
}
在下面的示例中,实际上可以使用继承类(例如NSSearchField
,NSSecureTextField
)中的任何一个来替换NSTextField
)以获得新功能
class SearchField: NSTextField {
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if performEditingKeyEquivalent(with: event) {
return true
}
return super.performEditingKeyEquivalent(with: event)
}
}
针对Thomas Kilian解决方案的Swift 4.2
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)
}
}
<