NSToolbarItem validation in relevant controller

2020-07-10 06:09发布

问题:

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.

  • Subclass NSToolbarItem and override validate(). However, no guidance is given as to how I end up getting the controller’s validateToolbarItem(_:) to call.

  • Subclass NSToolbar and override validateVisibleToolbarItems(), then send messages to the first responder. Here I am running into the problem, that I cannot send a message to the split-view controller, because it is outside of the toolbar’s responder chain.

  • Subclass NSToolbar as above, but implement validateToolbarItem(_:) in a controller that is within the responder chain, such as the NSWindowController. This would work, but then I have to add additional code to handle what is not necessary for the menu item.

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

回答1:

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()
    }
}