Reputation: 1655
I have a Document based application. I want to add a contextual menu that displays context-sensitive info when the user right-clicks selected text in an NSTextView.
I have followed the advice in the Apple documentation and
menu
outlet of the NSTextView.So far so good. Every thing works as expected: the menu item appears and the action is called when it is selected.
I need to get the selected text from the NSTextView before the menu appears so that I can configure my menu item appropriately. According to the docs
If you need to customize the contextual menu, you can do so by setting an appropriate object as the menu’s delegate and implementing the menuWillOpen: method to customize the menu as you see fit just before it appears.
I connect the delegate of the NSMenu to File's Owner. None of the delegate methods are called. ( menuWillOpen:
is the only one I need, but I've tried others, too).
I set a breakpoint inside the IBAction that gets called when the menu item is selected. If I inspect the menu with the debugger I can see that the delegate is correctly set to the object that implements the delegate method.
Is there anything else to check? Anything I'm doing blatantly wrong?
Xcode v4.6.3
SDK v10.8
Deployment target 10.7
Upvotes: 3
Views: 1873
Reputation: 775
Finding this thread saved me a lot of time...thanks! Here's an implementation that works in an NSView
in Swift. myNSMenu
is an outlet from Storyboard to appDelegate
and a subclass of NSMenu
. Without the assignment of the delegate in the code below, the NSMenuDelegate
functions were not called.
let appDelegate = NSApplication.sharedApplication().delegate as! AppDelegate
appDelegate.myNSMenu.delegate = appDelegate.myNSMenu
NSMenu.popUpContextMenu(appDelegate.myNSMenu, withEvent: theEvent, forView: self)
Upvotes: 0
Reputation:
After some digging, this is what I found: NSTextView
builds a different NSMenu
instance to use as the contextual menu, probably by overriding -menuForEvent:
or some similar internal method. This new menu copies the menu items from the menu you created in Interface Builder (in fact, it creates new menu item instances whose attributes are copied from the original menu items) but it does not copy the menu delegate, which is why your menu delegate does not receive -menuWillOpen:
. I am not sure whether this is intentional or not. Reading that documentation quote you posted, it seems to be a bug.
What you can do is to set the delegate of your NSTextView
instance to an object whose class conforms to NSTextViewDelegate
(maybe your File’s Owner, which already conforms to NSMenuDelegate
) and implement the following method:
- (NSMenu *)textView:(NSTextView *)view menu:(NSMenu *)menu forEvent:(NSEvent *)event atIndex:(NSUInteger)charIndex
{
// if the menu delegate is not self, set another object
[menu setDelegate:self];
return menu;
}
This will make sure that the contextual menu created by the text view uses your delegate.
NB: since NSTextView
creates a different contextual menu, it could be the case that it might want to set the menu delegate to itself or some other internal object. In my tests the delegate is nil
, so it looks like it’s safe. Alternatively, you could discard the proposed menu
argument and return your own NSMenu
instance with the delegate correctly set.
Upvotes: 3