teclas teclado programar programa personalizado para directo crear combinaciones combinacion atajos atajo acceso cocoa osx nsstatusitem

programar - Atajos de teclado Cocoa en el diálogo sin un menú de edición



programa para crear atajos de teclado windows 10 (11)

Tengo una aplicación LSUIElement que muestra un elemento de estado de la barra de menú. La aplicación puede mostrar una ventana de diálogo que contiene un campo de texto.

Si el usuario hace clic derecho / clic de control en el campo de texto, aparece un menú que permite cortar, copiar, pegar, etc. Sin embargo, los métodos abreviados de teclado Command-X, Command-C y Command-V no funcionan en el campo. Supongo que esto se debe a que mi aplicación no proporciona un menú Editar con los accesos directos definidos.

Intenté agregar un elemento del menú Editar al menú de mi aplicación, como se sugiere en el blog Enviar algunos códigos , pero no funcionó. Se pueden usar los elementos del menú en el menú Editar, pero los atajos de teclado todavía no funcionan.

Puedo imaginar algunas formas de hackear el manejo del teclado, pero ¿hay alguna manera "recomendada" de hacer que esto funcione?

(Para detalles sobre la aplicación, vea la cuenta regresiva de la barra de menú ).

Pregunta relacionada: copiar / pegar que no funciona en la ventana modal


Tuve el mismo problema que tú, y creo que he logrado encontrar una solución más simple. Solo tiene que abandonar el menú principal original en MainMenu.xib; no se mostrará, pero todas las acciones se manejarán correctamente. El truco es que tiene que ser el original, si solo arrastras un nuevo NSMenu de la biblioteca, la aplicación no lo reconocerá como Menú principal y no tengo idea de cómo marcarlo como tal (si desmarcas LSUIElement, verás que no aparecerá en la parte superior si no es el original). Si ya lo ha eliminado, puede crear una nueva aplicación de muestra y arrastrar un menú desde su NIB, que también funciona.


Mejorando en esa solución CocoaRocket:

A continuación, guarda la subclase NSTextField y recuerda usar la subclase en toda la aplicación; también permitirá copiar, pegar y amigos para otros respondedores que los manejan, ej. NSTextView.

Coloque esto en una subclase de NSApplication y modifique la clase principal en su Info.plist en consecuencia.

- (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]; }


He mejorado la solución de Adrian para que funcione cuando también está activado el bloqueo de mayúsculas:

- (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]; }


La solución de Adrian es buena, pero creo que mejor usar una declaración de cambio en lugar de todas las comparaciones de cadenas, por ejemplo:

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!


Hace aproximadamente 1 hora tropecé con el mismo problema. No necesitas codificar nada. Podría hacer esto en Interface Builder:

  • Cree un menú (por ejemplo, "Editar") que contenga sus elementos de menú Cortar / Copiar / Pegar
  • Agregue KeyEquivalent para la clave CMD a su menú "Editar" (no sé, si esto es realmente necesario, simplemente copié la estructura de otro proyecto)
  • Agregue los KeyEquivalents a estos elementos del menú (CMD + X, etc.)
  • Vincule el FirstResponder del FirstResponder cut: , copy: y paste: funciones a sus elementos de menú correspondientes

Eso funcionó para mí. Lamentablemente, este comportamiento (predeterminado) no parece funcionar cuando oculta el menú "Editar" (simplemente lo intenté).


Aquí hay una guía paso a paso rápida para Swift, basada en las excelentes respuestas de @Adrian, Travis B y Thomas Kilian.

El objetivo será subclasificar la aplicación NSA, en lugar de NSTextField. Una vez que haya creado esta clase, hágalo en su configuración de "clase principal" de Info.plist, tal como lo establece Adrian. A diferencia de la gente de Objective-C, los swiftlers tendremos que agregar un prefijo adicional a la configuración de la clase principal. Entonces, como mi proyecto se llama "Foo", voy a establecer "Clase principal" en "Foo.MyApplication". Obtendrá un error de tiempo de ejecución de "Class MyApplication not found".

Los contenidos de MyApplication se leen de la siguiente manera (copiados y adaptados de todas las respuestas dadas hasta el momento)

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) } }


Aquí está la respuesta de Travis como C # para usar con 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); }


Explico lo que funcionó para mí en XCode 8 / Swift 3.

MyApplication.swift dentro de la carpeta de mi proyecto 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) } }

A continuación, cambie la Principal class de MyApp.MyApplication a MyApp.MyApplication . Cree y ejecute para validar que mis campos de texto y vistas de texto tengan soporte para Cmd + X , Cmd + C , Cmd + V y Cmd + A


Solución de Thomas Kilian en Swift 3.

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) }


Lo que funcionó para mí fue utilizar The View Solution presentada en Copiar y pegar atajos de teclado en CocoaRocket.

Básicamente, esto significa la subclasificación de NSTextField y la anulación de performKeyEquivalent:

Actualización: aparentemente, el sitio CocoaRocket ya no está. Aquí está el enlace del Archivo de Internet: http://web.archive.org/web/20100126000339/http://www.cocoarocket.com/articles/copypaste.html

Editar : El código Swift se ve así

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) } }

Editar: El código de Swift 3 se ve así

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) } }


No es necesario agregar una nueva clase, extensión o realmente ningún código.

  1. Simplemente agregue un nuevo MenuItems en uno de sus menús y ''Rampa'', ''Cortar'' y ''Pegar''.
  2. Agregue las teclas de método abreviado correctas a cada elemento.
  3. Control + arrastre para conectarlos a los métodos correspondientes enumerados en la primera respuesta.

La ventaja aquí es que los elementos no están ocultos para los usuarios, y esto lleva menos tiempo que la creación de una nueva clase y la reasignación de todos tus campos de texto existentes.