Make progress on drag and drop. Reads feeds from pasteboard. This needs refactoring, though.

This commit is contained in:
Brent Simmons 2018-09-19 21:49:13 -07:00
parent f29f690625
commit 089ffbd299
2 changed files with 92 additions and 25 deletions

View File

@ -26,6 +26,18 @@ extension Feed: PasteboardWriterOwner {
static let feedUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.feed" static let feedUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.feed"
static let feedUTIInternalType = NSPasteboard.PasteboardType(rawValue: feedUTIInternal) static let feedUTIInternalType = NSPasteboard.PasteboardType(rawValue: feedUTIInternal)
private struct Key {
static let url = "URL"
static let homePageURL = "homePageURL"
static let name = "name"
// Internal
static let accountID = "accountID"
static let feedID = "feedID"
static let editedName = "editedName"
}
init(feed: Feed) { init(feed: Feed) {
self.feed = feed self.feed = feed
@ -57,23 +69,20 @@ extension Feed: PasteboardWriterOwner {
return plist return plist
} }
// MARK: - Dragged Feed
static func draggedFeeds(with pasteboard: NSPasteboard) -> Set<DraggedFeed>? {
guard let items = pasteboard.pasteboardItems else {
return nil
}
let feeds = items.compactMap { draggedFeed(with: $0) }
return feeds.isEmpty ? nil : Set(feeds)
}
} }
private extension FeedPasteboardWriter { private extension FeedPasteboardWriter {
private struct Key {
static let url = "URL"
static let homePageURL = "homePageURL"
static let name = "name"
// Internal
static let accountID = "accountID"
static let feedID = "feedID"
static let editedName = "editedName"
static let unreadCount = "unreadCount"
}
func exportDictionary() -> [String: String] { func exportDictionary() -> [String: String] {
var d = [String: String]() var d = [String: String]()
@ -100,11 +109,51 @@ private extension FeedPasteboardWriter {
if let editedName = feed.editedName { if let editedName = feed.editedName {
d[Key.editedName] = editedName d[Key.editedName] = editedName
} }
if feed.unreadCount > 0 { if let accountID = feed.account?.accountID {
d[Key.unreadCount] = feed.unreadCount d[Key.accountID] = accountID
} }
return d return d
}
static func draggedFeed(with dictionary: [String: String]) -> DraggedFeed? {
guard let url = dictionary[Key.url] else {
return nil
}
let homePageURL = dictionary[Key.homePageURL]
let name = dictionary[Key.name]
let accountID = dictionary[Key.accountID]
let feedID = dictionary[Key.feedID]
let editedName = dictionary[Key.editedName]
return DraggedFeed(url: url, feedID: feedID, homePageURL: homePageURL, name: name, editedName: editedName, accountID: accountID)
}
static func draggedFeed(with pasteboardItem: NSPasteboardItem) -> DraggedFeed? {
// TODO: This needs to handle strings and URLs also.
var pasteboardType: NSPasteboard.PasteboardType?
if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIInternalType) {
pasteboardType = FeedPasteboardWriter.feedUTIInternalType
}
else if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIType) {
pasteboardType = FeedPasteboardWriter.feedUTIType
}
guard let foundType = pasteboardType else {
return nil
}
let feedDictionary = pasteboardItem.propertyList(forType: foundType) as! [String: String]
return draggedFeed(with: feedDictionary)
} }
} }
struct DraggedFeed: Hashable {
let url: String
let feedID: String?
let homePageURL: String?
let name: String?
let editedName: String?
let accountID: String?
}

View File

@ -25,21 +25,21 @@ import Account
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
return nodeForItem(item as AnyObject?).numberOfChildNodes return nodeForItem(item).numberOfChildNodes
} }
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
return nodeForItem(item as AnyObject?).childNodes[index] return nodeForItem(item).childNodes[index]
} }
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
return nodeForItem(item as AnyObject?).canHaveChildNodes return nodeForItem(item).canHaveChildNodes
} }
func outlineView(_ outlineView: NSOutlineView, pasteboardWriterForItem item: Any) -> NSPasteboardWriting? { func outlineView(_ outlineView: NSOutlineView, pasteboardWriterForItem item: Any) -> NSPasteboardWriting? {
let node = nodeForItem(item as AnyObject?) let node = nodeForItem(item)
guard nodeRepresentsDraggableItem(node) else { guard nodeRepresentsDraggableItem(node) else {
return nil return nil
} }
@ -49,10 +49,29 @@ import Account
// MARK: - Drag and Drop // MARK: - Drag and Drop
func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
let parentNode = nodeForItem(item)
if parentNode == treeController.rootNode {
return SidebarOutlineDataSource.dragOperationNone
}
guard let draggedFeeds = FeedPasteboardWriter.draggedFeeds(with: info.draggingPasteboard()) else {
return SidebarOutlineDataSource.dragOperationNone
}
let draggingSourceOutlineView = info.draggingSource() as? NSOutlineView let draggingSourceOutlineView = info.draggingSource() as? NSOutlineView
let isLocalDrop = draggingSourceOutlineView == outlineView let isLocalDrop = draggingSourceOutlineView == outlineView
// // If NSOutlineViewDropOnItemIndex, retarget to parent of parent item, if possible.
// if index == NSOutlineViewDropOnItemIndex && !parentNode.canHaveChildNodes {
// guard let grandparentNode = parentNode.parent, grandparentNode.canHaveChildNodes else {
// return SidebarOutlineDataSource.dragOperationNone
// }
// outlineView.setDropItem(grandparentNode, dropChildIndex: NSOutlineViewDropOnItemIndex)
// return isLocalDrop ? .move : .copy
// }
if isLocalDrop { if isLocalDrop {
return validateLocalDrop(info, proposedItem: item, proposedChildIndex: index) return validateLocalDrop(draggedFeeds, proposedItem: item, proposedChildIndex: index)
} }
return SidebarOutlineDataSource.dragOperationNone return SidebarOutlineDataSource.dragOperationNone
} }
@ -66,8 +85,7 @@ import Account
private extension SidebarOutlineDataSource { private extension SidebarOutlineDataSource {
func nodeForItem(_ item: AnyObject?) -> Node { func nodeForItem(_ item: Any?) -> Node {
if item == nil { if item == nil {
return treeController.rootNode return treeController.rootNode
} }
@ -82,10 +100,10 @@ private extension SidebarOutlineDataSource {
return node.representedObject is Feed return node.representedObject is Feed
} }
func validateLocalDrop(_ info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { func validateLocalDrop(_ draggedFeeds: Set<DraggedFeed>, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
// let parentNode = nodeForItem(item)
// let node = nodeForItem(item)
return SidebarOutlineDataSource.dragOperationNone return SidebarOutlineDataSource.dragOperationNone
} }
} }