2012-06-26 8 views
6

Ich möchte einen Menüeintrag in das Hauptmenü der Anwendung einfügen, der sehr selten verwendet wird. Ich möchte, dass es standardmäßig ausgeblendet ist und nur angezeigt wird, wenn der Benutzer die Wahltaste gedrückt hält. Wie mache ich das?Menüeintrag im Hauptmenü der Anwendung ausblenden/anzeigen durch Drücken der Optionstaste

Es scheint, dass ich flagsChanged: umgehen sollte, aber es ist NSResponder ‚s Methode und NSMenu nicht erbt nicht von NSResponder? Ich habe es im Hauptfenster-Controller versucht, und es funktioniert, wenn ich die Optionstaste drücke, bevor ich auf Menü klicke. Der folgende Anwendungsfall funktioniert nicht: Klicken Sie auf den Menüeintrag (es gibt keinen Eintrag), drücken Sie die Optionstaste - mein Eintrag sollte erscheinen, die Auswahltaste loslassen - der Eintrag sollte verschwinden.

Ich habe auch versucht NSEvent die addLocalMonitorForEventsMatchingMask:handler: und addGlobalMonitorForEventsMatchingMask:handler: für NSFlagsChangedMask aber als Option-Taste gedrückt, während Hauptmenü ist geöffnet weder lokal oder global Handler werden nicht gebrannt.

Wie kann ich das tun?

Antwort

5

Fügen Sie Folgendes zu applicationDidFinishLaunch hinzu.

// Dynamically update QCServer menu when option key is pressed 
NSMenu *submenu = [[[NSApp mainMenu] itemWithTitle:@"QCServer"] submenu];  
NSTimer *t = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(updateMenu:) userInfo:submenu repeats:YES]; 
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSEventTrackingRunLoopMode]; 

dann

- (void)updateMenu:(NSTimer *)t { 

    static NSMenuItem *menuItem = nil; 
    static BOOL isShowing = YES; 

    // Get global modifier key flag, [[NSApp currentEvent] modifierFlags] doesn't update while menus are down 
    CGEventRef event = CGEventCreate (NULL); 
    CGEventFlags flags = CGEventGetFlags (event); 
    BOOL optionKeyIsPressed = (flags & kCGEventFlagMaskAlternate) == kCGEventFlagMaskAlternate; 
    CFRelease(event); 

    NSMenu *menu = [t userInfo]; 

    if (!menuItem) { 
     // View Batch Jobs... 
     menuItem = [menu itemAtIndex:6]; 
     [menuItem retain]; 
    } 

    if (!isShowing && optionKeyIsPressed) { 
     [menu insertItem:menuItem atIndex:6]; 
     [menuItem setEnabled:YES]; 
     isShowing = YES; 
    } else if (isShowing && !optionKeyIsPressed) { 
     [menu removeItem:menuItem]; 
     isShowing = NO; 
    } 

    NSLog(@"optionKeyIsPressed %d", optionKeyIsPressed); 
} 

Der Timer wird nur ausgelöst, hinzufügen, während Kontrollen verfolgt werden, so dass es nicht zu viel von einem Performance-Verlust ist.

+0

Ich hatte gerade eine Zeit, es zu versuchen und es funktioniert. Danke vielmals! –

9

Wenn Sie das Menü erstellen, fügen Sie das optionale Element hinzu und markieren Sie es als ausgeblendet. Setzen Sie dann Ihre Klasseninstanz als den Delegaten des Menüs und fügen Sie eine Beobachterüberwachung hinzu, während das Menü geöffnet ist, um den verborgenen Zustand des optionalen Elements zu steuern.

@implementation AppController { 
    CFRunLoopObserverRef _menuObserver; 
} 

- (void)updateMenu { 
    BOOL hideOptionalMenuItems = ([NSEvent modifierFlags] & NSAlternateKeyMask) != NSAlternateKeyMask; 
    [self.optionalMenuItem setHidden:hideOptionalMenuItems]; 
} 

- (void)menuWillOpen:(NSMenu *)menu { 
    [self updateMenu]; 

    if (_menuObserver == NULL) { 
     _menuObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { 
      [self updateMenu]; 
     }); 

     CFRunLoopAddObserver(CFRunLoopGetCurrent(), _menuObserver, kCFRunLoopCommonModes); 
    } 
} 

- (void)menuDidClose:(NSMenu *)menu { 
    if (_menuObserver != NULL) { 
     CFRunLoopObserverInvalidate(_menuObserver); 
     CFRelease(_menuObserver); 
     _menuObserver = NULL; 
    } 
} 
+0

Dies scheint eine bessere Lösung zu sein, ich werde es ein bisschen später versuchen. Vielen Dank. –

+0

Das funktionierte wie ein Champion. – dbainbridge

+1

Vorsicht hier. Ich habe es gerade ausprobiert und dabei festgestellt, dass menuNeedsUpdate: nicht immer aufgerufen wird (wenn das Menü zuvor geöffnet war), wenn man das Menü verlässt und die Maus über die Menüleiste bewegt, um andere Menüs zu öffnen, was zu einem Absturz führt. Verwenden Sie 'menuWillOpen:' anstelle von 'menuNeedsUpdate:' – Mark

1

Da die NSMenuDelegate Methode menuNeedsUpdate: vor der Anzeige genannt wird, ist es möglich, sie außer Kraft zu setzen, prüfen Sie, ob [NSEvent modifierFlags] die alternative Bit gesetzt hat, und dass, um Ihre geheimen Menüpunkte ein-/auszublenden.

Hier ist ein Beispiel, von Reveal Functionality with Key Modifiers kopiert, die genau dieses Thema behandelt:

#pragma NSMenu delegate methods 

- (void) menuNeedsUpdate: (NSMenu *)menu 
{ 
    NSLog(@"menuNeedsUpdate: %@", menu); 

    NSUInteger flags = ([NSEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask); 

    // We negate the value below because, if flags == NSAlternateKeyMask is TRUE, that 
    // means the user has the Option key held, and wants to see the secret menu, so we 
    // need shoudHideSecretMenu to be FALSE, so we just negate the value. 
    BOOL shoudHideSecretMenu = !(flags == NSAlternateKeyMask); 

    NSLog(@"Flags: 0x%lx (0x%x), shoudHideSecretMenu = %d", flags, NSAlternateKeyMask, shoudHideSecretMenu); 

    [secretMenuItem setHidden:shoudHideSecretMenu]; 
} 
+0

Ich habe es nicht versucht, aber es scheint, dass diese Methode geheime Menüpunkte nur anzeigen wird, wenn die Cmd-Taste vor dem Öffnen des Menüs gedrückt wurde. Aber ich möchte diesen Menüpunkt anzeigen/ausblenden, indem ich die CMD-Taste drücke/loslasse, während das Menü noch geöffnet ist. –

10

Der beste Weg, dies durch die Verwendung von zwei Menüpunkte ist der erste Menüpunkt erreichen kann, verwendet eine benutzerdefinierte Ansicht der Höhe 0 und ist deaktiviert, dann rechts darunter ist ein "alternativer" Gegenstand. (Sie müssen den Wert keyEquivalentModifierMask dieses Elements auf NSAlternateKeyMask setzen.) Wenn Sie bei dieser Konfiguration die Optionstaste drücken, ersetzt NSMenu automatisch den Menüeintrag Nullhöhe mit dem alternativen Element, wodurch ein Menüelement magisch erscheint.

Keine Notwendigkeit für Timer, Updates oder Flag Change-Benachrichtigungen.

Diese Funktionalität wird hier in der Dokumentation beschrieben: Managing Alternates

+0

"verwendet eine benutzerdefinierte Ansicht der Höhe 0 und ist deaktiviert" - Ein Beispiel wäre nett. – frakman1

+0

Keine Notwendigkeit, eine benutzerdefinierte Ansicht zu verwenden. Es funktioniert einfach (getestet auf MacOS 11.6). –

+0

Das mag funktionieren, aber ist wirklich schlecht auf Barrierefreiheit, und alle anderen Mechanismen, die chack welche Menüpunkte sind für den Benutzer verfügbar. Sie wollen nicht den Status Ihres NSMenuItem vor dem Betriebssystem verbergen (mit anderen Worten - verstecken Sie es wirklich). –

0

Es gibt einige komplexe Antworten hier, aber es ist eigentlich sehr einfach:

2 menuitems erstellen. Der erste ist der Standardwert mit dem Schlüssel Äquivalent und Titel, den Sie möchten. Die zweite wird angezeigt, wenn die Modifier-Taste gedrückt wird - wiederum mit separatem keyEquivalent und Titel. Aktivieren Sie auf dem zweiten Menüpunkt "Alternate" und alles andere geschieht automatisch.

Der erforderliche Modifikator wird durch Vergleichen der 2 Schlüsseläquivalenzwerte erkannt.