cocoansmenuitemnstoolbarnsrespondernstoolbaritem

NSToolbarItem validation in relevant controller


I have an NSToolbarItem with an NSButton as its view and an NSMenuItem in the main menu. Both have the same action, which is sent to the first responder, not to a particular target. That method is ultimately implemented in a subclass of NSSplitViewController, somewhere in the view hierarchy of the window’s content view. I want to validate both items, but have that specific split-view controller take care of the validation, because it relies on some conditions local to that controller.

I overrode validateToolbarItem(_:) and validateMenuItem(_:) in that split-view controller. For the menu item, this is working as expected. The method is called and the validation happens. validateToolbarItem(_:) is never called, however.

According to Apple’s documentation, NSToolbar does not send validateToolbarItem(_:) to view-based toolbar items. To test this, I have substituted the toolbar item with an image toolbar item and there it works as expected.

Based on this, I have come across several solutions, but they aren’t quite what I want.

Is there an elegant solution for this that works as well like it does for the image toolbar item and the menu item?


Solution

  • I wrote the following code in my NSToolbarItem subclass for buttons. With this toolbarItem subclass, you can use normal validateUserInterfaceItem() or validateToolbarItem() to validate toolbar items that contain an NSControl.

    override func validate() {
    
        // validate content view
        if
            let control = self.view as? NSControl,
            let action = self.action,
            let validator = NSApp.target(forAction: action, to: self.target, from: self) as AnyObject?
        {
            switch validator {
            case let validator as NSUserInterfaceValidations:
                control.isEnabled = validator.validateUserInterfaceItem(self)
            default:
                control.isEnabled = validator.validateToolbarItem(self)
            }
    
        } else {
            super.validate()
        }
    }