I would like to create a status item with a vertical slider in it, much like the sound control provided by Apple. My question is: how do I make it react to the up/down arrow keys, just like the slider in the sound menu?
I have tried to create a NSSlider subclass that would increase/decrease its value when the keys are pressed (see below), but I need to make it the first responder. To make it the first responder I made the main class the delegate of this menu and added this method:
- (void)menuWillOpen: (NSMenu*)menu
{
if (menu == statusBarMenu) {
[THE_WINDOW_THAT_CONTAINS_THE_SLIDER makeFirstResponder: slider];
}
Which window should I call? Should I do this any other way? How would you do this?
The slider subclass:
#import <AppKit/AppKit.h>
@interface KeyRespondingSlider : NSSlider
@end
@implementation KeyRespondingSlider
- (BOOL)canBecomeKeyView
{
return YES;
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (void)keyDown: (NSEvent*)theEvent
{
unsigned short keyCode = [theEvent keyCode];
if (keyCode == 126) { // up-arrow
[self setDoubleValue: [self doubleValue] + kChange];
}
else if (keyCode == 125) { // down-arrow
[self setDoubleValue: [self doubleValue] - kChange];
}
}
@end
I have tested it and it works when it is the first responder with a normal NSWindow. I just can't do it with a menu in a status bar item.
I have figured it out. Apple uses a NSWindow subclass called NSCarbonMenuWindow
for implementing menus. So I get the menuWillOpen: message, I wait a bit for the menu to open, I get the last window (the one created just now - menu window) and I make the slider the firstResponder. It all works now!
- (void)menuWillOpen: (NSMenu*)menu
{
if (menu == statusBarMenu) {
[NSThread detachNewThreadSelector: @selector(threadedMenuWillOpen) toTarget: self withObject: nil];
}
}
- (void)threadedMenuWillOpen
{
[NSThread sleepForTimeInterval: 0.1];
NSArray* windows = [NSApp windows];
NSWindow* menuWindow = [windows lastObject]; // The last window is the one I want because it has just been created
if ([[menuWindow className] isEqualToString: @"NSCarbonMenuWindow"]) {
[menuWindow makeFirstResponder: bSlider];
}
}