11

I've bounded the menu to the NSCollectionView in interface builder. But when I CTRL+click (right click) on it the menu is not showing.

I've tried adding some method to the NSCollectionView subclass. None of them is invoked:

+ (NSMenu*)defaultMenu
- (NSMenu *)menuForEvent:(NSEvent *)theEvent
- (void)rightMouseDown:(NSEvent *)theEvent
- (void)sendEvent:(NSEvent *)theEvent

The only method which is invoked is:

- (NSView *)hitTest:(NSPoint)aPoint

Which means that the NSCollectionView receives the mouse events.

I've also tried to add the same methods to the subclass of NSCollectionViewItem, and the result is the same. Only hitTest: is called.

Rajesh
  • 850
  • 9
  • 18
aneuryzm
  • 63,052
  • 100
  • 273
  • 488

3 Answers3

4

I had the same issue with the "new" NSCollectionView. The contextual menu is set up in the xib, and it is actually correctly triggered by an actual right-click on the mouse, and also by double-finger tab on the trackpad (if the user has set that option in the System Preferences), but not by control-click. Thus it seems like a legitimate bug or limitation with NSCollectionView, maybe dependent on how it is set up.

In any case, here is a shorter solution, this one in Swift, that assumes you have otherwise set up the contextual menu using the menu outlet for the collection view (or you have it set up as outlined in Apple's documentation).

You will need to create a subclass of NSCollectionView and choose the subclass for the collection view in the xib. Here is the code for the subclass:

import Cocoa

class MyCollectionView: NSCollectionView {

    /// Fixes the behavior of collection view with control-click, that does not properly trigger the contextual menu.
    override func mouseDown(with event: NSEvent) {
        super.mouseDown(with: event)
        if event.type == .rightMouseDown || event.modifierFlags.contains(.control) {
            rightMouseDown(with: event)
        }
    }

}
charles
  • 11,212
  • 3
  • 31
  • 46
1

This works for me:

@interface MyCollectionView : NSView
-(void)mouseDown:(NSEvent *)theEvent;
@end

@implementation MyCollectionView

-(void)mouseDown:(NSEvent *)theEvent
{

    NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@"Contextual Menu"];
    [theMenu insertItemWithTitle:@"Beep" action:@selector(beep) keyEquivalent:@"" atIndex:0];
    [theMenu insertItemWithTitle:@"Honk" action:@selector(honk) keyEquivalent:@"" atIndex:1];

    [NSMenu popUpContextMenu:theMenu withEvent:theEvent forView:self];

    [super mouseDown:theEvent];

}

-(void)beep{

}

-(void)honk{

}

@end

I hope this helps.

Jonas
  • 2,139
  • 17
  • 38
  • The mouseDown method is called, but the menu doesn't popup. But I don't get your answer: you have override the superclass method with any custom code in it, so what's the difference? – aneuryzm Feb 15 '16 at 10:20
  • I thought your problem is that the mousedown won't get called. With it getting called it should be easy to display the contextmenu. I update my answer in a minute – Jonas Feb 15 '16 at 10:25
  • I see, ok thanks. However this is a workaround, right? It should popup automatically. – aneuryzm Feb 15 '16 at 10:25
  • ok thanks. I've added if ([theEvent modifierFlags] & NSControlKeyMask) to check if it's right click (or ctrl+click). – aneuryzm Feb 15 '16 at 10:39
  • I just checked the Apple Docs. It says it's one of three possible implementations. https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MenuList/Articles/DisplayContextMenu.html – Jonas Feb 15 '16 at 11:13
  • Ah ok. So not so workaround after all. Is maybe because NSCollectionView is an old class? I saw a new version is going to be published soon. – aneuryzm Feb 15 '16 at 11:51
  • I don't think that the problem is the age of the class. I just think you are missing something in your original implementation. But if it works now, everything should be fine. But i saw a WWDC Video recently about changes to the NSCollectionView, too. I think they changed the design of it for osx 10.11+ – Jonas Feb 15 '16 at 12:02
1

Subclass the NSCollectionView

class OSCollectionView: NSCollectionView {

    
    override func menu(for event: NSEvent) -> NSMenu? {
        print("menu() called")
        let menu = NSMenu()
        menu.addItem(NSMenuItem(title: "Create a clone", action: #selector(clone(_:)), keyEquivalent: ""))
        return menu
    }
    
    
    @objc
    func clone(_ sender: Any){
        //editDelegate?.terminateAndReplace(self)
        print("Clone item")
    }
}
Duncan Groenewald
  • 8,496
  • 6
  • 41
  • 76