Added optional separators in the timeline view.

The timeline now optionally includes "Mail style" separators (behind a
hidden default — "CorreiaSeparators" — which defaults to NO).
This commit is contained in:
Jim Correia 2019-09-02 09:13:21 -07:00
parent 1b26b1ece5
commit c8920ca5a6
5 changed files with 126 additions and 4 deletions

View File

@ -32,6 +32,7 @@ struct AppDefaults {
static let exportOPMLAccountID = "exportOPMLAccountID"
// Hidden prefs
static let timelineShowsSeparators = "CorreiaSeparators"
static let showTitleOnMainWindow = "KafasisTitleMode"
static let hideDockUnreadCount = "JustinMillerHideDockUnreadCount"
}
@ -135,6 +136,10 @@ struct AppDefaults {
setSortDirection(for: Key.timelineSortDirection, newValue)
}
}
static var timelineShowsSeparators: Bool {
return bool(for: Key.timelineShowsSeparators)
}
static var mainWindowWidths: [Int]? {
get {
@ -268,4 +273,17 @@ private extension AppDefaults {
}
}
// MARK: -
extension UserDefaults {
/// This property exists so that it can conveniently be observed via KVO
@objc var CorreiaSeparators: Bool {
get {
return bool(forKey: AppDefaults.Key.timelineShowsSeparators)
}
set {
set(newValue, forKey: AppDefaults.Key.timelineShowsSeparators)
}
}
}

View File

@ -22,9 +22,10 @@ struct TimelineCellLayout {
let unreadIndicatorRect: NSRect
let starRect: NSRect
let avatarImageRect: NSRect
let separatorRect: NSRect
let paddingBottom: CGFloat
init(width: CGFloat, height: CGFloat, feedNameRect: NSRect, dateRect: NSRect, titleRect: NSRect, numberOfLinesForTitle: Int, summaryRect: NSRect, textRect: NSRect, unreadIndicatorRect: NSRect, starRect: NSRect, avatarImageRect: NSRect, paddingBottom: CGFloat) {
init(width: CGFloat, height: CGFloat, feedNameRect: NSRect, dateRect: NSRect, titleRect: NSRect, numberOfLinesForTitle: Int, summaryRect: NSRect, textRect: NSRect, unreadIndicatorRect: NSRect, starRect: NSRect, avatarImageRect: NSRect, separatorRect: NSRect, paddingBottom: CGFloat) {
self.width = width
self.feedNameRect = feedNameRect
@ -36,6 +37,7 @@ struct TimelineCellLayout {
self.unreadIndicatorRect = unreadIndicatorRect
self.starRect = starRect
self.avatarImageRect = avatarImageRect
self.separatorRect = separatorRect
self.paddingBottom = paddingBottom
if height > 0.1 {
@ -73,10 +75,11 @@ struct TimelineCellLayout {
let avatarImageRect = TimelineCellLayout.rectForAvatar(cellData, appearance, showAvatar, textBoxRect, width, height)
let unreadIndicatorRect = TimelineCellLayout.rectForUnreadIndicator(appearance, textBoxRect)
let starRect = TimelineCellLayout.rectForStar(appearance, unreadIndicatorRect)
let separatorRect = TimelineCellLayout.rectForSeparator(cellData, appearance, showAvatar ? avatarImageRect : titleRect, width, height)
let paddingBottom = appearance.cellPadding.bottom
self.init(width: width, height: height, feedNameRect: feedNameRect, dateRect: dateRect, titleRect: titleRect, numberOfLinesForTitle: numberOfLinesForTitle, summaryRect: summaryRect, textRect: textRect, unreadIndicatorRect: unreadIndicatorRect, starRect: starRect, avatarImageRect: avatarImageRect, paddingBottom: paddingBottom)
self.init(width: width, height: height, feedNameRect: feedNameRect, dateRect: dateRect, titleRect: titleRect, numberOfLinesForTitle: numberOfLinesForTitle, summaryRect: summaryRect, textRect: textRect, unreadIndicatorRect: unreadIndicatorRect, starRect: starRect, avatarImageRect: avatarImageRect, separatorRect: separatorRect, paddingBottom: paddingBottom)
}
static func height(for width: CGFloat, cellData: TimelineCellData, appearance: TimelineCellAppearance) -> CGFloat {
@ -210,6 +213,10 @@ private extension TimelineCellLayout {
return r
}
static func rectForSeparator(_ cellData: TimelineCellData, _ appearance: TimelineCellAppearance, _ alignmentRect: NSRect, _ width: CGFloat, _ height: CGFloat) -> NSRect {
return NSRect(x: alignmentRect.minX, y: height - 1, width: width - alignmentRect.minX, height: 1)
}
}
private extension Array where Element == NSRect {

View File

@ -27,11 +27,18 @@ class TimelineTableCellView: NSTableCellView {
}()
private let starView = TimelineTableCellView.imageView(with: AppAssets.timelineStar, scaling: .scaleNone)
private let separatorView = TimelineTableCellView.separatorView()
private lazy var textFields = {
return [self.dateView, self.feedNameView, self.titleView, self.summaryView, self.textView]
}()
private var showsSeparator: Bool = AppDefaults.timelineShowsSeparators {
didSet {
separatorView.isHidden = !showsSeparator
}
}
var cellAppearance: TimelineCellAppearance! {
didSet {
if cellAppearance != oldValue {
@ -77,6 +84,15 @@ class TimelineTableCellView: NSTableCellView {
convenience init() {
self.init(frame: NSRect.zero)
}
override func prepareForReuse() {
super.prepareForReuse()
separatorView.isHidden = !showsSeparator
}
func timelineShowsSeparatorsDefaultDidChange() {
showsSeparator = AppDefaults.timelineShowsSeparators
}
override func setFrameSize(_ newSize: NSSize) {
@ -111,6 +127,7 @@ class TimelineTableCellView: NSTableCellView {
feedNameView.rs_setFrameIfNotEqual(layoutRects.feedNameRect)
avatarImageView.rs_setFrameIfNotEqual(layoutRects.avatarImageRect)
starView.rs_setFrameIfNotEqual(layoutRects.starRect)
separatorView.rs_setFrameIfNotEqual(layoutRects.separatorRect)
}
}
@ -148,6 +165,11 @@ private extension TimelineTableCellView {
imageView.imageScaling = scaling
return imageView
}
static func separatorView() -> NSView {
return TimelineSeparatorView(frame: .zero)
}
func setFrame(for textField: NSTextField, rect: NSRect) {
@ -193,6 +215,7 @@ private extension TimelineTableCellView {
addSubviewAtInit(feedNameView, hidden: true)
addSubviewAtInit(avatarImageView, hidden: true)
addSubviewAtInit(starView, hidden: true)
addSubviewAtInit(separatorView, hidden: !AppDefaults.timelineShowsSeparators)
makeTextFieldColorsNormal()
}
@ -296,3 +319,32 @@ private extension TimelineTableCellView {
updateAvatar()
}
}
// MARK: -
private class TimelineSeparatorView: NSView {
private static let backgroundColor = NSColor(named: "timelineSeparatorColor")!
override init(frame: NSRect) {
super.init(frame: frame)
self.wantsLayer = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidChangeEffectiveAppearance() {
super.viewDidChangeEffectiveAppearance()
needsDisplay = true
}
override var wantsUpdateLayer: Bool {
return true
}
override func updateLayer() {
super.updateLayer()
layer?.backgroundColor = TimelineSeparatorView.backgroundColor.cgColor
}
}

View File

@ -141,12 +141,14 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
}
private let keyboardDelegate = TimelineKeyboardDelegate()
private var timelineShowsSeparatorsObserver: NSKeyValueObservation?
convenience init(delegate: TimelineDelegate) {
self.init(nibName: "TimelineTableView", bundle: nil)
self.delegate = delegate
self.startObservingUserDefaults()
}
override func viewDidLoad() {
cellAppearance = TimelineCellAppearance(showAvatar: false, fontSize: fontSize)
@ -846,6 +848,19 @@ extension TimelineViewController: NSTableViewDelegate {
private extension TimelineViewController {
func startObservingUserDefaults() {
assert(timelineShowsSeparatorsObserver == nil)
timelineShowsSeparatorsObserver = UserDefaults.standard.observe(\UserDefaults.CorreiaSeparators) { [weak self] (_, _) in
guard let self = self, self.isViewLoaded else { return }
self.tableView.enumerateAvailableRowViews { (rowView, index) in
if let cellView = rowView.view(atColumn: 0) as? TimelineTableCellView {
cellView.timelineShowsSeparatorsDefaultDidChange()
}
}
}
}
@objc func reloadAvailableCells() {
if let indexesToReload = tableView.indexesOfAvailableRows() {
@ -1114,4 +1129,3 @@ private extension TimelineViewController {
return false
}
}

View File

@ -0,0 +1,31 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "gray-gamma-22",
"components" : {
"white" : "0.900",
"alpha" : "1.000"
}
}
},
{
"idiom" : "universal",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"platform" : "osx",
"reference" : "gridColor"
}
}
]
}