objective-cmacosvalidationnsmenuitem

validateMenuItem to disable specific menu items


I an new to macOS XCode Obj-C so bear with me. I am trying to use validateMenuItem to disable and enable specific menu items.

I currently have this working to disable and enable *all menu items:

BOOL g_bEnableAllMenus = YES;

and then I am using the validateMenuItem

- (BOOL)validateMenuItem:(NSMenuItem *)item
{
    if( !g_bEnableAllMenus  )
        return NO;

    return YES;
}

To test, I am calling g_bEnableAllMenus in awakeFromNib and it is disabling all my menu items correctly.

- (void)awakeFromNib {
    g_bEnableAllMenus = NO; 
}

What I am trying to do now is this scenario:

Instead of g_bEnableAllMenus disabling and enabling ALL menu items when g_bEnableAllMenus = YES/NO; is called, I want it to disable all menu items EXCEPT a list of several other menuItems under a menu called TestMenu.

Then I want this list of other menu items under TestMenu separately controlled with a different BOOL: so I can enable and disable this menu item separately and not have it changed by g_bEnableAllMenus - only by g_bEnableTestMenu.

BOOL g_bEnableTestMenu = YES;

There is where I am stuck. I think I have to use some combination of [item action] == @selector(TestMenuItem:) in validateMenuItem so when g_bEnableTestMenu == NO it disables all menu items under my TestMenu (but doesn't touch other menu items).

Then when g_bEnableAllMenus == NO it still disables all my menus (as it correctly does now) but excludes my TestMenu.


Solution

  • Here's a more concrete answer based on my comment above.

    You can set a tag value on a menu item to avoid having to match by action/selector. If you're creating the menu items in code just set the .tag property directly; in an .xib layout set it on the main properties tab of the menu item:

    screenshot of menu properties user-interface

    Then in your validation handler you can just check the range of the tag:

    func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
        if item.tag >= 1000 && item.tag <= 1100 {
            // return your test validation logic result
        } else {
            // return your normal validation result
        }
    }
    

    Of course you could improve this sample by defining constants for your test-menu-item tag range min and max, or with other data structures (e.g. a CaseIterable enum of test-item tag values). You might want to use tags for other groups of commands that share validation logic that isn't readily determined by selectors/responder-chain.