Disabling Segments in a NSSegmentedControl in a Toolbar

null

This post is part of a series on getting NSSegmentedControl to work in your toolbars.

  1. Original Approach
  2. Enabling/Disabling Segments (this post)
  3. Fixing an action dispatching bug
  4. Fixing action dispatching within overflow menus

Earlier this week I posted how to create a segmented toolbar item with 1 label for each segment. Now some options in TableFlip depend on context: without a selected cell, you cannot remove the selection, for example. So I disabled some segments and it looked alright:

disabled segments screenshot
How the segments should look with 2/3 disabled

Easy enough:

let enabled = hasSelection()

ThisParticularSegmentedControl.contextualSegments // = (0...1)
    .forEach { (segment: Int) in
        self.setEnabled(enabled, forSegment: segment)
}

But even though it’s disabled, and even though the NSSegmentedControl’s trackingMode is set to NSSegmentSwitchTracking.Momentary, clicking a disabled segment in the toolbar results in a permanent disabled-looking selection:

selected disabled segments screenshot
Disabled but toggled segment

I tried a ton of different things, including, but not limited to:

  • when the selection changes, deselect the segment again
  • when the toolbar validates, set selectedSegment = -1 (“no selection”)

The problem isn’t with the NSSegmentedControl, though. It’s with the NSToolbarItem that corresponds to the disabled segments!

So during NSToolbarItemGroup.validate() in my custom subclass I disable the corresponding subitems, too.

Some Example Code

This is the group:

class ToolbarItemGroup: NSToolbarItemGroup {

    convenience init(itemIdentifier: String, validator: ActionItemValidation) {

        self.init(itemIdentifier: itemIdentifier)

        self.validator = validator
    }

    var validator: ActionItemValidation!

    override func validate() {

        validateSubitems()
        validateSegmentedControl()
    }

    private func validateSubitems() {

        self.subitems.forEach(validator.validate)
    }

    private func validateSegmentedControl() {

        guard let segmentedControl = self.view as? AddDimensionSegmentedControl
            else { return }

        validator.validate(segmentedControl: segmentedControl)
    }
}

The ActionItemValidation type exposes two relevant methods:

  1. validate(segmentedControl:)
  2. validate(toolbarItem:)

The validate(toolbarItem:) variant existed already to enable/disable buttons in the toolbar. I achieve this by tagging the items, for example:

  • 0 equals “always on”,
  • 1 equals “enable only for selections”.

Re-using the method here was easy. I only had to set the tag for the group’s affected subitems.