Обновление Force NSMenu (вложенное подменю) для главного меню приложения Cocoa

  1. У меня есть какое-то подменю, вставленное как подменю "Элемент окна" главного меню
  2. У меня есть экземпляр моего объекта (предположим, что его имя класса - MenuController), унаследованный от NSObject и поддерживающий 2 из методов NSMenuDelegate: - numberOfItemsInMenu: - menu:updateItem:atIndex:shouldCancel:
  3. Этот экземпляр добавлен как Blue-Object в NIB для пробуждения во время выполнения
  4. Объект из шагов 2-3 настроен как делегат для подменю (шаг 1)

Теперь я могу предоставить содержимое подменю во время выполнения.

Далее я делаю следующее: я могу добавлять новые элементы или удалять старые из массива (внутри MenuController, который содержит заголовки меню), который отображается в реальном подменю по протоколу и делегату. Все работает просто отлично. За исключением одного: мне нравится назначать ярлыки для моих динамических пунктов меню. CMD-1, CMD-2, CMD-3 и т. Д.

Окно / MySubmenu / MyItem1 CMD-1, MyItem2 CMD-2, ...

Поэтому для вызова некоторых элементов я не хочу переходить в Window / MySubmenu / MyItem, чтобы щелкать по нему мышью, я хочу нажать только одну комбинацию клавиш, например CMD-3, чтобы вызвать элемент.

Хорошо, периодически это работает, как ожидалось. Но, как правило, я не могу сообщить Главному меню об изменениях во вложенных подменю, кроме как открыть окно / MySubmenu, чтобы перезагрузить его содержимое. Один стабильный способ воспроизвести проблему - просто попытайтесь удалить какой-либо элемент и нажмите назначенный ему старый ярлык, после того как вы создадите новый элемент в качестве замены для удаленного - bingo - ярлык не будет работать, прежде чем вы перейдете в Window / MySubmenu для просмотра текущего содержимого подменю.,

Я не знаю, как заставить главное меню перестроить свои подменю... Я пытался: [[NSApp mainMenu] update] и игры с NSNotificationCenter для отправки NSMenuDidAddItemNotification, NSMenuDidRemoveItemNotification, NSMenuDidChangeItemNotification

Я попытался выйти в подменю и явно вызвать метод update - нет пути... Иногда AppKit вызывает мои методы делегата - и я вижу, что иногда он не хочет ничего вызывать. Похоже на случайную стратегию.

Как я могу убедиться, что после "некоторого вызова" мое подменю будет в текущем состоянии после внутренних изменений массива?

3 ответа

Решение

Чтобы реализовать отображение 1:1, внедрите в делегат эти 3 метода:

- (BOOL)menu:(NSMenu *)menu
updateItem:(NSMenuItem *)item 
atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel

а также

- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu

а также

- (void)menuNeedsUpdate:(NSMenu *)menu
{
    if (!attachedMenu)
        attachedMenu = menu;
    if (!menu)
        menu = attachedMenu;
    NSInteger count = [self numberOfItemsInMenu:menu];
    while ([menu numberOfItems] < count)
        [menu insertItem:[[NSMenuItem new] autorelease] atIndex:0];
    while ([menu numberOfItems] > count)
        [menu removeItemAtIndex:0];
    for (NSInteger index = 0; index < count; index++)
        [self menu:menu updateItem:[menu itemAtIndex:index] atIndex:index shouldCancel:NO];
}

attachMenu - это внутренняя переменная типа NSMenu*

Затем, когда вы хотите принудительно обновить подменю, в любое время - просто позвоните

[self menuNeedsUpdate:nil];

Я попытался: [[NSApp mainMenu] обновить]...

Вы на правильном пути. Это может быть одна ситуация, в которой параллельный массив в приложении Какао является оправданным.

  1. Держите изменчивый массив пунктов меню, параллельно вашему массиву объектов модели, которые представляют пункты меню.
  2. Когда вы получите numberOfItemsInMenu: Сравните количество имеющихся у вас объектов модели с количеством элементов массива menu. Если это меньше, используйте removeObjectsInRange: способ сократить массив пунктов меню. Если это больше, дополните массив объектами NSNull. (Вы не можете использовать nil здесь, поскольку NSArray может содержать только объекты, и nil это отсутствие объекта.)
  3. Когда вы получите menu:updateItem:atIndex:shouldCancel: замените объект в массиве по этому индексу новым пунктом меню перед возвратом нового пункта меню.
  4. Соответствует протоколу NSMenuValidation, как указано в документации для update метод. В вашем методе проверки найдите индекс элемента меню в массиве, затем получите объект модели по этому индексу в массиве объектов модели и обновите элемент меню из него. Если вы находитесь на Snow Leopard, вы можете отправить меню propertiesToUpdate сообщение, чтобы определить, какие значения свойств вам нужно присвоить из объекта модели.

Предостережение заключается в том, что этот объект, делегат меню, также должен быть целью пунктов меню. Я предполагаю, что это так. Если это не так, на шаге 4 произойдет сбой, так как сообщения проверки отправляются адресатам пунктов меню.

Вы можете подать запрос на улучшение, прося лучшего способа.

В делегат призыв к numberOfItemsInMenu: вызов removeAllItems... это довольно странно, но в остальном меню не обновляется, даже если он вызывает делегата

- (NSInteger)numberOfItemsInMenu:(NSMenu*)menu {

    [menu removeAllItems];

    return [self.templateURLs count] + 2;

}
Другие вопросы по тегам