Search code examples
swiftxcodemacosswift3swift4

OS X NSMenuItem with custom NSView does not highlight swift


I'm building a menubar only app. I have a custom view for my menuItem but it is not highlight(blue) when hovering over.

The custom class is working, it's detecting clicks but it's not highlighting, I tried using the enclosingMenuItem property in the menu to check for highlight status in the draw method but that did not work

override func draw(_ dirtyRect: NSRect) {
    super.draw(dirtyRect)
    if (self.enclosingMenuItem?.isHighlighted)! {
        // never triggered
    }
}

I use a xib for my menuBar object and linked a class to it containing this code when adding a new item:

    menuItem = NSMenuItem()
    menuItem.target = self
    menuItem.isEnabled = true
    view = MenuBarItem(frame: NSRect(x: 0, y: 0, width: 230, height: 28))
    view.delegate = self
    view.setupWith(title: "", lastRun: "", running: false)
    menuItem.view = view.view

    statusMenu.insertItem(menuItem, at: 1)

normal menuItems work as intended - but when hovering over a custom view

enter image description here . enter image description here

I'm out of ideas and any help is appreciated, thanks.


Solution

  • You need to implement your own highlighted property with an observer to update the view

     var highlighted : Bool = false {
            didSet {
                if oldValue != highlighted {
                    needsDisplay = true
                }
            }
        }
    

    Then you have to override mouseEntered and mouseExited

    override func mouseEntered(with theEvent: NSEvent) { highlighted = true }
    override func mouseExited(with theEvent: NSEvent) { highlighted = false }
    

    and drawRect like this:

    override func draw(_ dirtyRect: NSRect) {
    
        super.draw(dirtyRect)
        if highlighted && enclosingMenuItem!.isHighlighted {
            NSColor.selectedMenuItemColor.set()
        } else {
            NSColor.clear.set()
        }
        NSBezierPath.fill(dirtyRect)
    }
    

    and you might also adjust the label text color respectively.

    If you want to trigger the menu item action you need also to implement mouseUp and a tracking area and viewWillMove and viewDidMoveToWindow to enable and disable the tracking area.

    A custom view as a replacement for NSMenuItem is very smart but you are responsible to handle all events.