From 23ae585b128061d989aaa7464df91f417c18c223 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sat, 18 Nov 2017 16:56:36 -0800 Subject: [PATCH] Add a group-style row for accounts. --- Evergreen/Extensions/Node-Extensions.swift | 19 ++++++- .../FeedListTreeControllerDelegate.swift | 2 +- .../FolderTreeControllerDelegate.swift | 2 +- .../Sidebar/SidebarOutlineView.swift | 17 ++++++- .../SidebarTreeControllerDelegate.swift | 49 ++++++++++++++----- .../Sidebar/SidebarViewController.swift | 22 ++++++++- Frameworks/RSTree/RSTree/Node.swift | 8 ++- 7 files changed, 99 insertions(+), 20 deletions(-) diff --git a/Evergreen/Extensions/Node-Extensions.swift b/Evergreen/Extensions/Node-Extensions.swift index e7445fe35..362250c54 100644 --- a/Evergreen/Extensions/Node-Extensions.swift +++ b/Evergreen/Extensions/Node-Extensions.swift @@ -11,8 +11,21 @@ import RSTree import Data import RSCore -extension Node { - +extension Array where Element == Node { + + func sortedAlphabetically() -> [Node] { + + return Node.nodesSortedAlphabetically(self) + } + + func sortedAlphabeticallyWithFoldersAtEnd() -> [Node] { + + return Node.nodesSortedAlphabeticallyWithFoldersAtEnd(self) + } +} + +private extension Node { + class func nodesSortedAlphabetically(_ nodes: [Node]) -> [Node] { return nodes.sorted { (node1, node2) -> Bool in @@ -50,3 +63,5 @@ extension Node { } } } + + diff --git a/Evergreen/FeedList/FeedListTreeControllerDelegate.swift b/Evergreen/FeedList/FeedListTreeControllerDelegate.swift index d4000220d..6e356dacf 100644 --- a/Evergreen/FeedList/FeedListTreeControllerDelegate.swift +++ b/Evergreen/FeedList/FeedListTreeControllerDelegate.swift @@ -61,7 +61,7 @@ private extension FeedListTreeControllerDelegate { func childNodesForContainerNode(_ containerNode: Node, _ children: [AnyObject]) -> [Node]? { let nodes = unsortedNodes(parent: containerNode, children: children) - return Node.nodesSortedAlphabeticallyWithFoldersAtEnd(nodes) + return nodes.sortedAlphabeticallyWithFoldersAtEnd() } func unsortedNodes(parent: Node, children: [AnyObject]) -> [Node] { diff --git a/Evergreen/MainWindow/AddFeed/FolderTreeControllerDelegate.swift b/Evergreen/MainWindow/AddFeed/FolderTreeControllerDelegate.swift index b6a3f6604..471d441ec 100644 --- a/Evergreen/MainWindow/AddFeed/FolderTreeControllerDelegate.swift +++ b/Evergreen/MainWindow/AddFeed/FolderTreeControllerDelegate.swift @@ -36,7 +36,7 @@ private extension FolderTreeControllerDelegate { } } - return Node.nodesSortedAlphabetically(folderNodes) + return folderNodes.sortedAlphabetically() } func createNode(_ folder: Folder, parent: Node) -> Node { diff --git a/Evergreen/MainWindow/Sidebar/SidebarOutlineView.swift b/Evergreen/MainWindow/Sidebar/SidebarOutlineView.swift index e8e2dbf2f..e51f6aea6 100644 --- a/Evergreen/MainWindow/Sidebar/SidebarOutlineView.swift +++ b/Evergreen/MainWindow/Sidebar/SidebarOutlineView.swift @@ -8,6 +8,7 @@ import AppKit import RSCore +import RSTree class SidebarOutlineView : NSOutlineView { @@ -56,7 +57,21 @@ class SidebarOutlineView : NSOutlineView { super.keyDown(with: event) } } - + + override func frameOfCell(atColumn column: Int, row: Int) -> NSRect { + + var frame = super.frameOfCell(atColumn: column, row: row) + + let node = item(atRow: row) as! Node + if node.isGroupItem { + return frame + } + + frame.origin.x -= indentationPerLevel + frame.size.width += indentationPerLevel + return frame + } + override func viewWillStartLiveResize() { if let scrollView = self.enclosingScrollView { diff --git a/Evergreen/MainWindow/Sidebar/SidebarTreeControllerDelegate.swift b/Evergreen/MainWindow/Sidebar/SidebarTreeControllerDelegate.swift index 8c19aba7f..8d6027adb 100644 --- a/Evergreen/MainWindow/Sidebar/SidebarTreeControllerDelegate.swift +++ b/Evergreen/MainWindow/Sidebar/SidebarTreeControllerDelegate.swift @@ -21,6 +21,9 @@ final class SidebarTreeControllerDelegate: TreeControllerDelegate { if node.representedObject is Folder { return childNodesForFolderNode(node) } + if node.representedObject is Account { + return childNodesForAccount(node) + } return nil } @@ -29,12 +32,17 @@ final class SidebarTreeControllerDelegate: TreeControllerDelegate { private extension SidebarTreeControllerDelegate { func childNodesForRootNode(_ rootNode: Node) -> [Node]? { - - // The child nodes are the top-level items of the local Account. - // This will be expanded later to add synthetic feeds (All Unread, for instance) - // and other accounts. - return childNodesForContainerNode(rootNode, AccountManager.shared.localAccount.children) + // The top-level nodes are pseudo-feeds (All Unread, Starred, etc.) and accounts. + // TODO: pseudo-feeds + + return sortedAccountNodes(rootNode) + } + + func childNodesForAccount(_ accountNode: Node) -> [Node]? { + + let account = accountNode.representedObject as! Account + return childNodesForContainerNode(accountNode, account.children) } func childNodesForFolderNode(_ folderNode: Node) -> [Node]? { @@ -61,8 +69,7 @@ private extension SidebarTreeControllerDelegate { } } - updatedChildNodes = Node.nodesSortedAlphabeticallyWithFoldersAtEnd(updatedChildNodes) - return updatedChildNodes + return updatedChildNodes.sortedAlphabeticallyWithFoldersAtEnd() } func createNode(representedObject: Any, parent: Node) -> Node? { @@ -73,21 +80,39 @@ private extension SidebarTreeControllerDelegate { if let folder = representedObject as? Folder { return createNode(folder: folder, parent: parent) } + if let account = representedObject as? Account { + return createNode(account: account, parent: parent) + } + return nil } func createNode(feed: Feed, parent: Node) -> Node { - - return Node(representedObject: feed, parent: parent) + + return parent.createChildNode(feed) } func createNode(folder: Folder, parent: Node) -> Node { - - let node = Node(representedObject: folder, parent: parent) + + let node = parent.createChildNode(folder) node.canHaveChildNodes = true return node } - + + func createNode(account: Account, parent: Node) -> Node { + + let node = parent.createChildNode(account) + node.canHaveChildNodes = true + node.isGroupItem = true + return node + } + + func sortedAccountNodes(_ parent: Node) -> [Node] { + + let nodes = AccountManager.shared.accounts.map { createNode(account: $0, parent: parent) } + return nodes.sortedAlphabetically() + } + func nodeInArrayRepresentingObject(_ nodes: [Node], _ representedObject: AnyObject) -> Node? { for oneNode in nodes { diff --git a/Evergreen/MainWindow/Sidebar/SidebarViewController.swift b/Evergreen/MainWindow/Sidebar/SidebarViewController.swift index 669e75995..3b9c8e9a1 100644 --- a/Evergreen/MainWindow/Sidebar/SidebarViewController.swift +++ b/Evergreen/MainWindow/Sidebar/SidebarViewController.swift @@ -118,14 +118,26 @@ import RSCore func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { - let cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "DataCell"), owner: self) as! SidebarCell - let node = item as! Node + + if node.isGroupItem { + let cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "HeaderCell"), owner: self) as! NSTableCellView + configureGroupCell(cell, node) + return cell + } + + let cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "DataCell"), owner: self) as! SidebarCell configure(cell, node) return cell } + func outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool { + + let node = item as! Node + return node.isGroupItem + } + func outlineViewSelectionDidChange(_ notification: Notification) { // TODO: support multiple selection @@ -283,6 +295,12 @@ private extension SidebarViewController { cell.image = imageFor(node) } + func configureGroupCell(_ cell: NSTableCellView, _ node: Node) { + + cell.objectValue = node + cell.textField?.stringValue = nameFor(node) + } + func imageFor(_ node: Node) -> NSImage? { return nil diff --git a/Frameworks/RSTree/RSTree/Node.swift b/Frameworks/RSTree/RSTree/Node.swift index c97649080..4b40710a0 100644 --- a/Frameworks/RSTree/RSTree/Node.swift +++ b/Frameworks/RSTree/RSTree/Node.swift @@ -71,7 +71,13 @@ public final class Node: Equatable { node.canHaveChildNodes = true return node } - + + public func createChildNode(_ representedObject: AnyObject) -> Node { + + // Just creates — doesn’t add it. + return Node(representedObject: representedObject, parent: self) + } + public func childAtIndex(_ index: Int) -> Node? { guard let childNodes = childNodes else {