2010-05-11 18 views
33

我有一個彈開的NSMenu一個狀態的項目,我有一個代表組,它的正確掛接(-(void)menuNeedsUpdate:(NSMenu *)menu正常工作)。也就是說,該方法設置爲在菜單顯示之前調用,我需要監聽並觸發異步請求,稍後在打開菜單時更新菜單,但我無法弄清楚應該如何完成該操作。Apple如何在機場菜單打開時進行更新? (如何更改NSMenu時候已經是打開的)

謝謝:)

編輯

好了,我現在在這裏:

當你點擊菜單項(狀態欄),選擇被稱爲運行NSTask。我用的是通知中心,聽取當這項任務完成了,並寫:

[[NSRunLoop currentRunLoop] performSelector:@selector(updateTheMenu:) target:self argument:statusBarMenu order:0 modes:[NSArray arrayWithObject:NSEventTrackingRunLoopMode]]; 

,並具有:

- (void)updateTheMenu:(NSMenu*)menu { 
    NSMenuItem *mitm = [[NSMenuItem alloc] init]; 
    [mitm setEnabled:NO]; 
    [mitm setTitle:@"Bananas"]; 
    [mitm setIndentationLevel:2]; 
    [menu insertItem:mitm atIndex:2]; 
    [mitm release]; 
} 

這個方法,如果我點擊了菜單,並立即回肯定是因爲所謂的在它上面,我得到一個帶有這些信息的更新菜單。問題在於它不更新 - 菜單打開時。

回答

13

這裏的問題是,你需要你的回調,即使在菜單跟蹤模式被觸發。

例如, - [NSTask waitUntilExit]「使用NSDefaultRunLoopMode輪詢當前運行循環,直到任務完成」。這意味着直到菜單關閉後才能運行。在那一點上,調度updateTheMenu在NSCommonRunLoopMode上運行並沒有幫助 - 畢竟它不能及時回溯。我相信NSNotificationCenter觀察者也只能在NSDefaultRunLoopMode中觸發。

如果您可以找到一些方法來計劃即使在菜單跟蹤模式下運行的回調,也會設置;你可以直接從該回調調用updateTheMenu。

- (void)updateTheMenu { 
    static BOOL flip = NO; 
    NSMenu *filemenu = [[[NSApp mainMenu] itemAtIndex:1] submenu]; 
    if (flip) { 
    [filemenu removeItemAtIndex:[filemenu numberOfItems] - 1]; 
    } else { 
    [filemenu addItemWithTitle:@"Now you see me" action:nil keyEquivalent:@""]; 
    } 
    flip = !flip; 
} 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 
    NSTimer *timer = [NSTimer timerWithTimeInterval:0.5 
              target:self 
             selector:@selector(updateTheMenu) 
             userInfo:nil 
              repeats:YES]; 
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 
} 

運行此操作並按住文件菜單,您將看到額外的菜單項每半秒出現一次並消失。顯然,「每半秒鐘」不是你要找的東西,NSTimer不明白「我的後臺任務何時完成」。但是可能有一些同樣簡單的機制可以使用。

如果沒有,您可以自己創建一個NSPort子類 - 例如,創建一個NSMessagePort,並在完成時寫入NSTask。

唯一的情況下,你真的需要明確安排updateTheMenu羅布Keniger上面描述的方式是如果你試圖從運行循環之外調用它。例如,你可以產生一個線程來觸發一個子進程並調用waitpid(這會阻塞,直到進程完成),那麼該線程將不得不調用performSelector:target:argument:order:modes:而不是直接調用updateTheMenu。

+1

儘管這個問題很長時間,但我仍然對此很好奇,我很高興看到我爲什麼沒有工作。謝謝! – Aaron 2012-04-26 18:57:23

13

(如果你想改變菜單,類似於機場的菜單是如何顯示的詳細信息的佈局,當您選擇單擊它,然後繼續閱讀。如果你想要做完全不同的事情,那麼這個答案可能不是作爲相關的,只要你願意。)

的關鍵是-[NSMenuItem setAlternate:]。舉個例子,假設我們要構建一個NSMenu,其中有一個Do something...的操作。你的代碼了,就像這樣:

NSMenu * m = [[NSMenu alloc] init]; 

NSMenuItem * doSomethingPrompt = [m addItemWithTitle:@"Do something..." action:@selector(doSomethingPrompt:) keyEquivalent:@"d"]; 
[doSomethingPrompt setTarget:self]; 
[doSomethingPrompt setKeyEquivalentModifierMask:NSShiftKeyMask]; 

NSMenuItem * doSomething = [m addItemWithTitle:@"Do something" action:@selector(doSomething:) keyEquivalent:@"d"]; 
[doSomething setTarget:self]; 
[doSomething setKeyEquivalentModifierMask:(NSShiftKeyMask | NSAlternateKeyMask)]; 
[doSomething setAlternate:YES]; 

//do something with m 

現在,你可能會認爲,這將創建一個菜單,在這兩個項目:「做一些...」和「做什麼」,而你部分是對的。因爲我們設定的第二個菜單項是備用,因爲這兩個菜單項具有相同的等效鍵(但不同的修飾口罩),那麼只有第一個(即,一個是默認setAlternate:NO)將顯示。然後,當菜單打開時,如果按下表示第二個(即選項鍵)的修飾符掩碼,則該菜單項將從第一個菜單項實時變換到第二個菜單項。

這一點,例如,是蘋果菜單是如何工作的。如果您點擊一次,您會看到一些橢圓選項,例如「Restart ...」和「Shutdown ...」。 HIG指定如果存在省略號,則意味着系統將在執行操作之前提示用戶進行確認。但是,如果按下選項鍵(菜單仍處於打開狀態),則會發現它們更改爲「重新啓動」和「關閉」。省略號會消失,這意味着如果在按下選項鍵時選擇它們,它們將立即執行,而不會提示用戶進行確認。

相同的一般功能適用於狀態項目中的菜單。您可以將擴展的信息設置爲「備用」項目,並將其顯示爲僅在按下選項鍵時顯示的常規信息。一旦你理解了基本原理,實際上很容易實現而不需要太多的詭計。

+4

非常有用的信息,但不是我想要的。儘管如此,我一定會找到它的用處。我非常喜歡菜單注入它在打開時發現的網絡的方式。謝謝 – Aaron 2010-05-11 19:03:30

16

菜單鼠標跟蹤是在特殊的運行循環模式下完成的(NSEventTrackingRunLoopMode)。爲了修改菜單,您需要發送一條消息,以便在事件跟蹤模式下處理它。要做到這一點,最簡單的方法是使用的NSRunLoop這個方法:

[[NSRunLoop currentRunLoop] performSelector:@selector(updateTheMenu:) target:self argument:yourMenu order:0 modes:[NSArray arrayWithObject:NSEventTrackingRunLoopMode]] 

您也可以指定模式NSRunLoopCommonModes和消息將在任何常見的運行循環模式,包括NSEventTrackingRunLoopMode的發送。然後

您的更新方法會做這樣的事情:

- (void)updateTheMenu:(NSMenu*)menu 
{ 
    [menu addItemWithTitle:@"Foobar" action:NULL keyEquivalent:@""]; 
    [menu update]; 
} 
+0

今晚要出手了,謝謝! – Aaron 2010-05-11 19:04:45

+0

這似乎沒有工作。我通過NSTask提出數據請求,等待通知,收到通知後,填充整個類可訪問的數據對象,然後調用調用updateTheMenu方法的NSRunLoop行。然而,菜單並沒有實時更新,我必須點擊它,然後在更新的信息出現之前重新打開它。 – Aaron 2010-05-12 04:07:01

+1

如果你使用'NSRunLoopCommonModes'而不是'NSEventTrackingRunLoopMode',它能工作嗎? – 2010-05-12 05:39:20

相關問題