Cocoa: How to have a context menu when you right-click on a cell of NSTableView
Asked Answered
S

2

20

I have a TableView with custom cells. I want a contextual menu to appear when the user right clicks (or any other Apple variants of right-click) on one of the cells (and know which cell they clicked on).

I tried to subclass NSTableView and overwrite this method:

- (NSMenu *)menuForEvent:(NSEvent *)theEvent;

But it is never being called.

On the other hand,

- (void)rightMouseDown:(NSEvent *)theEvent;

Gets called. But I'm not sure it's the one we want.

More details:

//
//  PTTableView.m
// 
//
//  Created by Nathan Hazout on 5/31/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "PTTableView.h"


@implementation PTTableView

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}

- (void)rightMouseDown:(NSEvent *)theEvent { 
    NSLog(@"entered rightMouseDown");
}

- (NSMenu *)menuForEvent:(NSEvent *)theEvent {
    NSLog(@"entered menuForEvent");
    return [super menuForEvent:theEvent];
}

- (NSView *)hitTest:(NSPoint)aPoint{
    NSLog(@"entered hitTest");
    return [super hitTest:aPoint];
}

- (void)dealloc
{
    [super dealloc];
}

@end

rightMouseDown gets called. hiTest gets called many times. But menuForEvent doesn't.

Sustenance answered 31/5, 2011 at 11:36 Comment(0)
C
73

There's no need to muck about with event handling, all you have to do to assign a contextual menu is set the table view's menu outlet to point to the NSMenu object that you want to use for the contextual menu.

You can do this in Interface Builder by dropping an NSMenu object into your nib file and control-dragging from the table view to the menu to set the outlet.

Alternatively, you can use the -setMenu: method of NSTableView (inherited from NSResponder) to assign the menu programmatically.

Cobblestone answered 31/5, 2011 at 12:5 Comment(10)
Should I do it on the TableView or the Cell?Sustenance
You assign the contextual menu to the view.Cobblestone
Hi Rob, could you please point out what should I do to open the same context menu on left-clicking (instead of right).Tinney
@shy There should only be one contextual menu for any UI item. Control-click, left-click (mouse) and two-finger click (pad) are the common accessors for this menu. The standard tap/click is for interacting with the table/cells. (i.e. i believe this is already doing what you want)Atonality
For anyone stumbling upon this, you get the associated row with tableView.clickedRowSellars
Well, it's not really contextual if its the same item for every item then, is it? (using table views menu)Antipathy
@ErikAigner that's what the menu validation delegate methods are for.Cobblestone
@RobKeniger yes and no. Then you have tons of disabled fields. But I guess it's better to have disabled fields compared to a different menu for each item.Antipathy
@ErikAigner it's possible to use the NSMenu delegate methods (specifically menu(NSMenu, update: NSMenuItem, at: Int, shouldCancel: Bool)) to hide menu items that aren't relevant before the menu displays.Cobblestone
Nota: the -validateMenuItem: delegate method is now in NSMenuItemValidation delegate protocol.Jurat
P
20

A Swift 4 version of Rob's answer:

Add the menu:

let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Edit", action: #selector(tableViewEditItemClicked(_:)), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "Delete", action: #selector(tableViewDeleteItemClicked(_:)), keyEquivalent: ""))
tableView.menu = menu

The functions:

@objc private func tableViewEditItemClicked(_ sender: AnyObject) {

    guard tableView.clickedRow >= 0 else { return }

    let item = items[tableView.clickedRow]

    showDetailsViewController(with: item)
}

@objc private func tableViewDeleteItemClicked(_ sender: AnyObject) {

    guard tableView.clickedRow >= 0 else { return }

    items.remove(at: tableView.clickedRow)

    tableView.reloadData()
}
Pizzeria answered 3/4, 2019 at 12:37 Comment(4)
This solution creates a blue border around the cellLegislatorial
@Legislatorial I'm not sure why this solution would do that as it has no styling attributes.Pizzeria
what is showDetailsViewController method?Drop
@Drop Just a method that calls DetailsVC with the selected itemPizzeria

© 2022 - 2024 — McMap. All rights reserved.