NSTableView Right Clicked Row Index
Asked Answered
F

6

17

I'm looking for a way to get right-clicked row index from NSTableView but I can't find any delegate methods or class attributes for it. Any suggestion is appreciated.

Friable answered 19/9, 2012 at 12:13 Comment(0)
L
11

While I haven't done this, I am pretty sure you can by overriding NSView's - (NSMenu*)menuForEvent:(NSEvent*)theEvent. The example in this link does a point conversion to determine the index.

-(NSMenu*)menuForEvent:(NSEvent*)theEvent
{
    NSPoint mousePoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
   int row = [self rowAtPoint:mousePoint];
   // Produce the menu here or perform an action like selection of the row.
}
Louvre answered 19/9, 2012 at 12:37 Comment(2)
I was hoping to find a way without subclassing NSTableView.Friable
Graham's answer below works without subclassing. Set the NSTableView's menu property to an NSMenu item you own. Set that NSMenu's delegate. In menuNeedsUpdate(_: NSMenu) you can call clickedRow() on the NSTableView. This DOES work for right clicks at the point this menu delegate is called.Ailurophile
N
23

Use the NSTableView method - (NSInteger)clickedRow to get the index of the last clicked row. The returned NSInteger will be the index of the right clicked row.

You do not need to subclass NSTableView for this solution. clickedRow is also available on NSOutlineView.

Nous answered 7/2, 2013 at 15:42 Comment(7)
I don't think this should work when right clicking a row because the return value of clickedRow is only meaningful in the target's implementation of the action or double-action method.Genesa
This works, so long as it is called from within the NSTableView's target action or doubleAction method. Do you have another situation where you want the clicked row?Nous
I think the original poster asked for a solution when right clicking a row. From what I could tell it's not possible to trigger either of those methods with a right-click.Genesa
According to the 10.5 release notes (developer.apple.com/library/content/releasenotes/AppKit/…): “The key thing to note is that clickedRow and clickedColumn will now both be valid when a contextual menu is popped up. … The clickedRow and clickedColumn properties will still be valid when the action is sent from the NSMenuItem.”. I found that this does work if the table’s menu outlet is set and if you override -menuForEvent: you have to call super.Lael
Thanks, this is the easiest answer to implement.Melano
This answer works correctly. Set the NSTableView's menu property to an NSMenu item you own. Set that NSMenu's delegate. In menuNeedsUpdate(_: NSMenu) you can call clickedRow() on the NSTableView. This DOES work for right clicks at the point this menu delegate is called.Ailurophile
@MichaelTsai that comment is the key for me. I had to use the menu outlet. Just creating and returning a NSMenu() in code was not updating my clickedRow valueBrenna
L
11

While I haven't done this, I am pretty sure you can by overriding NSView's - (NSMenu*)menuForEvent:(NSEvent*)theEvent. The example in this link does a point conversion to determine the index.

-(NSMenu*)menuForEvent:(NSEvent*)theEvent
{
    NSPoint mousePoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
   int row = [self rowAtPoint:mousePoint];
   // Produce the menu here or perform an action like selection of the row.
}
Louvre answered 19/9, 2012 at 12:37 Comment(2)
I was hoping to find a way without subclassing NSTableView.Friable
Graham's answer below works without subclassing. Set the NSTableView's menu property to an NSMenu item you own. Set that NSMenu's delegate. In menuNeedsUpdate(_: NSMenu) you can call clickedRow() on the NSTableView. This DOES work for right clicks at the point this menu delegate is called.Ailurophile
J
7

Updated Answer

If you want to get clicked row index on menu opening, the answer is NSTableView.clickedRow. Anyway this property is available only in specific moments, and usually just -1.

When is this index to be available? That's in NSMenuDelegate.menuWillOpen method. So you conform the delegate and implement the method on your class, and access the clickedRow property. It's done.

final class FileNavigatorViewController: NSViewController, NSMenuDelegate {
    let ov = NSOutlineView() // Assumed you setup this properly.
    let ctxm = NSMenu()
    override func viewDidLoad() {
        super.viewDidLoad()
        ov.menu = ctxm
        ctxm.delegate = self
    }
    func menuWillOpen(_ menu: NSMenu) {
        print(outlineView.clickedRow)
    }
}

Clicked row index is available until you click an item in the menu. So this also works.

final class FileNavigatorViewController: NSViewController {
    let ov = NSOutlineView() // Assumed you setup this properly.
    let ctxm = NSMenu()
    let item1 = NSMenuItem()
    override func viewDidLoad() {
        super.viewDidLoad()
        ov.menu = ctxm
        ov.addItem(item1)
        ov.target = self
        ov.action = #selector(onClickItem1(_:))
    }
    @objc
    func onClickItem1(_: NSObject?) {
        print(outlineView.clickedRow)
    }
}

I tested this on macOS Sierra (10.12.5).


Old Answer

Starting from OS X 10.11, Apple finally added a method to access clickedRow easily. Just subclass NSTableView and override this method and you'll get the clickedRow as far as I experienced.

func willOpenMenu(menu: NSMenu, withEvent event: NSEvent)

This needs subclassing, but anyway, the cleanest and simplest way to access clickedRow.

Also, there's a pairing method.

func didCloseMenu(menu: NSMenu, withEvent event: NSEvent?)
Jeanene answered 25/5, 2016 at 11:8 Comment(1)
Would be awesome if you could provide a working example. I spent the last two days getting a context menu in a NSTableCellView work but failed 😓Tavia
F
3

Just select row on right-click by implementing menuForEvent: in NSTableView subclass:

@implementation MyTableView

- (NSMenu *)menuForEvent:(NSEvent *)theEvent
{
    int row = [self rowAtPoint:[self convertPoint:theEvent.locationInWindow fromView:nil]];
    if (row == -1) 
        return nil;
    if (row != self.selectedRow)
        [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
    return self.menu;
}

@end
Finale answered 22/7, 2015 at 11:6 Comment(1)
This does not match the behavior in Finder and Mail, which let you use the contextual menu to act on a row without affecting the selection.Lael
P
0

if you dont need to open NSMenu but need to know "right click action with row number", i think most simple way is below. (Swift4 Code) Don't need any other connected Outer NSMenu class.

class SomeViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
  @IBOutlet weak var tableView: NSTableView!
  ...

  override func viewDidLoad() {
    ...
    tableView.action = #selector(some method()) // left single click action
    tableView.doubleAction = #selector(someMethod()) // left double click action
  }

  // right click action
  override func rightMouseDown(with theEvent: NSEvent) {
    let point = tableView.convert(theEvent.locationInWindow, from: nil)
    let row = tableView.row(at: point)
    print("right click")
    print(row)
  }
Protract answered 9/11, 2017 at 8:13 Comment(0)
E
0

I had the same question but I also needed a solution that would work with multiple selected rows (because when multiple rows are selected and you right-click on one of them, NSTableView highlights all of them). Here's the property I added for this in a subclass of NSTableView:

var rightClickRowIndexes: IndexSet {
    if clickedRow >= 0 {
        return selectedRowIndexes.contains(clickedRow) ? selectedRowIndexes : IndexSet(integer: clickedRow)
    } else {
        return IndexSet()
    }
}

enter image description here

Efrenefron answered 14/4, 2022 at 15:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.