2012-06-26 1 views
6

아주 드물게 사용되는 응용 프로그램의 주 메뉴에 메뉴 항목을 추가하고 싶습니다. 기본적으로 숨겨져 사용자가 Option 키를 누른 경우에만 표시되도록합니다. 어떻게해야합니까?Option 키를 눌러 응용 프로그램의 주 메뉴에서 메뉴 항목 숨기기/표시

은 내가 flagsChanged:을 처리해야한다는 것,하지만 NSResponder의 방법이며 NSMenuNSResponder에서 상속하지 않는 이유는 무엇입니까? 메인 윈도우 콘트롤러 안에서 시도해 보았고 메뉴를 클릭하기 전에 Option 키를 누르면 작동합니다. 다음 유스 케이스가 작동하지 않습니다. 메뉴 항목을 클릭하고 (항목이 없음) 옵션 키를 누릅니다. 항목이 표시되고 옵션 키가 해제되어야합니다. 항목이 사라집니다.

또한 NSEvent의 addLocalMonitorForEventsMatchingMask:handler:addGlobalMonitorForEventsMatchingMask:handler:NSFlagsChangedMask으로 시도했지만 기본 메뉴가 열려있는 동안 옵션 키를 누르면 로컬 또는 전역 처리기가 실행되지 않습니다.

어떻게하면됩니까?

답변

5

다음을 applicationDidFinishLaunching에 추가하십시오. 그것은 성능 저하가 너무 많이하지 그래서 컨트롤을 추적하는 동안

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

다음

- (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); 
} 

타이머는 화재를 추가합니다.

+0

방금 ​​시도해 볼 시간이있어서 작동합니다. 고마워요! –

9

메뉴를 구성 할 때 옵션 항목을 포함하고 숨김으로 표시하십시오. 그런 다음 클래스 인스턴스를 메뉴의 위임자로 설정하고 메뉴가 열려있는 동안 실행 루프 관찰자를 추가하여 옵션 항목의 숨겨진 상태를 제어합니다. NSMenuDelegate 방법 menuNeedsUpdate: 이후

@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

이것은 더 나은 해결책 인 것 같습니다. 조금 후에 시도하겠습니다. 감사. –

+0

이것은 챔피언처럼 일했습니다. – dbainbridge

+1

여기에주의하십시오. 난 그냥 시도하고 메뉴 및 다른 메뉴를 열려면 메뉴 막대를 통해 마우스를 이동할 때 메뉴 NeedsUpdate : (항상 전에 메뉴가 열린 경우), 충돌로 이어지는 것을 알 수없는 것으로 나타났습니다. 'menuNeedsUpdate :'대신'menuWillOpen :'을 사용하십시오. – Mark

1

가 표시되기 전에 호출, 그것은 [NSEvent modifierFlags]가 대체 비트가 설정되어 있는지 확인, 그것을 무시하고, 표시/비밀 메뉴 항목을 숨길 것을 사용하는 것이 가능하다. 여기

정확히이 항목에 포함되는 Reveal Functionality with Key Modifiers에서 복사, 예를 들어 : 당신이 두 개의 메뉴 항목을 사용하는 것입니다 달성 할 수

#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

나는 그것을 시도하지는 않았지만이 방법은 메뉴를 열기 전에 Cmd 키를 누른 경우에만 비밀 메뉴 항목을 보여줄 것으로 보인다. 그러나 메뉴가 열려있는 동안 cmd 키를 눌러서이 메뉴 항목을 표시하거나 숨기고 싶습니다. –

10

가장 좋은 방법은, 첫 번째 메뉴 항목은 높이 0의 사용자 정의보기를 사용 , 비활성화 된 경우 바로 아래에 "대체"항목이 표시됩니다. (이 항목의 keyEquivalentModifierMaskNSAlternateKeyMask으로 설정해야합니다.)이 배열을 사용하면 옵션 키를 누를 때 NSMenu가 메뉴 항목을 마술처럼 나타낼 수있는 대체 항목으로 0 높이 메뉴 항목을 자동으로 대체합니다.

타이머, 업데이트 또는 플래그 변경 알림이 필요하지 않습니다.

이 기능은 여기에 문서에 설명되어 있습니다 : Managing Alternates

+0

"높이 0의 사용자 정의보기를 사용하고 사용할 수 없습니다."- 좋은 예가 될 것입니다. – frakman1

+0

사용자 지정보기를 사용할 필요가 없습니다. 그냥 작동합니다 (macOS 11.6에서 테스트되었습니다). –

+0

그게 효과가 있지만, 접근성 및 모든 메뉴 항목을 사용자가 사용할 수있는 모든 다른 메커니즘에 정말 나쁘다. OS에서 NSMenuItem의 상태를 숨기고 싶지는 않습니다 (즉, 실제로 숨김). –

0

여기에 몇 가지 복잡한 대답을있다하지만 실제로 매우 간단합니다

2 개 메뉴 아이템을 만듭니다. 첫 번째는 원하는 keyEquivalent와 title이있는 기본값입니다. 두 번째는 수식어 키가 눌려져있을 때 표시되는 것으로 별도의 keyEquivalent와 title로 다시 나타납니다. 두 번째 메뉴 항목에서 '대체'를 활성화하면 다른 모든 항목이 자동으로 실행됩니다.

필요한 수정자는 2 개의 keyEquivalent 값을 비교하여 감지됩니다.