One approach that I used is to not try to apply context menu actions to one specific item that was clicked, but to the selected items. And I make a clicked-on element that adds itself to the selection.
I used a custom view to represent a collection item. The custom view class has an output, item , to its own collection view element, which I plug into the NIB. It also overrides -rightMouseDown: so that the element adds itself to the selection:
- (void) rightMouseDown:(NSEvent*)event { NSCollectionView* parent = self.item.collectionView; NSUInteger index = NSNotFound; NSUInteger count = parent.content.count; for (NSUInteger i = 0; i < count; i++) { if ([parent itemAtIndex:i] == self.item) { index = i; break; } } NSMutableIndexSet* selectionIndexes = [[parent.selectionIndexes mutableCopy] autorelease]; if (index != NSNotFound && ![selectionIndexes containsIndex:index]) { [selectionIndexes addIndex:index]; parent.selectionIndexes = selectionIndexes; } return [super rightMouseDown:event]; }
If you prefer rather than adding an item to this selection, you can check if it is already selected. If so, do not change the selection. If this is not the case, replace the selection with only one element (making it the only selected element).
Alternatively, you can set the context menu in the element views, and not in the collection view. Then the menu items can be targeted either at the item or at the collection item.
Finally, you can subclass NSCollectionView and override -menuForEvent: You still call super and return the returned menu, but you can take the opportunity to record the event and / or element in its location. To define this, you would do something like:
- (NSMenu*) menuForEvent:(NSEvent*)event { _clickedItemIndex = NSNotFound; NSPoint point = [self convertPoint:event.locationInWindow fromView:nil]; NSUInteger count = self.content.count; for (NSUInteger i = 0; i < count; i++) { NSRect itemFrame = [self frameForItemAtIndex:i]; if (NSMouseInRect(point, itemFrame, self.isFlipped)) { _clickedItemIndex = i; break; } } return [super menuForEvent:event]; }
source share