From 089ffbd2992ba48c0f4c20f35d67ead6475cf05d Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Wed, 19 Sep 2018 21:49:13 -0700 Subject: [PATCH] Make progress on drag and drop. Reads feeds from pasteboard. This needs refactoring, though. --- .../Sidebar/FeedPasteboardWriter.swift | 79 +++++++++++++++---- .../Sidebar/SidebarOutlineDataSource.swift | 38 ++++++--- 2 files changed, 92 insertions(+), 25 deletions(-) diff --git a/NetNewsWire/MainWindow/Sidebar/FeedPasteboardWriter.swift b/NetNewsWire/MainWindow/Sidebar/FeedPasteboardWriter.swift index df2155f42..92390f0c5 100644 --- a/NetNewsWire/MainWindow/Sidebar/FeedPasteboardWriter.swift +++ b/NetNewsWire/MainWindow/Sidebar/FeedPasteboardWriter.swift @@ -26,6 +26,18 @@ extension Feed: PasteboardWriterOwner { static let feedUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.feed" 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) { self.feed = feed @@ -57,23 +69,20 @@ extension Feed: PasteboardWriterOwner { return plist } + + // MARK: - Dragged Feed + + static func draggedFeeds(with pasteboard: NSPasteboard) -> Set? { + 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 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] { var d = [String: String]() @@ -100,11 +109,51 @@ private extension FeedPasteboardWriter { if let editedName = feed.editedName { d[Key.editedName] = editedName } - if feed.unreadCount > 0 { - d[Key.unreadCount] = feed.unreadCount + if let accountID = feed.account?.accountID { + d[Key.accountID] = accountID } 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? +} diff --git a/NetNewsWire/MainWindow/Sidebar/SidebarOutlineDataSource.swift b/NetNewsWire/MainWindow/Sidebar/SidebarOutlineDataSource.swift index 137021972..13bb5de72 100644 --- a/NetNewsWire/MainWindow/Sidebar/SidebarOutlineDataSource.swift +++ b/NetNewsWire/MainWindow/Sidebar/SidebarOutlineDataSource.swift @@ -25,21 +25,21 @@ import Account 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 { - return nodeForItem(item as AnyObject?).childNodes[index] + return nodeForItem(item).childNodes[index] } 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? { - let node = nodeForItem(item as AnyObject?) + let node = nodeForItem(item) guard nodeRepresentsDraggableItem(node) else { return nil } @@ -49,10 +49,29 @@ import Account // MARK: - Drag and Drop 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 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 { - return validateLocalDrop(info, proposedItem: item, proposedChildIndex: index) + return validateLocalDrop(draggedFeeds, proposedItem: item, proposedChildIndex: index) } return SidebarOutlineDataSource.dragOperationNone } @@ -66,8 +85,7 @@ import Account private extension SidebarOutlineDataSource { - func nodeForItem(_ item: AnyObject?) -> Node { - + func nodeForItem(_ item: Any?) -> Node { if item == nil { return treeController.rootNode } @@ -82,10 +100,10 @@ private extension SidebarOutlineDataSource { return node.representedObject is Feed } - func validateLocalDrop(_ info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { + func validateLocalDrop(_ draggedFeeds: Set, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { + +// let parentNode = nodeForItem(item) -// let node = nodeForItem(item) return SidebarOutlineDataSource.dragOperationNone - } }