Apple 如何在打开时更新机场菜单?(当 NSMenu 已经打开时如何更改它) [英] How does Apple update the Airport menu while it is open? (How to change NSMenu when it is already open)

查看:18
本文介绍了Apple 如何在打开时更新机场菜单?(当 NSMenu 已经打开时如何更改它)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个可以弹出 NSMenu 的状态栏项目,我有一个委托集并且它正确连接(-(void)menuNeedsUpdate:(NSMenu *)menu 工作正常).也就是说,该方法设置为在显示菜单之前调用,我需要监听它并触发异步请求,稍后在菜单打开时更新菜单,我不知道应该如何完成.

I've got a statusbar item that pops open an NSMenu, and I have a delegate set and it's hooked up correctly (-(void)menuNeedsUpdate:(NSMenu *)menu works fine). That said, that method is setup to be called before the menu is displayed, I need to listen for that and trigger an asynchronous request, later updating the menu while it is open, and I can't figure out how that's supposed to be done.

谢谢:)

编辑

好的,我现在在这里:

当您单击菜单项(在状态栏中)时,将调用一个运行 NSTask 的选择器.我用通知中心监听那个任务什么时候完成,然后写:

When you click on the menu item (in the status bar), a selector is called that runs an NSTask. I use the notification center to listen for when that task is finished, and write:

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

这个方法肯定会被调用,因为如果我点击菜单外并立即返回它,我会得到一个包含这些信息的更新菜单.问题是它没有更新-菜单打开时-.

This method is definitely called because if I click out of the menu and immediately back onto it, I get an updated menu with this information in it. The problem is that it's not updating -while the menu is open-.

推荐答案

这里的问题是,即使在菜单跟踪模式下,您也需要触发回调.

The problem here is that you need your callback to get triggered even in menu tracking mode.

例如,-[NSTask waitUntilExit]使用 NSDefaultRunLoopMode 轮询当前运行循环,直到任务完成".这意味着它在菜单关闭之前不会运行.在这一点上,安排 updateTheMenu 在 NSCommonRunLoopMode 上运行并没有帮助——毕竟它不能回到过去.我相信 NSNotificationCenter 观察者也只在 NSDefaultRunLoopMode 中触发.

For example, -[NSTask waitUntilExit] "polls the current run loop using NSDefaultRunLoopMode until the task completes". This means that it won't get run until after the menu closes. At that point, scheduling updateTheMenu to run on NSCommonRunLoopMode doesn't help—it can't go back in time, after all. I believe that NSNotificationCenter observers also only trigger in NSDefaultRunLoopMode.

如果您能找到某种方法来安排即使在菜单跟踪模式下也能运行的回调,那么您就做好了;您可以直接从该回调中调用 updateTheMenu.

If you can find some way to schedule a callback that gets run even in the menu tracking mode, you're set; you can just call updateTheMenu directly from that callback.

- (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 不明白我的后台任务何时完成".但是,您可以使用一些同样简单的机制.

Run this and hold down the File menu, and you'll see the extra menu item appears and disappears every half second. Obviously "every half second" isn't what you're looking for, and NSTimer doesn't understand "when my background task is finished". But there may be some equally simple mechanism that you can use.

如果没有,您可以使用 NSPort 子类之一自行构建它——例如,创建一个 NSMessagePort 并在完成后让您的 NSTask 写入它.

If not, you can build it yourself out of one of the NSPort subclasses—e.g., create an NSMessagePort and have your NSTask write to that when it's done.

您真正需要按照 Rob Keniger 上面描述的方式显式安排 updateTheMenu 的唯一情况是,如果您尝试从运行循环外部调用它.例如,您可以生成一个线程来触发子进程并调用 waitpid(它会阻塞直到进程完成),然后该线程必须调用 performSelector:target:argument:order:modes: 而不是直接调用 updateTheMenu.

The only case you're really going to need to explicitly schedule updateTheMenu the way Rob Keniger described above is if you're trying to call it from outside of the run loop. For example, you could spawn a thread that fires off a child process and calls waitpid (which blocks until the process is done), then that thread would have to call performSelector:target:argument:order:modes: instead of calling updateTheMenu directly.

这篇关于Apple 如何在打开时更新机场菜单?(当 NSMenu 已经打开时如何更改它)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆