简体   繁体   中英

Status button highlight when popover is active (Swift, macOS)

I am making a status bar macOS app. On clicking on the status bar icon, I am showing an NSPopover (not an NSMenu). However, when my NSPopover is shown, my status menu icon is not highlighted. It is only highlighted for a moment when I click it. I want it to stay highlighted, much like how it behaves with the wifi status bar icon.

I know that if I use a NSMenu instead of a NSPopover, it can probably be fixed. But the requirement is such that I need to use a NSPopover.

I have tried the following approaches, but to no avail:

1.

let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
if let button = statusItem.button {
    button.setButtonType(.pushOnPushOff)
}
statusItem.highlightMode = true
statusItem.button?.highlight(true)
statusItem.button?.isHighlighted = true

I am not very experienced with status bar apps. So I am not really sure about which approach to take here.

The left most icon is my status bar app icon. The popover is currently active but the icon is not highlighted. I had to crop out the popover.

最左边的图标是我的状态栏应用程序图标。弹出窗口当前处于活动状态,但图标未突出显示。我不得不剪掉弹出窗口。

This can be done, but to do it reliably requires some tight coupling. In this example, I assume you have a NSViewController named PopoverController that has a togglePopover() method and that you've set this controller as the target of the status bar button.

Step 0: Context

Here's the basic setup of the class that controls the popover:

final class PopoverController: NSViewController
{
    private(set) var statusItem: NSStatusItem!
    private(set) var popover: NSPopover


    // You can set up your StatusItem and Popover anywhere; but here's an example with -awakeFromNib()
    override func awakeFromNib()
    {
        super.awakeFromNib()

        statusItem = NSStatusBar.system.statusItem(withLength: 20.0)
        statusItem.button?.sendAction(on: [.leftMouseDown, .rightMouseDown])
        statusItem.button?.image = NSImage(named: "statusBar-icon")
        statusItem.button?.target = self
        statusItem.button?.action = #selector(togglePopover)

        popover = NSPopover()
        popover.behavior = .transient
        popover.delegate = self
        popover.contentViewController = self
        popover.setValue(true, forKeyPath: "shouldHideAnchor")      // Private API
    }


    @objc func togglePopover()
    {
        if popover.isShown
        {
            popover.performClose(nil)
        }
        else if !popover.isShown,
                let button: NSButton = statusItem.button,
                button.window?.contentView != nil,              // Exception thrown if view is nil or not part of a window.
                button.superview != nil
        {
            popover.show(relativeTo: .zero, of: button, preferredEdge: .minY)
        }
    }
}

Step 1: Override the Status Button

extension NSStatusBarButton
{
    public override func mouseDown(with event: NSEvent)
    {
        if event.modifierFlags.contains(.control)
        {
            self.rightMouseDown(with: event)
            return
        }
        
        if let controller: PopoverController = self.target as? PopoverController
        {
            controller.togglePopover()
            self.highlight(controller.popover.isShown)
        }
    }
}

Step 2: Handle Popover Closing

Make sure PopoverController conforms to NSPopoverDelegate and implement this delegate method:

func popoverDidClose(_ notification: Notification)
{
    statusItem.button?.highlight(false)
}

Outcome

With all of that in place, the button highlighting now works just as it does for Apple's system status bar items like Control Center, Wifi, Battery, etc.

Note: you'll also need to add a global event monitor to listen for clicks that happen outside of your popover to ensure that it closes properly when the user clicks away from it. But that's outside the scope of this question and available elsewhere on SO.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM