Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom NSView in NSMenuItem not receiving mouse events

I have an NSMenu popping out of an NSStatusItem using popUpStatusItemMenu. These NSMenuItems show a bunch of different links, and each one is connected with setAction: to the openLink: method of a target. This arrangement has been working fine for a long time. The user chooses a link from the menu and the openLink: method then deals with it.

Unfortunately, I recently decided to experiment with using NSMenuItem's setView: method to provide a nicer/slicker interface. Basically, I just stopped setting the title, created the NSMenuItem, and then used setView: to display a custom view. This works perfectly, the menu items look great and my custom view is displayed.

However, when the user chooses a menu item and releases the mouse, the action no longer works (i.e., openLink: isn't called). If I just simply comment out the setView: call, then the actions work again (of course, the menu items are blank, but the action is executed properly). My first question, then, is why setting a view breaks the NSMenuItem's action.

No problem, I thought, I'll fix it by detecting the mouseUp event in my custom view and calling my action method from there. I added this method to my custom view:

- (void)mouseUp:(NSEvent *)theEvent {
  NSLog(@"in mouseUp");
  }

No dice! This method is never called.

I can set tracking rects and receive mouseEntered: events, though. I put a few tests in my mouseEntered routine, as follows:

if ([[self window] ignoresMouseEvents]) {  NSLog(@"ignoring mouse events");  }
else {  NSLog(@"not ignoring mouse events");  }
if ([[self window] canBecomeKeyWindow]) {  dNSLog((@"canBecomeKeyWindow"));  }
else {  NSLog(@"not canBecomeKeyWindow");  }
if ([[self window] isKeyWindow]) {  dNSLog((@"isKeyWindow"));  }
else {  NSLog(@"not isKeyWindow");  }

And got the following responses:

not ignoring mouse events
canBecomeKeyWindow
not isKeyWindow

Is this the problem? "not isKeyWindow"? Presumably this isn't good because Apple's docs say "If the user clicks a view that isn’t in the key window, by default the window is brought forward and made key, but the mouse event is not dispatched." But there must be a way do detect these events. HOW?

Adding:

[[self window] makeKeyWindow];

has no effect, despite the fact that canBecomeKeyWindow is YES.

like image 204
Dennis Avatar asked Sep 08 '09 18:09

Dennis


2 Answers

I've updated this version for SwiftUI Swift 5.3:

final class HostingView<Content: View>: NSHostingView<Content> {
    override func viewDidMoveToWindow() {
        window?.becomeKey()
    }
}

And then use like so:

let item = NSMenuItem()
let contentView = ContentView()
item.view = HostingView(rootView: contentView)

let menu = NSMenu()
menu.items = [item]
like image 140
SwiftyFinch Avatar answered Oct 13 '22 20:10

SwiftyFinch


Add this to your custom view and you should be fine:

- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
    return YES;
}
like image 21
fabian Avatar answered Oct 13 '22 20:10

fabian