1

I'm implementing the rename/editing function in an NSOutlineView. The basic implementation is like:

@objc func renameAction() {
    let row = outlineView.clickedRow
    let rowView = outlineView.view(
        atColumn: 0,
        row: row,
        makeIfNecessary: false) as! NSTableCellView

    rowView.textField!.isEditable = true
    rowView.window?.makeFirstResponder(rowView.textField!)
}

A mouseDown: is handled in the NSOutlineView so that the NSTextField can quit editing mode when clicking on the row that is being edited. The idea of using a custom delegate is from this question

override func mouseDown(with event: NSEvent) {
    super.mouseDown(with: event)
    let localLocation = self.convert(
        event.locationInWindow,
        from: nil)

    let clickedRow = self.row(at: localLocation)
    #if DEBUG
        print(#file, #line, clickedRow)
    #endif
    if clickedRow != -1 {
        self.extendedDelegate?.didClickRow(clickedRow)
    }
}

In the delegate:

func didClickRow(_ row: Int) {
    //... get the textField
    textField.isEditable = false
    textField.resignFirstResponder()
    textField.window?.makeFirstResponder(
        textField.window?.contentView
    )
}

The strange thing is I can right click on an NSTableCellView(including both an icon and an NSTextField) to get the context menu before a left click on ANY row. After the left click, I can only get the context menu by right clicking anywhere outside the NSTableCellView (of any row, no matter it's the clicked one or not) but inside the row (the blank areas surrouding the NSTableCellView).

Screenshots

Before any left clicking, right clicking on the NSTableCellView gets the menu:

Right clicked to get a menu

After a left clicking on any row, right click doesn't work "on" all the NSTableCellViews, but still works on the blank area of the row.

Right clicked on the "CG" but got nother // No menu upon clicking on the NSTableCellView. But the menu will appear when clicking on the blank areas of the row.

Update:

I found that if the overriding mouseDown: is commented out, (as expected) there's no issue. But that will throw the ability to end editing by clicking on the current row.

LShi
  • 1,500
  • 16
  • 29
  • 1
    Don't call `resignFirstResponder`, `makeFirstResponder` will do that. Do `isEditable = false` after moving the first responder. Don't make `contentView` first responder unless `contentView` can be the first responder. Instead, make the outline view or the window first responder. – Willeke Oct 20 '17 at 11:30
  • Thank you! This works! I also tried not moving the `isEditable = false`, it also works. Is there anything special for the order? – LShi Oct 20 '17 at 12:41
  • I think so. The text field probably can't be the first responder if `isEditable` and `isSelectable` are `false`. – Willeke Oct 20 '17 at 15:26

0 Answers0