Merge branch 'mac-candidate'
This commit is contained in:
commit
314fd970d1
@ -43,7 +43,6 @@ protocol SidebarDelegate: class {
|
|||||||
// MARK: - NSViewController
|
// MARK: - NSViewController
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
|
||||||
sidebarCellAppearance = SidebarCellAppearance(fontSize: AppDefaults.sidebarFontSize)
|
sidebarCellAppearance = SidebarCellAppearance(fontSize: AppDefaults.sidebarFontSize)
|
||||||
|
|
||||||
outlineView.dataSource = dataSource
|
outlineView.dataSource = dataSource
|
||||||
@ -78,30 +77,9 @@ protocol SidebarDelegate: class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: State Restoration
|
|
||||||
|
|
||||||
// private static let stateRestorationSelectedRowIndexes = "selectedRowIndexes"
|
|
||||||
//
|
|
||||||
// override func encodeRestorableState(with coder: NSCoder) {
|
|
||||||
//
|
|
||||||
// super.encodeRestorableState(with: coder)
|
|
||||||
//
|
|
||||||
// coder.encode(outlineView.selectedRowIndexes, forKey: SidebarViewController.stateRestorationSelectedRowIndexes)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override func restoreState(with coder: NSCoder) {
|
|
||||||
//
|
|
||||||
// super.restoreState(with: coder)
|
|
||||||
//
|
|
||||||
// if let restoredRowIndexes = coder.decodeObject(of: [NSIndexSet.self], forKey: SidebarViewController.stateRestorationSelectedRowIndexes) as? IndexSet {
|
|
||||||
// outlineView.selectRowIndexes(restoredRowIndexes, byExtendingSelection: false)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MARK: - Notifications
|
// MARK: - Notifications
|
||||||
|
|
||||||
@objc func unreadCountDidChange(_ note: Notification) {
|
@objc func unreadCountDidChange(_ note: Notification) {
|
||||||
|
|
||||||
guard let representedObject = note.object else {
|
guard let representedObject = note.object else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -125,7 +103,6 @@ protocol SidebarDelegate: class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func userDidAddFeed(_ notification: Notification) {
|
@objc func userDidAddFeed(_ notification: Notification) {
|
||||||
|
|
||||||
guard let feed = notification.userInfo?[UserInfoKey.feed] else {
|
guard let feed = notification.userInfo?[UserInfoKey.feed] else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -133,12 +110,10 @@ protocol SidebarDelegate: class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func faviconDidBecomeAvailable(_ note: Notification) {
|
@objc func faviconDidBecomeAvailable(_ note: Notification) {
|
||||||
|
|
||||||
applyToAvailableCells(configureFavicon)
|
applyToAvailableCells(configureFavicon)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func feedSettingDidChange(_ note: Notification) {
|
@objc func feedSettingDidChange(_ note: Notification) {
|
||||||
|
|
||||||
guard let feed = note.object as? Feed, let key = note.userInfo?[Feed.FeedSettingUserInfoKey] as? String else {
|
guard let feed = note.object as? Feed, let key = note.userInfo?[Feed.FeedSettingUserInfoKey] as? String else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -148,7 +123,6 @@ protocol SidebarDelegate: class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func displayNameDidChange(_ note: Notification) {
|
@objc func displayNameDidChange(_ note: Notification) {
|
||||||
|
|
||||||
guard let object = note.object else {
|
guard let object = note.object else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -159,7 +133,6 @@ protocol SidebarDelegate: class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func userDidRequestSidebarSelection(_ note: Notification) {
|
@objc func userDidRequestSidebarSelection(_ note: Notification) {
|
||||||
|
|
||||||
guard let feed = note.userInfo?[UserInfoKey.feed] else {
|
guard let feed = note.userInfo?[UserInfoKey.feed] else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -175,7 +148,6 @@ protocol SidebarDelegate: class {
|
|||||||
// MARK: - Actions
|
// MARK: - Actions
|
||||||
|
|
||||||
@IBAction func delete(_ sender: AnyObject?) {
|
@IBAction func delete(_ sender: AnyObject?) {
|
||||||
|
|
||||||
if outlineView.selectionIsEmpty {
|
if outlineView.selectionIsEmpty {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -183,7 +155,6 @@ protocol SidebarDelegate: class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func openInBrowser(_ sender: Any?) {
|
@IBAction func openInBrowser(_ sender: Any?) {
|
||||||
|
|
||||||
guard let feed = singleSelectedFeed, let homePageURL = feed.homePageURL else {
|
guard let feed = singleSelectedFeed, let homePageURL = feed.homePageURL else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -191,29 +162,24 @@ protocol SidebarDelegate: class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func gotoToday(_ sender: Any?) {
|
@IBAction func gotoToday(_ sender: Any?) {
|
||||||
|
|
||||||
outlineView.revealAndSelectRepresentedObject(SmartFeedsController.shared.todayFeed, treeController)
|
outlineView.revealAndSelectRepresentedObject(SmartFeedsController.shared.todayFeed, treeController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func gotoAllUnread(_ sender: Any?) {
|
@IBAction func gotoAllUnread(_ sender: Any?) {
|
||||||
|
|
||||||
outlineView.revealAndSelectRepresentedObject(SmartFeedsController.shared.unreadFeed, treeController)
|
outlineView.revealAndSelectRepresentedObject(SmartFeedsController.shared.unreadFeed, treeController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func gotoStarred(_ sender: Any?) {
|
@IBAction func gotoStarred(_ sender: Any?) {
|
||||||
|
|
||||||
outlineView.revealAndSelectRepresentedObject(SmartFeedsController.shared.starredFeed, treeController)
|
outlineView.revealAndSelectRepresentedObject(SmartFeedsController.shared.starredFeed, treeController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func copy(_ sender: Any?) {
|
@IBAction func copy(_ sender: Any?) {
|
||||||
|
|
||||||
NSPasteboard.general.copyObjects(selectedObjects)
|
NSPasteboard.general.copyObjects(selectedObjects)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Navigation
|
// MARK: - Navigation
|
||||||
|
|
||||||
func canGoToNextUnread() -> Bool {
|
func canGoToNextUnread() -> Bool {
|
||||||
|
|
||||||
if let _ = nextSelectableRowWithUnreadArticle() {
|
if let _ = nextSelectableRowWithUnreadArticle() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -221,7 +187,6 @@ protocol SidebarDelegate: class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func goToNextUnread() {
|
func goToNextUnread() {
|
||||||
|
|
||||||
guard let row = nextSelectableRowWithUnreadArticle() else {
|
guard let row = nextSelectableRowWithUnreadArticle() else {
|
||||||
assertionFailure("goToNextUnread called before checking if there is a next unread.")
|
assertionFailure("goToNextUnread called before checking if there is a next unread.")
|
||||||
return
|
return
|
||||||
@ -230,26 +195,19 @@ protocol SidebarDelegate: class {
|
|||||||
NSCursor.setHiddenUntilMouseMoves(true)
|
NSCursor.setHiddenUntilMouseMoves(true)
|
||||||
outlineView.selectRowIndexes(IndexSet([row]), byExtendingSelection: false)
|
outlineView.selectRowIndexes(IndexSet([row]), byExtendingSelection: false)
|
||||||
outlineView.scrollTo(row: row)
|
outlineView.scrollTo(row: row)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func focus() {
|
func focus() {
|
||||||
|
outlineView.window?.makeFirstResponderUnlessDescendantIsFirstResponder(outlineView)
|
||||||
guard let window = outlineView.window else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
window.makeFirstResponderUnlessDescendantIsFirstResponder(outlineView)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Contextual Menu
|
// MARK: - Contextual Menu
|
||||||
|
|
||||||
func contextualMenuForSelectedObjects() -> NSMenu? {
|
func contextualMenuForSelectedObjects() -> NSMenu? {
|
||||||
|
|
||||||
return menu(for: selectedObjects)
|
return menu(for: selectedObjects)
|
||||||
}
|
}
|
||||||
|
|
||||||
func contextualMenuForClickedRows() -> NSMenu? {
|
func contextualMenuForClickedRows() -> NSMenu? {
|
||||||
|
|
||||||
let row = outlineView.clickedRow
|
let row = outlineView.clickedRow
|
||||||
guard row != -1, let node = nodeForRow(row) else {
|
guard row != -1, let node = nodeForRow(row) else {
|
||||||
return nil
|
return nil
|
||||||
@ -264,7 +222,7 @@ protocol SidebarDelegate: class {
|
|||||||
return menu(for: [object])
|
return menu(for: [object])
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: NSMenuDelegate
|
// MARK: - NSMenuDelegate
|
||||||
|
|
||||||
public func menuNeedsUpdate(_ menu: NSMenu) {
|
public func menuNeedsUpdate(_ menu: NSMenu) {
|
||||||
menu.removeAllItems()
|
menu.removeAllItems()
|
||||||
@ -278,7 +236,6 @@ protocol SidebarDelegate: class {
|
|||||||
// MARK: - NSOutlineViewDelegate
|
// MARK: - NSOutlineViewDelegate
|
||||||
|
|
||||||
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
|
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
|
||||||
|
|
||||||
let node = item as! Node
|
let node = item as! Node
|
||||||
|
|
||||||
if node.isGroupItem {
|
if node.isGroupItem {
|
||||||
@ -294,13 +251,11 @@ protocol SidebarDelegate: class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool {
|
func outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool {
|
||||||
|
|
||||||
let node = item as! Node
|
let node = item as! Node
|
||||||
return node.isGroupItem
|
return node.isGroupItem
|
||||||
}
|
}
|
||||||
|
|
||||||
func outlineView(_ outlineView: NSOutlineView, selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet) -> IndexSet {
|
func outlineView(_ outlineView: NSOutlineView, selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet) -> IndexSet {
|
||||||
|
|
||||||
// Don’t allow selecting group items.
|
// Don’t allow selecting group items.
|
||||||
// If any index in IndexSet contains a group item,
|
// If any index in IndexSet contains a group item,
|
||||||
// return the current selection (not a modified version of the proposed selection).
|
// return the current selection (not a modified version of the proposed selection).
|
||||||
@ -315,19 +270,16 @@ protocol SidebarDelegate: class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func outlineView(_ outlineView: NSOutlineView, shouldSelectItem item: Any) -> Bool {
|
func outlineView(_ outlineView: NSOutlineView, shouldSelectItem item: Any) -> Bool {
|
||||||
|
|
||||||
return !self.outlineView(outlineView, isGroupItem: item)
|
return !self.outlineView(outlineView, isGroupItem: item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func outlineViewSelectionDidChange(_ notification: Notification) {
|
func outlineViewSelectionDidChange(_ notification: Notification) {
|
||||||
selectionDidChange(selectedObjects.isEmpty ? nil : selectedObjects)
|
selectionDidChange(selectedObjects.isEmpty ? nil : selectedObjects)
|
||||||
// self.invalidateRestorableState()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Node Manipulation
|
//MARK: - Node Manipulation
|
||||||
|
|
||||||
func deleteNodes(_ nodes: [Node]) {
|
func deleteNodes(_ nodes: [Node]) {
|
||||||
|
|
||||||
let nodesToDelete = treeController.normalizedSelectedNodes(nodes)
|
let nodesToDelete = treeController.normalizedSelectedNodes(nodes)
|
||||||
|
|
||||||
guard let undoManager = undoManager, let deleteCommand = DeleteCommand(nodesToDelete: nodesToDelete, treeController: treeController, undoManager: undoManager, errorHandler: ErrorHandler.present) else {
|
guard let undoManager = undoManager, let deleteCommand = DeleteCommand(nodesToDelete: nodesToDelete, treeController: treeController, undoManager: undoManager, errorHandler: ErrorHandler.present) else {
|
||||||
@ -374,7 +326,6 @@ protocol SidebarDelegate: class {
|
|||||||
extension SidebarViewController: NSUserInterfaceValidations {
|
extension SidebarViewController: NSUserInterfaceValidations {
|
||||||
|
|
||||||
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||||
|
|
||||||
if item.action == #selector(copy(_:)) {
|
if item.action == #selector(copy(_:)) {
|
||||||
return NSPasteboard.general.canCopyAtLeastOneObject(selectedObjects)
|
return NSPasteboard.general.canCopyAtLeastOneObject(selectedObjects)
|
||||||
}
|
}
|
||||||
@ -412,7 +363,6 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func rebuildTreeAndReloadDataIfNeeded() {
|
func rebuildTreeAndReloadDataIfNeeded() {
|
||||||
|
|
||||||
if !animatingChanges && !BatchUpdate.shared.isPerforming {
|
if !animatingChanges && !BatchUpdate.shared.isPerforming {
|
||||||
treeController.rebuild()
|
treeController.rebuild()
|
||||||
outlineView.reloadData()
|
outlineView.reloadData()
|
||||||
@ -444,7 +394,6 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateUnreadCounts(for objects: [AnyObject]) {
|
func updateUnreadCounts(for objects: [AnyObject]) {
|
||||||
|
|
||||||
// On selection, update unread counts for folders and feeds.
|
// On selection, update unread counts for folders and feeds.
|
||||||
// For feeds, actually fetch from database.
|
// For feeds, actually fetch from database.
|
||||||
|
|
||||||
@ -459,7 +408,6 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func nodeForItem(_ item: AnyObject?) -> Node {
|
func nodeForItem(_ item: AnyObject?) -> Node {
|
||||||
|
|
||||||
if item == nil {
|
if item == nil {
|
||||||
return treeController.rootNode
|
return treeController.rootNode
|
||||||
}
|
}
|
||||||
@ -467,7 +415,6 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func nodeForRow(_ row: Int) -> Node? {
|
func nodeForRow(_ row: Int) -> Node? {
|
||||||
|
|
||||||
if row < 0 || row >= outlineView.numberOfRows {
|
if row < 0 || row >= outlineView.numberOfRows {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -479,7 +426,6 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func rowHasAtLeastOneUnreadArticle(_ row: Int) -> Bool {
|
func rowHasAtLeastOneUnreadArticle(_ row: Int) -> Bool {
|
||||||
|
|
||||||
if let oneNode = nodeForRow(row) {
|
if let oneNode = nodeForRow(row) {
|
||||||
if let unreadCountProvider = oneNode.representedObject as? UnreadCountProvider {
|
if let unreadCountProvider = oneNode.representedObject as? UnreadCountProvider {
|
||||||
if unreadCountProvider.unreadCount > 0 {
|
if unreadCountProvider.unreadCount > 0 {
|
||||||
@ -491,7 +437,6 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func rowIsGroupItem(_ row: Int) -> Bool {
|
func rowIsGroupItem(_ row: Int) -> Bool {
|
||||||
|
|
||||||
if let node = nodeForRow(row), outlineView.isGroupItem(node) {
|
if let node = nodeForRow(row), outlineView.isGroupItem(node) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -499,7 +444,6 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func nextSelectableRowWithUnreadArticle() -> Int? {
|
func nextSelectableRowWithUnreadArticle() -> Int? {
|
||||||
|
|
||||||
// Skip group items, because they should never be selected.
|
// Skip group items, because they should never be selected.
|
||||||
|
|
||||||
let selectedRow = outlineView.selectedRow
|
let selectedRow = outlineView.selectedRow
|
||||||
@ -533,12 +477,10 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func configureUnreadCount(_ cell: SidebarCell, _ node: Node) {
|
func configureUnreadCount(_ cell: SidebarCell, _ node: Node) {
|
||||||
|
|
||||||
cell.unreadCount = unreadCountFor(node)
|
cell.unreadCount = unreadCountFor(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureFavicon(_ cell: SidebarCell, _ node: Node) {
|
func configureFavicon(_ cell: SidebarCell, _ node: Node) {
|
||||||
|
|
||||||
cell.image = imageFor(node)
|
cell.image = imageFor(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,7 +489,6 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func imageFor(_ node: Node) -> NSImage? {
|
func imageFor(_ node: Node) -> NSImage? {
|
||||||
|
|
||||||
if let smallIconProvider = node.representedObject as? SmallIconProvider {
|
if let smallIconProvider = node.representedObject as? SmallIconProvider {
|
||||||
return smallIconProvider.smallIcon
|
return smallIconProvider.smallIcon
|
||||||
}
|
}
|
||||||
@ -555,7 +496,6 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func nameFor(_ node: Node) -> String {
|
func nameFor(_ node: Node) -> String {
|
||||||
|
|
||||||
if let displayNameProvider = node.representedObject as? DisplayNameProvider {
|
if let displayNameProvider = node.representedObject as? DisplayNameProvider {
|
||||||
return displayNameProvider.nameForDisplay
|
return displayNameProvider.nameForDisplay
|
||||||
}
|
}
|
||||||
@ -563,7 +503,6 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func unreadCountFor(_ node: Node) -> Int {
|
func unreadCountFor(_ node: Node) -> Int {
|
||||||
|
|
||||||
if let unreadCountProvider = node.representedObject as? UnreadCountProvider {
|
if let unreadCountProvider = node.representedObject as? UnreadCountProvider {
|
||||||
return unreadCountProvider.unreadCount
|
return unreadCountProvider.unreadCount
|
||||||
}
|
}
|
||||||
@ -571,14 +510,11 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cellForRowView(_ rowView: NSTableRowView) -> SidebarCell? {
|
func cellForRowView(_ rowView: NSTableRowView) -> SidebarCell? {
|
||||||
|
|
||||||
return rowView.view(atColumn: 0) as? SidebarCell
|
return rowView.view(atColumn: 0) as? SidebarCell
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyToAvailableCells(_ callback: (SidebarCell, Node) -> Void) {
|
func applyToAvailableCells(_ callback: (SidebarCell, Node) -> Void) {
|
||||||
|
|
||||||
outlineView.enumerateAvailableRowViews { (rowView: NSTableRowView, row: Int) -> Void in
|
outlineView.enumerateAvailableRowViews { (rowView: NSTableRowView, row: Int) -> Void in
|
||||||
|
|
||||||
guard let cell = cellForRowView(rowView), let node = nodeForRow(row) else {
|
guard let cell = cellForRowView(rowView), let node = nodeForRow(row) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -595,23 +531,19 @@ private extension SidebarViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func configureCellsForRepresentedObject(_ representedObject: AnyObject) {
|
func configureCellsForRepresentedObject(_ representedObject: AnyObject) {
|
||||||
|
|
||||||
applyToCellsForRepresentedObject(representedObject, configure)
|
applyToCellsForRepresentedObject(representedObject, configure)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureUnreadCountForCellsForRepresentedObject(_ representedObject: AnyObject) {
|
func configureUnreadCountForCellsForRepresentedObject(_ representedObject: AnyObject) {
|
||||||
|
|
||||||
applyToCellsForRepresentedObject(representedObject, configureUnreadCount)
|
applyToCellsForRepresentedObject(representedObject, configureUnreadCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func revealAndSelectRepresentedObject(_ representedObject: AnyObject) -> Bool {
|
func revealAndSelectRepresentedObject(_ representedObject: AnyObject) -> Bool {
|
||||||
|
|
||||||
return outlineView.revealAndSelectRepresentedObject(representedObject, treeController)
|
return outlineView.revealAndSelectRepresentedObject(representedObject, treeController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private extension Node {
|
private extension Node {
|
||||||
|
|
||||||
func representsSidebarObject(_ object: AnyObject) -> Bool {
|
func representsSidebarObject(_ object: AnyObject) -> Bool {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user