mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2025-02-03 12:27:32 +01:00
Do some refactoring.
This commit is contained in:
parent
af3f41fbda
commit
911e6b0879
@ -87,6 +87,7 @@
|
||||
84F204CE1FAACB660076E152 /* FeedListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204CD1FAACB660076E152 /* FeedListViewController.swift */; };
|
||||
84F204DE1FAACB8B0076E152 /* FeedListTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DD1FAACB8B0076E152 /* FeedListTimelineViewController.swift */; };
|
||||
84F204E01FAACBB30076E152 /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; };
|
||||
84F204E21FAAD4750076E152 /* TimelineTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204E11FAAD4750076E152 /* TimelineTableViewDelegate.swift */; };
|
||||
84FB3A6F1FA6612C00EFC320 /* TimelineTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FB3A6E1FA6612C00EFC320 /* TimelineTableViewDataSource.swift */; };
|
||||
84FB9A2F1EDCD6C4003D53B9 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; };
|
||||
84FB9A301EDCD6C4003D53B9 /* Sparkle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
@ -453,6 +454,7 @@
|
||||
84F204CD1FAACB660076E152 /* FeedListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListViewController.swift; sourceTree = "<group>"; };
|
||||
84F204DD1FAACB8B0076E152 /* FeedListTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListTimelineViewController.swift; sourceTree = "<group>"; };
|
||||
84F204DF1FAACBB30076E152 /* ArticleArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleArray.swift; sourceTree = "<group>"; };
|
||||
84F204E11FAAD4750076E152 /* TimelineTableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewDelegate.swift; sourceTree = "<group>"; };
|
||||
84FB3A6E1FA6612C00EFC320 /* TimelineTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewDataSource.swift; sourceTree = "<group>"; };
|
||||
84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Vendor/Sparkle.framework; sourceTree = SOURCE_ROOT; };
|
||||
/* End PBXFileReference section */
|
||||
@ -589,6 +591,7 @@
|
||||
849A976B1ED9EBC8007D329B /* TimelineViewController.swift */,
|
||||
84F204DF1FAACBB30076E152 /* ArticleArray.swift */,
|
||||
84FB3A6E1FA6612C00EFC320 /* TimelineTableViewDataSource.swift */,
|
||||
84F204E11FAAD4750076E152 /* TimelineTableViewDelegate.swift */,
|
||||
849A97691ED9EBC8007D329B /* TimelineTableRowView.swift */,
|
||||
849A976A1ED9EBC8007D329B /* TimelineTableView.swift */,
|
||||
849A976F1ED9EC04007D329B /* Cell */,
|
||||
@ -1220,6 +1223,7 @@
|
||||
849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */,
|
||||
849A97921ED9EF65007D329B /* IndeterminateProgressWindowController.swift in Sources */,
|
||||
849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */,
|
||||
84F204E21FAAD4750076E152 /* TimelineTableViewDelegate.swift in Sources */,
|
||||
849A976E1ED9EBC8007D329B /* TimelineViewController.swift in Sources */,
|
||||
849A978D1ED9EE4D007D329B /* FeedListWindowController.swift in Sources */,
|
||||
849A97771ED9EC04007D329B /* TimelineCellData.swift in Sources */,
|
||||
|
@ -9,6 +9,8 @@
|
||||
import Foundation
|
||||
import Data
|
||||
|
||||
typealias ArticleArray = [Article]
|
||||
|
||||
extension Array where Element == Article {
|
||||
|
||||
func articleAtRow(_ row: Int) -> Article? {
|
||||
@ -19,4 +21,86 @@ extension Array where Element == Article {
|
||||
return self[row]
|
||||
}
|
||||
|
||||
func rowOfNextUnreadArticle(_ selectedRow: Int) -> Int? {
|
||||
|
||||
if isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
var rowIndex = selectedRow
|
||||
while(true) {
|
||||
|
||||
rowIndex = rowIndex + 1
|
||||
if rowIndex >= count {
|
||||
break
|
||||
}
|
||||
let article = articleAtRow(rowIndex)!
|
||||
if !article.status.read {
|
||||
return rowIndex
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func articlesForIndexes(_ indexes: IndexSet) -> Set<Article> {
|
||||
|
||||
return Set(indexes.flatMap{ (oneIndex) -> Article? in
|
||||
return articleAtRow(oneIndex)
|
||||
})
|
||||
}
|
||||
|
||||
func indexesForArticleIDs(_ articleIDs: Set<String>) -> IndexSet {
|
||||
|
||||
var indexes = IndexSet()
|
||||
|
||||
articleIDs.forEach { (articleID) in
|
||||
let oneIndex = rowForArticleID(articleID)
|
||||
if oneIndex != NSNotFound {
|
||||
indexes.insert(oneIndex)
|
||||
}
|
||||
}
|
||||
|
||||
return indexes
|
||||
}
|
||||
|
||||
func sortedByDate() -> ArticleArray {
|
||||
|
||||
return sorted(by: articleComparator)
|
||||
}
|
||||
|
||||
func canMarkAllAsRead() -> Bool {
|
||||
|
||||
for article in self {
|
||||
if !article.status.read {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private extension Array where Element == Article {
|
||||
|
||||
func rowForArticleID(_ articleID: String) -> Int {
|
||||
|
||||
if let index = index(where: { $0.articleID == articleID }) {
|
||||
return index
|
||||
}
|
||||
|
||||
return NSNotFound
|
||||
}
|
||||
|
||||
func rowForArticle(_ article: Article) -> Int {
|
||||
|
||||
return rowForArticleID(article.articleID)
|
||||
}
|
||||
|
||||
// MARK: Sorting
|
||||
|
||||
func articleComparator(_ article1: Article, article2: Article) -> Bool {
|
||||
|
||||
return article1.logicalDatePublished > article2.logicalDatePublished
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,4 +41,16 @@ class TimelineTableView: NSTableView {
|
||||
super.viewDidEndLiveResize()
|
||||
}
|
||||
|
||||
func redrawGrid() {
|
||||
|
||||
guard let rowViews = visibleRowViews() else {
|
||||
return
|
||||
}
|
||||
|
||||
rowViews.forEach{ (rowView) in
|
||||
if let rowView = rowView as? TimelineTableRowView {
|
||||
rowView.invalidateGridRect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,12 +21,12 @@ import Cocoa
|
||||
|
||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
|
||||
return timelineViewController?.numberOfArticles ?? 0
|
||||
return timelineViewController?.articles.count ?? 0
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
|
||||
|
||||
return timelineViewController?.articleAtRow(row) ?? nil
|
||||
return timelineViewController?.articles.articleAtRow(row) ?? nil
|
||||
}
|
||||
|
||||
}
|
||||
|
112
Evergreen/MainWindow/Timeline/TimelineTableViewDelegate.swift
Normal file
112
Evergreen/MainWindow/Timeline/TimelineTableViewDelegate.swift
Normal file
@ -0,0 +1,112 @@
|
||||
//
|
||||
// TimelineTableViewDelegate.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 11/1/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import Data
|
||||
|
||||
@objc final class TimelineTableViewDelegate: NSObject, NSTableViewDelegate {
|
||||
|
||||
private weak var timelineViewController: TimelineViewController?
|
||||
|
||||
init(timelineViewController: TimelineViewController) {
|
||||
|
||||
self.timelineViewController = timelineViewController
|
||||
}
|
||||
|
||||
// MARK: NSTableViewDelegate
|
||||
|
||||
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
|
||||
|
||||
guard let timelineViewController = timelineViewController else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let rowView: TimelineTableRowView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "timelineRow"), owner: self) as! TimelineTableRowView
|
||||
rowView.cellAppearance = timelineViewController.cellAppearance
|
||||
return rowView
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
|
||||
guard let timelineViewController = timelineViewController else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let cell: TimelineTableCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "timelineCell"), owner: self) as! TimelineTableCellView
|
||||
cell.cellAppearance = timelineViewController.cellAppearance
|
||||
|
||||
if let article = timelineViewController.articles.articleAtRow(row) {
|
||||
configureTimelineCell(cell, article: article)
|
||||
}
|
||||
else {
|
||||
makeTimelineCellEmpty(cell)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
private func postTimelineSelectionDidChangeNotification(_ selectedArticle: Article?) {
|
||||
|
||||
guard let timelineViewController = timelineViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
let appInfo = AppInfo()
|
||||
if let article = selectedArticle {
|
||||
appInfo.article = article
|
||||
}
|
||||
appInfo.view = timelineViewController.tableView
|
||||
|
||||
NotificationCenter.default.post(name: .TimelineSelectionDidChange, object: timelineViewController, userInfo: appInfo.userInfo)
|
||||
}
|
||||
|
||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||
|
||||
guard let timelineViewController = timelineViewController, let tableView = timelineViewController.tableView else {
|
||||
return
|
||||
}
|
||||
|
||||
tableView.redrawGrid()
|
||||
|
||||
let selectedRow = tableView.selectedRow
|
||||
if selectedRow < 0 || selectedRow == NSNotFound || tableView.numberOfSelectedRows != 1 {
|
||||
postTimelineSelectionDidChangeNotification(nil)
|
||||
return
|
||||
}
|
||||
|
||||
if let selectedArticle = timelineViewController.articles.articleAtRow(selectedRow) {
|
||||
if (!selectedArticle.status.read) {
|
||||
markArticles(Set([selectedArticle]), statusKey: .read, flag: true)
|
||||
}
|
||||
postTimelineSelectionDidChangeNotification(selectedArticle)
|
||||
}
|
||||
else {
|
||||
postTimelineSelectionDidChangeNotification(nil)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension TimelineTableViewDelegate {
|
||||
|
||||
func configureTimelineCell(_ cell: TimelineTableCellView, article: Article) {
|
||||
|
||||
guard let timelineViewController = timelineViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
cell.objectValue = article
|
||||
cell.cellData = TimelineCellData(article: article, appearance: timelineViewController.cellAppearance, showFeedName: timelineViewController.showFeedNames)
|
||||
}
|
||||
|
||||
func makeTimelineCellEmpty(_ cell: TimelineTableCellView) {
|
||||
|
||||
cell.objectValue = nil
|
||||
cell.cellData = emptyCellData
|
||||
}
|
||||
}
|
@ -9,30 +9,16 @@
|
||||
import Foundation
|
||||
import RSCore
|
||||
import RSTextDrawing
|
||||
import RSTree
|
||||
import Data
|
||||
import Account
|
||||
|
||||
class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource, KeyboardDelegate {
|
||||
class TimelineViewController: NSViewController, KeyboardDelegate {
|
||||
|
||||
@IBOutlet var tableView: TimelineTableView!
|
||||
private var undoableCommands = [UndoableCommand]()
|
||||
private var dataSource: TimelineTableViewDataSource!
|
||||
var didRegisterForNotifications = false
|
||||
var fontSize: FontSize = AppDefaults.shared.timelineFontSize {
|
||||
didSet {
|
||||
fontSizeDidChange()
|
||||
}
|
||||
}
|
||||
var cellAppearance: TimelineCellAppearance!
|
||||
var showFeedNames = false
|
||||
|
||||
var numberOfArticles: Int {
|
||||
get {
|
||||
return articles.count
|
||||
}
|
||||
}
|
||||
|
||||
private var articles = [Article]() {
|
||||
var articles = ArticleArray() {
|
||||
didSet {
|
||||
if articles != oldValue {
|
||||
clearUndoableCommands()
|
||||
@ -41,6 +27,29 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView
|
||||
}
|
||||
}
|
||||
|
||||
var selectedArticles: [Article] {
|
||||
get {
|
||||
return Array(articles.articlesForIndexes(tableView.selectedRowIndexes))
|
||||
}
|
||||
}
|
||||
|
||||
private var undoableCommands = [UndoableCommand]()
|
||||
|
||||
private lazy var tableViewDataSource: TimelineTableViewDataSource! = {
|
||||
return TimelineTableViewDataSource(timelineViewController: self)
|
||||
}()
|
||||
|
||||
private lazy var tableViewDelegate: TimelineTableViewDelegate! = {
|
||||
return TimelineTableViewDelegate(timelineViewController: self)
|
||||
}()
|
||||
|
||||
private var didRegisterForNotifications = false
|
||||
private var fontSize: FontSize = AppDefaults.shared.timelineFontSize {
|
||||
didSet {
|
||||
fontSizeDidChange()
|
||||
}
|
||||
}
|
||||
|
||||
private var representedObjects: [AnyObject]? {
|
||||
didSet {
|
||||
if !representedObjectArraysAreEqual(oldValue, representedObjects) {
|
||||
@ -52,20 +61,6 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView
|
||||
}
|
||||
}
|
||||
|
||||
private var showFeedNames: Bool {
|
||||
|
||||
// if let _ = node?.representedObject as? Feed {
|
||||
return false
|
||||
// }
|
||||
// return true
|
||||
}
|
||||
|
||||
var selectedArticles: [Article] {
|
||||
get {
|
||||
return Array(articlesForIndexes(tableView.selectedRowIndexes))
|
||||
}
|
||||
}
|
||||
|
||||
private var oneSelectedArticle: Article? {
|
||||
get {
|
||||
return selectedArticles.count == 1 ? selectedArticles.first : nil
|
||||
@ -76,15 +71,13 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
dataSource = TimelineTableViewDataSource(timelineViewController: self)
|
||||
tableView.dataSource = dataSource
|
||||
|
||||
cellAppearance = TimelineCellAppearance(theme: currentTheme, fontSize: fontSize)
|
||||
tableView.rowHeight = calculateRowHeight()
|
||||
|
||||
tableView.dataSource = tableViewDataSource
|
||||
tableView.delegate = tableViewDelegate
|
||||
tableView.rowHeight = calculateRowHeight()
|
||||
tableView.target = self
|
||||
tableView.doubleAction = #selector(openArticleInBrowser(_:))
|
||||
|
||||
tableView.keyboardDelegate = self
|
||||
|
||||
if !didRegisterForNotifications {
|
||||
@ -229,36 +222,13 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView
|
||||
}
|
||||
|
||||
func canMarkAllAsRead() -> Bool {
|
||||
|
||||
for article in articles {
|
||||
if !article.status.read {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
return articles.canMarkAllAsRead()
|
||||
}
|
||||
|
||||
func indexOfNextUnreadArticle() -> Int? {
|
||||
|
||||
if articles.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
var rowIndex = tableView.selectedRow
|
||||
while(true) {
|
||||
|
||||
rowIndex = rowIndex + 1
|
||||
if rowIndex >= articles.count {
|
||||
break
|
||||
}
|
||||
let article = articleAtRow(rowIndex)!
|
||||
if !article.status.read {
|
||||
return rowIndex
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
return articles.rowOfNextUnreadArticle(tableView.selectedRow)
|
||||
}
|
||||
|
||||
// MARK: - Notifications
|
||||
@ -368,106 +338,10 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView
|
||||
|
||||
private func reloadCellsForArticleIDs(_ articleIDs: Set<String>) {
|
||||
|
||||
let indexes = indexesForArticleIDs(articleIDs)
|
||||
let indexes = articles.indexesForArticleIDs(articleIDs)
|
||||
tableView.reloadData(forRowIndexes: indexes, columnIndexes: NSIndexSet(index: 0) as IndexSet)
|
||||
}
|
||||
|
||||
// MARK: - Articles
|
||||
|
||||
private func indexesForArticleIDs(_ articleIDs: Set<String>) -> IndexSet {
|
||||
|
||||
var indexes = IndexSet()
|
||||
|
||||
articleIDs.forEach { (articleID) in
|
||||
let oneIndex = rowForArticleID(articleID)
|
||||
if oneIndex != NSNotFound {
|
||||
indexes.insert(oneIndex)
|
||||
}
|
||||
}
|
||||
|
||||
return indexes
|
||||
}
|
||||
|
||||
private func articlesForIndexes(_ indexes: IndexSet) -> Set<Article> {
|
||||
|
||||
return Set(indexes.flatMap{ (oneIndex) -> Article? in
|
||||
return articleAtRow(oneIndex)
|
||||
})
|
||||
}
|
||||
|
||||
func articleAtRow(_ row: Int) -> Article? {
|
||||
|
||||
if row < 0 || row == NSNotFound || row > articles.count - 1 {
|
||||
return nil
|
||||
}
|
||||
return articles[row]
|
||||
}
|
||||
|
||||
private func rowForArticle(_ article: Article) -> Int {
|
||||
|
||||
return rowForArticleID(article.articleID)
|
||||
}
|
||||
|
||||
private func rowForArticleID(_ articleID: String) -> Int {
|
||||
|
||||
if let index = articles.index(where: { $0.articleID == articleID }) {
|
||||
return index
|
||||
}
|
||||
|
||||
return NSNotFound
|
||||
}
|
||||
|
||||
func selectedArticle() -> Article? {
|
||||
|
||||
return articleAtRow(tableView.selectedRow)
|
||||
}
|
||||
|
||||
// MARK: Sorting Articles
|
||||
|
||||
private func articleComparator(_ article1: Article, article2: Article) -> Bool {
|
||||
|
||||
return article1.logicalDatePublished > article2.logicalDatePublished
|
||||
}
|
||||
|
||||
private func articlesSortedByDate(_ articles: Set<Article>) -> [Article] {
|
||||
|
||||
return Array(articles).sorted(by: articleComparator)
|
||||
}
|
||||
|
||||
// MARK: Fetching Articles
|
||||
|
||||
private func emptyTheTimeline() {
|
||||
|
||||
if !articles.isEmpty {
|
||||
articles = [Article]()
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchArticles() {
|
||||
|
||||
guard let representedObjects = representedObjects else {
|
||||
emptyTheTimeline()
|
||||
return
|
||||
}
|
||||
|
||||
var fetchedArticles = Set<Article>()
|
||||
|
||||
for object in representedObjects {
|
||||
|
||||
if let feed = object as? Feed {
|
||||
fetchedArticles.formUnion(feed.fetchArticles())
|
||||
}
|
||||
else if let folder = object as? Folder {
|
||||
fetchedArticles.formUnion(folder.fetchArticles())
|
||||
}
|
||||
}
|
||||
|
||||
let sortedArticles = articlesSortedByDate(fetchedArticles)
|
||||
if articles != sortedArticles {
|
||||
articles = sortedArticles
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Cell Configuring
|
||||
|
||||
private func calculateRowHeight() -> CGFloat {
|
||||
@ -482,84 +356,47 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView
|
||||
return height
|
||||
}
|
||||
|
||||
private func configureTimelineCell(_ cell: TimelineTableCellView, article: Article) {
|
||||
|
||||
cell.objectValue = article
|
||||
cell.cellData = TimelineCellData(article: article, appearance: cellAppearance, showFeedName: showFeedNames)
|
||||
}
|
||||
}
|
||||
|
||||
private extension TimelineViewController {
|
||||
|
||||
private func makeTimelineCellEmpty(_ cell: TimelineTableCellView) {
|
||||
|
||||
cell.objectValue = nil
|
||||
cell.cellData = emptyCellData
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDataSource
|
||||
|
||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
|
||||
return articles.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
|
||||
|
||||
return articleAtRow(row)
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDelegate
|
||||
|
||||
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
|
||||
|
||||
let rowView: TimelineTableRowView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "timelineRow"), owner: self) as! TimelineTableRowView
|
||||
rowView.cellAppearance = cellAppearance
|
||||
return rowView
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
|
||||
let cell: TimelineTableCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "timelineCell"), owner: self) as! TimelineTableCellView
|
||||
cell.cellAppearance = cellAppearance
|
||||
|
||||
if let article = articleAtRow(row) {
|
||||
configureTimelineCell(cell, article: article)
|
||||
var hasAtLeastOneSelectedArticle: Bool {
|
||||
get {
|
||||
return tableView.selectedRow != -1
|
||||
}
|
||||
else {
|
||||
makeTimelineCellEmpty(cell)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
private func postTimelineSelectionDidChangeNotification(_ selectedArticle: Article?) {
|
||||
func emptyTheTimeline() {
|
||||
|
||||
let appInfo = AppInfo()
|
||||
if let article = selectedArticle {
|
||||
appInfo.article = article
|
||||
if !articles.isEmpty {
|
||||
articles = [Article]()
|
||||
}
|
||||
appInfo.view = tableView
|
||||
|
||||
NotificationCenter.default.post(name: .TimelineSelectionDidChange, object: self, userInfo: appInfo.userInfo)
|
||||
}
|
||||
|
||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||
// MARK: Fetching Articles
|
||||
|
||||
tableView.redrawGrid()
|
||||
|
||||
let selectedRow = tableView.selectedRow
|
||||
|
||||
if selectedRow < 0 || selectedRow == NSNotFound || tableView.numberOfSelectedRows != 1 {
|
||||
postTimelineSelectionDidChangeNotification(nil)
|
||||
func fetchArticles() {
|
||||
|
||||
guard let representedObjects = representedObjects else {
|
||||
emptyTheTimeline()
|
||||
return
|
||||
}
|
||||
|
||||
if let selectedArticle = articleAtRow(selectedRow) {
|
||||
if (!selectedArticle.status.read) {
|
||||
markArticles(Set([selectedArticle]), statusKey: .read, flag: true)
|
||||
var fetchedArticles = Set<Article>()
|
||||
|
||||
for object in representedObjects {
|
||||
|
||||
if let feed = object as? Feed {
|
||||
fetchedArticles.formUnion(feed.fetchArticles())
|
||||
}
|
||||
else if let folder = object as? Folder {
|
||||
fetchedArticles.formUnion(folder.fetchArticles())
|
||||
}
|
||||
postTimelineSelectionDidChangeNotification(selectedArticle)
|
||||
}
|
||||
else {
|
||||
postTimelineSelectionDidChangeNotification(nil)
|
||||
|
||||
let sortedArticles = Array(fetchedArticles).sortedByDate()
|
||||
if articles != sortedArticles {
|
||||
articles = sortedArticles
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,72 +423,3 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView
|
||||
}
|
||||
}
|
||||
|
||||
private extension TimelineViewController {
|
||||
|
||||
var hasAtLeastOneSelectedArticle: Bool {
|
||||
get {
|
||||
return self.tableView.selectedRow != -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSTableView extension
|
||||
|
||||
private extension NSTableView {
|
||||
|
||||
func scrollTo(row: Int) {
|
||||
|
||||
guard let scrollView = self.enclosingScrollView else {
|
||||
return
|
||||
}
|
||||
let documentVisibleRect = scrollView.documentVisibleRect
|
||||
|
||||
let r = rect(ofRow: row)
|
||||
if NSContainsRect(documentVisibleRect, r) {
|
||||
return
|
||||
}
|
||||
|
||||
let rMidY = NSMidY(r)
|
||||
var scrollPoint = NSZeroPoint;
|
||||
let extraHeight = 150
|
||||
scrollPoint.y = floor(rMidY - (documentVisibleRect.size.height / 2.0)) + CGFloat(extraHeight)
|
||||
scrollPoint.y = max(scrollPoint.y, 0)
|
||||
|
||||
let maxScrollPointY = frame.size.height - documentVisibleRect.size.height
|
||||
scrollPoint.y = min(maxScrollPointY, scrollPoint.y)
|
||||
|
||||
let clipView = scrollView.contentView
|
||||
|
||||
let rClipView = NSMakeRect(scrollPoint.x, scrollPoint.y, NSWidth(clipView.bounds), NSHeight(clipView.bounds))
|
||||
|
||||
clipView.animator().bounds = rClipView
|
||||
}
|
||||
|
||||
func visibleRowViews() -> [TimelineTableRowView]? {
|
||||
|
||||
guard let scrollView = self.enclosingScrollView, numberOfRows > 0 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let range = rows(in: scrollView.documentVisibleRect)
|
||||
let ixMax = numberOfRows - 1
|
||||
let ixStart = min(range.location, ixMax)
|
||||
let ixEnd = min(((range.location + range.length) - 1), ixMax)
|
||||
|
||||
var visibleRows = [TimelineTableRowView]()
|
||||
|
||||
for ixRow in ixStart...ixEnd {
|
||||
if let oneRowView = rowView(atRow: ixRow, makeIfNecessary: false) as? TimelineTableRowView {
|
||||
visibleRows += [oneRowView]
|
||||
}
|
||||
}
|
||||
|
||||
return visibleRows.isEmpty ? nil : visibleRows
|
||||
}
|
||||
|
||||
func redrawGrid() {
|
||||
|
||||
visibleRowViews()?.forEach { $0.invalidateGridRect() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,4 +15,54 @@ public extension NSTableView {
|
||||
return selectedRowIndexes.startIndex == selectedRowIndexes.endIndex
|
||||
}
|
||||
}
|
||||
|
||||
func scrollTo(row: Int) {
|
||||
|
||||
guard let scrollView = self.enclosingScrollView else {
|
||||
return
|
||||
}
|
||||
let documentVisibleRect = scrollView.documentVisibleRect
|
||||
|
||||
let r = rect(ofRow: row)
|
||||
if NSContainsRect(documentVisibleRect, r) {
|
||||
return
|
||||
}
|
||||
|
||||
let rMidY = NSMidY(r)
|
||||
var scrollPoint = NSZeroPoint;
|
||||
let extraHeight = 150
|
||||
scrollPoint.y = floor(rMidY - (documentVisibleRect.size.height / 2.0)) + CGFloat(extraHeight)
|
||||
scrollPoint.y = max(scrollPoint.y, 0)
|
||||
|
||||
let maxScrollPointY = frame.size.height - documentVisibleRect.size.height
|
||||
scrollPoint.y = min(maxScrollPointY, scrollPoint.y)
|
||||
|
||||
let clipView = scrollView.contentView
|
||||
|
||||
let rClipView = NSMakeRect(scrollPoint.x, scrollPoint.y, NSWidth(clipView.bounds), NSHeight(clipView.bounds))
|
||||
|
||||
clipView.animator().bounds = rClipView
|
||||
}
|
||||
|
||||
func visibleRowViews() -> [NSTableRowView]? {
|
||||
|
||||
guard let scrollView = self.enclosingScrollView, numberOfRows > 0 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let range = rows(in: scrollView.documentVisibleRect)
|
||||
let ixMax = numberOfRows - 1
|
||||
let ixStart = min(range.location, ixMax)
|
||||
let ixEnd = min(((range.location + range.length) - 1), ixMax)
|
||||
|
||||
var visibleRows = [NSTableRowView]()
|
||||
|
||||
for ixRow in ixStart...ixEnd {
|
||||
if let oneRowView = rowView(atRow: ixRow, makeIfNecessary: false) {
|
||||
visibleRows += [oneRowView]
|
||||
}
|
||||
}
|
||||
|
||||
return visibleRows.isEmpty ? nil : visibleRows
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user