Frage

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.

War es hilfreich?

Lösung

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

Andere Tipps

- (void)keyDown: (NSEvent*)theEvent
{
    unsigned short keyCode = [theEvent keyCode];
    if (keyCode == 126) { // up-arrow
        [[NSNotificationCenter defaultCenter] postNotificationName:@"upArrow" object:nil];
    }
    else if (keyCode == 125) { // down-arrow
        [[NSNotificationCenter defaultCenter] postNotificationName:@"DownArrow" object:nil];
    } else
    {
        [super keyDown:theEvent];
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top