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:
parent
1b26b1ece5
commit
c8920ca5a6
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue