July 9, 2012

Global Keyboard Shortcuts in Cocoa

There is a lot of menu bar apps which display a panel with user interface when you click the menu bar icon. To make our life easier, these apps usually provide a global keyboard shortcut to access that panel without using your mouse.

Other apps just run in background and wait while you press a hotkey to perform some action. Evernote, Fantastical, Clocks, LittleSnapper, Things… all of these use global shortcuts to do their work. But how to implement this feature in you own Mac app?

Problem

Unfortunately, there is no official Cocoa API like NSGlobalShortcut. Instead, you have to deal with old low-level Carbon APIs to listen for global hotkeys, look here and here to find more details.

In addition, we would need a control like NSGlobalShortcutView to enter and display keyboard shortcuts with special characters for Command, Option and Shift, something like this.

From the links I just posted, you may find that adding support for global keyboard shortcuts is a non-trivial task. You have to dive into the component architecture, copy many sources or framework into your project, and finally add many lines of code into your app.

Solution

I always wanted to have something more lightweight and easy to use, something modern, something that will have simple APIs with blocks. And here we go, I would like to share a “framework” MASShortcut. I use it in my own apps CodeBox and Hunting, all code is compatible with latest OS X 10.7, with the Mac App Store and its sandboxed environment. So I hope you will find it useful :)

Usage

To use MASShortcut in a Cocoa app, git clone the repository from GitHub, link to the Carbon.framework in Xcode and drag all Objective-C files into your project. Then insert a custom view of 19 pixels height into XIB and set its class to MASShortcutView. Next, import "MASShortcutView+UserDefaults.h" into the view controller and ask the shortcut view to read and store a hotkey in user preferences automatically. That's it for the UI part.

To listen for the global hotkey stored in user preferences, you have to import "MASShortcut+UserDefaults.h" into the application delegate, for example. And then provide a block of code to execute when the user presses that global hotkey. This is really easy as you can see in README:

MASShortcut on GitHub

I built a simple application which demonstrates MASShortcut in action, please check MASShortcutDemo on GitHub and let me know if you have any questions or suggestions, — @vadimshpakovski in Twitter.

9 comments:

  1. Oh my god ! You finally made it ! Great, thanks.

    ReplyDelete
  2. I'm the creator of Shortcut Recorder, where by creator I mean initial implementor since most of the hard work was done by other people past the first few releases.

    Shortcut Recorder is old, busted and weird in many ways and I am very happy to see that someone else is attacking this problem now. I have tried myself a few times to reboot it, and someone's actually giving it a whirl right now, but I will be happy to defer to this if it comes to it.

    If the need arises, please steal mercilessly from Shortcut Recorder - small bits, large bits, details, what have you. I am not interested in ego or getting there first or raising eyebrows because fundamental algorithms are the same or common sense aligns across several cracks at the same problem.

    Good luck and let me know if I can help in any way.

    ReplyDelete
    Replies
    1. Jesper, you did an amazing job with Shortcut Recorder! Thank you very much for “approving” MASShortcut and encouragement. I will do my best to keep the project up-to-date and reuse your time-tested code when needed :)

      Delete
    2. That sounds good. Just be sure to remember that there's a fair amount of rust that has been time-tested as well. If you want parity, let it be feature parity so that there are still ways to do the same thing, not mechanism parity where it necessarily involves the same rough lines of code.

      Delete
  3. Looks good, thanks for the permission to use it. Plan to a lot in the coming weeks. Appreciate your work! :)

    ReplyDelete
  4. I'm a heavy user of Text Expander as well as Keyboard Maestro to automate tasks and typing, and over the past couple of years, I've gotten used to typing a combined total of likely 200+ keyboard shortcut combinations on a regular basis, shortcuts that are loosely based on rules I "made" but have since broken and changed many times. I'm wondering if anyone might have any suggestions or tools to help me decide how to "choose" a keyboard shortcut each time I automate a task.

    I guess what I'm looking for us something that would help me visually see what shortcut key combinations are "available" (although I know that statistically speaking, the number of possible combinations is a huge number).

    If you've ever used Final Cut Pro, you mayhave seen the hotkey preferences screen, the Command Editor, is color-coded diagram of possible and suggested key combinations, but I haven't been able to find something similar outside of Final Cut.

    if you have any suggestions, thanks in advance :]

    ReplyDelete
    Replies
    1. Command Editor in Final Cut Pro looks fantastic. Unfortunately, I have never seen such component for Cocoa.

      Anyway, it seems like Apple uses local key equivalents to configure commands, while MASShortcut is a solution for a few global hotkeys per app.

      Delete
  5. Great thanks for your great work, Vadim

    ReplyDelete