I'd like to create a basic NSOutlineView
that mimics macOS Finder 'as List' behaviour. When clicking and dragging the NSOutlineView
should either select rows or start a dragging session, depending on the starting position of the mouse.
I've noticed this behaviour in multiple applications - Finder (as List), ForkLift (as List), Nova (File Browser panel) - so I assume this is some standard behaviour for NSOutlineView
.
But the default NSOutlineView
, whether created in Interface Builder or programmatically, either always selects rows or always drags rows.
The verticalMotionCanBeginDrag
property of NSTableView
looks related, but is not enabling the above behaviour. When enabled a mouse drag will always start a dragging session. When disabled a vertical mouse drag will always select rows.
I've tried both view-based and cell-based NSOutlineView
, both have the same dragging behaviour depending on verticalMotionCanBeginDrag
.
A dragging session starts if items are returned from the following NSOutlineViewDelegate
method:
func outlineView(_ outlineView: NSOutlineView, pasteboardWriterForItem item: Any) -> NSPasteboardWriting? {
// if nil is returned for all items, no dragging session will start
// if an item is returned, a dragging session will start
}
But it feels strange to do logic in there, since it's called for all target row items.
Alternatively using the following NSOutlineViewDelegate
method:
func outlineView(_ outlineView: NSOutlineView, writeItems items: [Any], to pasteboard: NSPasteboard) -> Bool {
// determine starting point of drag?
}
But this has been deprecated since macOS 10.15.
How to determine whether a mouse drag will select rows or drag the items?
Subclass NSOutlineView
and override
func canDragRows(with rowIndexes: IndexSet, at mouseDownPoint: NSPoint) -> Bool
It would be nice if there was a datasource method but there isn't.