Enabling keyboard shortcuts with NSStatusItems
January 4th, 2007It all started when a friend who has been helping me out with testing and ideas for improving MythGrowl noticed that it was not possible to paste into either the ‘MythTV Server IP/Hostname’ or ‘MythTV Port’ box which are both NSTextFields. When you push APPLE+V the system would just make it’s standard ‘ding’ noise and whatever was on the clipboard wouldn’t appear, I imagined that changing some property of the NSTextFields would fix the problem right up.
As it happened I couldn’t have been more wrong and a seemingly simple problem took me an entire morning to solve. The next thing I noticed was that if you right clicked in the field and clicked paste from the menu then whatever text that was currently on the clipboard would appear in the text field just fine. Strange, copy and pasting is working fine, but the shortcuts aren’t working at all. After some more reading and fiddling around I figured out that modifier key shortcuts (Apple+whatever, Option+whatever, Ctrl+whatever) are handled a long time before they get to an NSTextField.
OK, fine, so why aren’t they being handled and why aren’t the blasted shortcuts working?
The one major thing different about my application (and any application with only an NSStatusItem) is that it has no main application menu with an ‘Application’, ‘File’ and ‘Edit’ etc. menu showing along the top. Now, the application ‘Edit’ menu is the one with the ‘Copy’ and ‘Paste’ operations and corresponding key shortcuts. If your application has no ‘Edit’ menu with the operations you need under it then, believe it or not, the keyboard shortcuts won’t work anywhere in your application.
So, I tried dragging an ‘Edit’ menu onto my MythGrowl status item menu using the interface builder and then tried running my app and copying and pasting into the boxes. And it works! But now I have a problem, I don’t want my app to have an ugly, unneeded ‘Edit’ menu, I just want to enable the keyboard shortcuts. So, I created a ‘dummy’ menu with an ‘Edit’ menu in it and added it as an outlet, now I just needed to figure out a way to tell my application that my ‘dummy’ menu will be taking over the role of my main application menu. After some more messing around I found that you can do this with [NSApp setMainMenu:yourDummyMenuName]; which I called when setting up my NSStatusItem.
I recompiled and to my absolute joy all the keyboard shortcuts worked. I could copy and paste in and out of the NSTextfields using the keyboard to my hearts content. Problem solved. The dummy menu seems a but of hack, but I really cannot see how it is to be avoided if you don’t want a visible ‘Edit’ menu.
I imagined that this must be a problem that other Cocoa developers using an NSStatusItem would have had and surely there would be a better way to fix this somehow. Expecting some enlightenment I checked to see if I was able to copy the text in the about boxes of Jumpcut and Alarm Clock 2 (two of my favorite OS X applications) to the clipboard using only the keyboard shortcuts. To my amazement I heard the familiar ‘ding’ noise and the text wouldn’t copy, I right clicked and copied some text to the clipboard using the mouse and it worked just fine. So it would seem I’m not the only developer who has had this problem. The creators of both Jumpcut and Alarm Clock 2 either:
1) Didn’t notice the problem or
2) Didn’t think it was enough of a problem to fix (I have to agree) given that both Alarm Clock 2 and Jumpcut hardly use any NSTextFields and copying text from the about boxes is not something a lot of people would need to do
How many other applications with an NSStatusItem have this problem I wonder? Was I really the first to find it? All of this sure seems totally insane to make something so trivial as copy and pasting from a couple of text fields work.
So, next time you open the preferences window of MythGrowl, be sure to copy and paste in out of the fields a couple of times, it will make all my efforts that much more worth while
and I sure hope this post saves some other Cocoa developer a lot of pain.
January 14th, 2007 at 6:40 pm
Hey, the Jumpcut developer here — you’re right the first time; I never even considered this as a problem. (Thinking about it, I’m not sure that I do. What are the odds that someone would want to cut and paste out of my about box?) Thanks for the nice words re: Jumpcut, and best of luck with MythGrowl.
January 16th, 2007 at 3:07 am
Wow, thanks for the comment Steve. I use your application *every* day and really couldn’t work efficiently without it. Yes I don’t think it’s a problem at all in the case of your application. Thanks for your feedback and I’ll be sure to subscribe to your blog.
February 14th, 2009 at 11:09 pm
God bless you and Google. I was working on a menulet to intercept Apple Remote signals and forward them through XMPP to a remote machine when I noticed the same problem with my settings fields. You just saved me from what could have been hours of frustration and work.
February 15th, 2009 at 12:52 am
Awesome! That’s great to hear. Thanks for commenting.
June 9th, 2009 at 4:09 pm
Just had a user report this problem with my little Menubar Countdown application. Thanks for providing the answer.
July 9th, 2009 at 3:17 pm
Thanks, worked a treat.
I also went to the trouble instantiating the other menus in case people try to use those shortcuts too. Well I removed the stuff that made no sense for my app, like Find.
August 21st, 2009 at 4:49 am
thanks for this. i had to do some snooping around too. I figured out a slightly easier way.
-Make a new cocoa application in xcode.
-Drag the MainMenu into your real applications’ MainMenu nib. (Copys it)
-Delete the sub items from MainMenu. (leave Main Menu, and Main Menu > Menu Item (Edit)).
-Open the disclosure triangles on the menu item so you see the copy, paste, etc items.
-Select the Copy item
-Ctrl+Click on the Copy Item, and Drag it’s action to FirstResponder.
-The action of “copy:” will automatically be selected.
-Continue doing this for the rest of the items you want enabled.
Now you end up with just the edit in the nib, it’s hooked up, and no code to set the menu.
August 21st, 2009 at 8:15 pm
An easier way is to subclass NSTextField and override its performKeyEquivalent: method with
- (BOOL)performKeyEquivalent:(NSEvent *)event {
if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask) {
if ([[event charactersIgnoringModifiers] isEqualToString:@”x”]) {
return [NSApp sendAction:@selector(cut:) to:[[self window] firstResponder] from:self];
} else if ([[event charactersIgnoringModifiers] isEqualToString:@”c”]) {
return [NSApp sendAction:@selector(copy:) to:[[self window] firstResponder] from:self];
} else if ([[event charactersIgnoringModifiers] isEqualToString:@”v”]) {
return [NSApp sendAction:@selector(paste:) to:[[self window] firstResponder] from:self];
} else if ([[event charactersIgnoringModifiers] isEqualToString:@”a”]) {
return [NSApp sendAction:@selector(selectAll:) to:[[self window] firstResponder] from:self];
}
}
return [super performKeyEquivalent:event];
}
December 15th, 2010 at 4:21 am
“And it works! But now I have a problem, I don’t want my app to have an ugly, unneeded ‘Edit’ menu, I just want to enable the keyboard shortcuts. So, I created a ‘dummy’ menu with an ‘Edit’ menu in it and added it as an outlet, now I just needed to figure out a way to tell my application that my ‘dummy’ menu will be taking over the role of my main application menu. After some more messing around I found that you can do this with [NSApp setMainMenu:yourDummyMenuName]; which I called when setting up my NSStatusItem.”
You can read more about it?
November 15th, 2011 at 6:49 pm
The easiest way I found around this problem is just putting some NSButton Objects on the layout, give them the appropriate Key Equivalent in the editor and hook ‘m up to the corresponding inputs from First Responder.
After checking that everything works as expected make them transparent, non bordered and change the size to 0 by 0 pixels.
Ugly but easy!