How to implement shortcut key input in Mac Cocoa App?
Asked Answered
P

3

13

I've needed to make a global hot key input box in my Cocoa App.

I know about Shortcut Recorder, but it is a very old solution. It has parts implemented using Carbon, which has been deprecated, and I can't publish my app to the Mac App Store if I use it.

Is there any ready-to-use modern solution? Can anybody give me the way to make this by myself (I don't know where to start from)?

Potentiate answered 20/11, 2011 at 11:59 Comment(1)
Not all of Carbon is deprecated, just the GUI parts.Monro
L
16

There is a modern framework named MASShortcut for implementing Global Shortcuts in OS X 10.7+.

Liger answered 12/8, 2012 at 10:7 Comment(2)
(that he built) <- not that there's anything wrong with that, but should be transparentTungstate
Does this work only for shortcuts that don't exist already in other apps? I have problems registering shortcuts, one of them i saw implemented in another app. Also if i change the default shortcut it does not work and the key that is not the modifier key is not working in any app while mine is running. I want to catch cmd+c and alt+cmd+vChaco
M
16

Not all of Carbon is deprecated. You can't make a pure-Carbon application anymore, but some APIs live on and some of them are still the easiest way to do certain things.

One of these is the Carbon Events hotkey API. You certainly can sift through all the events using NSEvent's event-monitor methods, but it's unnecessary work. The Carbon Events hotkey API is still supported and much simpler—you just tell it what key you want to match and what function to call when the key is pressed. And there are Cocoa wrappers such as DDHotKey that make it even simpler.

  • RegisterEventHotKey, the relevant Carbon Events function (see also UnregisterEventHotKey in the same doc)
  • KeyboardShortcuts, written in Swift and includes a SwiftUI hotkey recorder view [added to this answer in edit by the project's author].
Mimimimic answered 20/11, 2011 at 20:11 Comment(3)
Thanks, Peter! But I know how register hotkeys. It's simple for me. I want to record keys from the user, so I've need to implement something like "hot key NSTextField" in my preferences window.Potentiate
@VictorShcherbakov: ShortcutRecorder is still the best way to do that that I know of. It should be 64-bit compatible, at least in the trunk version. code.google.com/p/shortcutrecorderMimimimic
shortcutrecorder (the demo application) has not been working for me on lion + xcode4. so i would still be happy to hear of other input field options other than shortcutrecorderAnalysand
L
16

There is a modern framework named MASShortcut for implementing Global Shortcuts in OS X 10.7+.

Liger answered 12/8, 2012 at 10:7 Comment(2)
(that he built) <- not that there's anything wrong with that, but should be transparentTungstate
Does this work only for shortcuts that don't exist already in other apps? I have problems registering shortcuts, one of them i saw implemented in another app. Also if i change the default shortcut it does not work and the key that is not the modifier key is not working in any app while mine is running. I want to catch cmd+c and alt+cmd+vChaco
D
13

In Mac OS X 10.6 and higher, you can use the methods +addGlobalMonitorForEventsMatchingMask:handler: and +addLocalMonitorForEventsMatchingMask:handler: defined from the NSEvent class. Monitoring Events reports the following information:

Local and global event monitors are mutually exclusive. For example, the global monitor does not observe the event stream of the application in which it is installed. The local event monitor only observes the event stream of its application. To monitor events from all applications, including the "current" application, you must install both event monitors.

The code shown in that page is for a local event monitor, but the code for a global event monitor is similar; what changes is the invoked NSEvent's method.

_eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:
        (NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask | NSKeyDownMask)
        handler:^(NSEvent *incomingEvent) {
    NSEvent *result = incomingEvent;
    NSWindow *targetWindowForEvent = [incomingEvent window];
    if (targetWindowForEvent != _window) {
        [self _closeAndSendAction:NO];
    } else if ([incomingEvent type] == NSKeyDown) {
        if ([incomingEvent keyCode] == 53) {
            // Escape
            [self _closeAndSendAction:NO];
            result = nil; // Don't process the event
        } else if ([incomingEvent keyCode] == 36) {
            // Enter
            [self _closeAndSendAction:YES];
            result = nil;
        }
    }
    return result;
}];

Once the monitor is not anymore necessary, you remove it using the following code:

[NSEvent removeMonitor:_eventMonitor];
Diffusive answered 20/11, 2011 at 13:23 Comment(5)
While this will work, it is the hard way. Carbon Event Manager's API for this is still supported and is simpler and easier, and the two third-party Cocoa wrappers for it make it even easier.Mimimimic
This doesn't work for key events unless the app is trusted for accessibilityHumblebee
global monitor doesn't work, locally I've got it to listen for key down though. But I would assume that people looking for a global hot key :).Impassable
Another problem to note with this is that some key commands using this method will still trigger the "Funk" error sound. Does anyone know of a way to stop this?Wace
@kiamlaluno Thank you for your clear answer. It works.Craftsman

© 2022 - 2024 — McMap. All rights reserved.