Switch to using NSTextField for timeline date and feed name views.
This commit is contained in:
parent
01b5292e3d
commit
f11604df48
|
@ -140,6 +140,7 @@
|
||||||
84D5BA20201E8FB6009092BD /* SidebarGearMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D5BA1F201E8FB6009092BD /* SidebarGearMenuDelegate.swift */; };
|
84D5BA20201E8FB6009092BD /* SidebarGearMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D5BA1F201E8FB6009092BD /* SidebarGearMenuDelegate.swift */; };
|
||||||
84DAEE301F86CAFE0058304B /* OPMLImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DAEE2F1F86CAFE0058304B /* OPMLImporter.swift */; };
|
84DAEE301F86CAFE0058304B /* OPMLImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DAEE2F1F86CAFE0058304B /* OPMLImporter.swift */; };
|
||||||
84DAEE321F870B390058304B /* DockBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DAEE311F870B390058304B /* DockBadge.swift */; };
|
84DAEE321F870B390058304B /* DockBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DAEE311F870B390058304B /* DockBadge.swift */; };
|
||||||
|
84E185B3203B74E500F69BFA /* SingleLineTextFieldSizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E185B2203B74E500F69BFA /* SingleLineTextFieldSizer.swift */; };
|
||||||
84E46C7D1F75EF7B005ECFB3 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E46C7C1F75EF7B005ECFB3 /* AppDefaults.swift */; };
|
84E46C7D1F75EF7B005ECFB3 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E46C7C1F75EF7B005ECFB3 /* AppDefaults.swift */; };
|
||||||
84E850861FCB60CE0072EA88 /* AuthorAvatarDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E850851FCB60CE0072EA88 /* AuthorAvatarDownloader.swift */; };
|
84E850861FCB60CE0072EA88 /* AuthorAvatarDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E850851FCB60CE0072EA88 /* AuthorAvatarDownloader.swift */; };
|
||||||
84E8E0DB202EC49300562D8F /* TimelineViewController+ContextualMenus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E8E0DA202EC49300562D8F /* TimelineViewController+ContextualMenus.swift */; };
|
84E8E0DB202EC49300562D8F /* TimelineViewController+ContextualMenus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E8E0DA202EC49300562D8F /* TimelineViewController+ContextualMenus.swift */; };
|
||||||
|
@ -661,6 +662,7 @@
|
||||||
84D5BA1F201E8FB6009092BD /* SidebarGearMenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarGearMenuDelegate.swift; sourceTree = "<group>"; };
|
84D5BA1F201E8FB6009092BD /* SidebarGearMenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarGearMenuDelegate.swift; sourceTree = "<group>"; };
|
||||||
84DAEE2F1F86CAFE0058304B /* OPMLImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLImporter.swift; sourceTree = "<group>"; };
|
84DAEE2F1F86CAFE0058304B /* OPMLImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLImporter.swift; sourceTree = "<group>"; };
|
||||||
84DAEE311F870B390058304B /* DockBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DockBadge.swift; path = Evergreen/DockBadge.swift; sourceTree = "<group>"; };
|
84DAEE311F870B390058304B /* DockBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DockBadge.swift; path = Evergreen/DockBadge.swift; sourceTree = "<group>"; };
|
||||||
|
84E185B2203B74E500F69BFA /* SingleLineTextFieldSizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleLineTextFieldSizer.swift; sourceTree = "<group>"; };
|
||||||
84E46C7C1F75EF7B005ECFB3 /* AppDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDefaults.swift; path = Evergreen/AppDefaults.swift; sourceTree = "<group>"; };
|
84E46C7C1F75EF7B005ECFB3 /* AppDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDefaults.swift; path = Evergreen/AppDefaults.swift; sourceTree = "<group>"; };
|
||||||
84E850851FCB60CE0072EA88 /* AuthorAvatarDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorAvatarDownloader.swift; sourceTree = "<group>"; };
|
84E850851FCB60CE0072EA88 /* AuthorAvatarDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorAvatarDownloader.swift; sourceTree = "<group>"; };
|
||||||
84E8E0DA202EC49300562D8F /* TimelineViewController+ContextualMenus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimelineViewController+ContextualMenus.swift"; sourceTree = "<group>"; };
|
84E8E0DA202EC49300562D8F /* TimelineViewController+ContextualMenus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimelineViewController+ContextualMenus.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -1008,6 +1010,7 @@
|
||||||
849A97741ED9EC04007D329B /* TimelineTableCellView.swift */,
|
849A97741ED9EC04007D329B /* TimelineTableCellView.swift */,
|
||||||
849A97701ED9EC04007D329B /* TimelineCellAppearance.swift */,
|
849A97701ED9EC04007D329B /* TimelineCellAppearance.swift */,
|
||||||
849A97721ED9EC04007D329B /* TimelineCellLayout.swift */,
|
849A97721ED9EC04007D329B /* TimelineCellLayout.swift */,
|
||||||
|
84E185B2203B74E500F69BFA /* SingleLineTextFieldSizer.swift */,
|
||||||
849A97711ED9EC04007D329B /* TimelineCellData.swift */,
|
849A97711ED9EC04007D329B /* TimelineCellData.swift */,
|
||||||
849A97731ED9EC04007D329B /* TimelineStringUtilities.swift */,
|
849A97731ED9EC04007D329B /* TimelineStringUtilities.swift */,
|
||||||
849A97751ED9EC04007D329B /* UnreadIndicatorView.swift */,
|
849A97751ED9EC04007D329B /* UnreadIndicatorView.swift */,
|
||||||
|
@ -2001,6 +2004,7 @@
|
||||||
849A97851ED9ECCD007D329B /* PreferencesWindowController.swift in Sources */,
|
849A97851ED9ECCD007D329B /* PreferencesWindowController.swift in Sources */,
|
||||||
D5F4EDB720074D6500B9E363 /* Feed+Scriptability.swift in Sources */,
|
D5F4EDB720074D6500B9E363 /* Feed+Scriptability.swift in Sources */,
|
||||||
84E850861FCB60CE0072EA88 /* AuthorAvatarDownloader.swift in Sources */,
|
84E850861FCB60CE0072EA88 /* AuthorAvatarDownloader.swift in Sources */,
|
||||||
|
84E185B3203B74E500F69BFA /* SingleLineTextFieldSizer.swift in Sources */,
|
||||||
8414AD251FCF5A1E00955102 /* TimelineHeaderView.swift in Sources */,
|
8414AD251FCF5A1E00955102 /* TimelineHeaderView.swift in Sources */,
|
||||||
849EE71F20391DF20082A1EA /* MainWindowToolbarDelegate.swift in Sources */,
|
849EE71F20391DF20082A1EA /* MainWindowToolbarDelegate.swift in Sources */,
|
||||||
849A977A1ED9EC04007D329B /* TimelineTableCellView.swift in Sources */,
|
849A977A1ED9EC04007D329B /* TimelineTableCellView.swift in Sources */,
|
||||||
|
|
|
@ -28,8 +28,10 @@ struct SidebarCellLayout {
|
||||||
}
|
}
|
||||||
self.faviconRect = rFavicon
|
self.faviconRect = rFavicon
|
||||||
|
|
||||||
textField.sizeToFit()
|
// textField.sizeToFit()
|
||||||
let textFieldSize = textField.frame.size
|
// let textFieldSize = textField.fittingSize//frame.size
|
||||||
|
let textFieldSize = SingleLineTextFieldSizer.size(for: textField.stringValue, font: textField.font!)
|
||||||
|
|
||||||
var rTextField = NSRect(x: 0.0, y: 0.0, width: textFieldSize.width, height: textFieldSize.height)
|
var rTextField = NSRect(x: 0.0, y: 0.0, width: textFieldSize.width, height: textFieldSize.height)
|
||||||
if shouldShowImage {
|
if shouldShowImage {
|
||||||
rTextField.origin.x = NSMaxX(rFavicon) + appearance.imageMarginRight
|
rTextField.origin.x = NSMaxX(rFavicon) + appearance.imageMarginRight
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
//
|
||||||
|
// SingleLineTextFieldSizer.swift
|
||||||
|
// Evergreen
|
||||||
|
//
|
||||||
|
// Created by Brent Simmons on 2/19/18.
|
||||||
|
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
// Get the size of an NSTextField configured with a specific font with a specific size.
|
||||||
|
// Uses a cache.
|
||||||
|
|
||||||
|
final class SingleLineTextFieldSizer {
|
||||||
|
|
||||||
|
let font: NSFont
|
||||||
|
private let textField: NSTextField
|
||||||
|
private var cache = [String: NSSize]()
|
||||||
|
|
||||||
|
init(font: NSFont) {
|
||||||
|
|
||||||
|
self.textField = NSTextField(labelWithString: "")
|
||||||
|
self.textField.font = font
|
||||||
|
self.font = font
|
||||||
|
}
|
||||||
|
|
||||||
|
func size(for text: String) -> NSSize {
|
||||||
|
|
||||||
|
if let cachedSize = cache[text] {
|
||||||
|
return cachedSize
|
||||||
|
}
|
||||||
|
|
||||||
|
textField.stringValue = text
|
||||||
|
var calculatedSize = textField.fittingSize
|
||||||
|
calculatedSize.height = ceil(calculatedSize.height)
|
||||||
|
calculatedSize.width = ceil(calculatedSize.width)
|
||||||
|
|
||||||
|
cache[text] = calculatedSize
|
||||||
|
return calculatedSize
|
||||||
|
}
|
||||||
|
|
||||||
|
static private var sizers = [NSFont: SingleLineTextFieldSizer]()
|
||||||
|
|
||||||
|
static func sizer(for font: NSFont) -> SingleLineTextFieldSizer {
|
||||||
|
|
||||||
|
if let cachedSizer = sizers[font] {
|
||||||
|
return cachedSizer
|
||||||
|
}
|
||||||
|
|
||||||
|
let newSizer = SingleLineTextFieldSizer(font: font)
|
||||||
|
sizers[font] = newSizer
|
||||||
|
|
||||||
|
return newSizer
|
||||||
|
}
|
||||||
|
|
||||||
|
static func size(for text: String, font: NSFont) -> NSSize {
|
||||||
|
|
||||||
|
return sizer(for: font).size(for: text)
|
||||||
|
}
|
||||||
|
}
|
|
@ -107,13 +107,15 @@ private extension TimelineCellLayout {
|
||||||
|
|
||||||
static func rectOfLineBelow(_ textBoxRect: NSRect, _ rectAbove: NSRect, _ topMargin: CGFloat, _ attributedString: NSAttributedString) -> NSRect {
|
static func rectOfLineBelow(_ textBoxRect: NSRect, _ rectAbove: NSRect, _ topMargin: CGFloat, _ attributedString: NSAttributedString) -> NSRect {
|
||||||
|
|
||||||
let renderer = RSSingleLineRenderer(attributedTitle: attributedString)
|
let font = attributedString.attribute(NSAttributedStringKey.font, at: 0, effectiveRange: nil) as! NSFont
|
||||||
|
let textFieldSize = SingleLineTextFieldSizer.size(for: attributedString.string, font: font)
|
||||||
|
// let renderer = RSSingleLineRenderer(attributedTitle: attributedString)
|
||||||
var r = NSZeroRect
|
var r = NSZeroRect
|
||||||
r.size = renderer.size
|
r.size = textFieldSize
|
||||||
r.origin.y = NSMaxY(rectAbove) + topMargin
|
r.origin.y = NSMaxY(rectAbove) + topMargin
|
||||||
r.origin.x = textBoxRect.origin.x
|
r.origin.x = textBoxRect.origin.x
|
||||||
|
|
||||||
var width = renderer.size.width
|
var width = textFieldSize.width
|
||||||
width = min(width, textBoxRect.size.width)
|
width = min(width, textBoxRect.size.width)
|
||||||
width = max(width, 0.0)
|
width = max(width, 0.0)
|
||||||
r.size.width = width
|
r.size.width = width
|
||||||
|
|
|
@ -13,29 +13,18 @@ class TimelineTableCellView: NSTableCellView {
|
||||||
|
|
||||||
private let titleView = RSMultiLineView(frame: NSZeroRect)
|
private let titleView = RSMultiLineView(frame: NSZeroRect)
|
||||||
private let unreadIndicatorView = UnreadIndicatorView(frame: NSZeroRect)
|
private let unreadIndicatorView = UnreadIndicatorView(frame: NSZeroRect)
|
||||||
private let dateView = RSSingleLineView(frame: NSZeroRect)
|
private let dateView = TimelineTableCellView.singleLineTextField()
|
||||||
private let feedNameView = RSSingleLineView(frame: NSZeroRect)
|
private let feedNameView = TimelineTableCellView.singleLineTextField()
|
||||||
|
private let avatarImageView = TimelineTableCellView.imageView(with: AppImages.genericFeedImage, scaling: .scaleProportionallyDown)
|
||||||
|
private let starView = TimelineTableCellView.imageView(with: AppImages.timelineStar, scaling: .scaleNone)
|
||||||
|
|
||||||
private let avatarImageView: NSImageView = {
|
private lazy var textFields = {
|
||||||
let imageView = NSImageView(frame: NSRect.zero)
|
return [self.dateView, self.feedNameView]
|
||||||
imageView.imageScaling = .scaleProportionallyDown
|
|
||||||
imageView.animates = false
|
|
||||||
imageView.imageAlignment = .alignCenter
|
|
||||||
imageView.image = AppImages.genericFeedImage
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
|
|
||||||
private let starView: NSImageView = {
|
|
||||||
let imageView = NSImageView(frame: NSRect.zero)
|
|
||||||
imageView.imageScaling = .scaleNone
|
|
||||||
imageView.animates = false
|
|
||||||
imageView.imageAlignment = .alignCenter
|
|
||||||
imageView.image = AppImages.timelineStar
|
|
||||||
return imageView
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var cellAppearance: TimelineCellAppearance! {
|
var cellAppearance: TimelineCellAppearance! {
|
||||||
didSet {
|
didSet {
|
||||||
|
updateTextFields()
|
||||||
needsLayout = true
|
needsLayout = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,20 +49,18 @@ class TimelineTableCellView: NSTableCellView {
|
||||||
|
|
||||||
var isEmphasized = false {
|
var isEmphasized = false {
|
||||||
didSet {
|
didSet {
|
||||||
dateView.emphasized = isEmphasized
|
|
||||||
feedNameView.emphasized = isEmphasized
|
|
||||||
titleView.emphasized = isEmphasized
|
titleView.emphasized = isEmphasized
|
||||||
unreadIndicatorView.isEmphasized = isEmphasized
|
unreadIndicatorView.isEmphasized = isEmphasized
|
||||||
|
updateTextFieldColors()
|
||||||
needsDisplay = true
|
needsDisplay = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isSelected = false {
|
var isSelected = false {
|
||||||
didSet {
|
didSet {
|
||||||
dateView.selected = isSelected
|
|
||||||
feedNameView.selected = isSelected
|
|
||||||
titleView.selected = isSelected
|
titleView.selected = isSelected
|
||||||
unreadIndicatorView.isSelected = isSelected
|
unreadIndicatorView.isSelected = isSelected
|
||||||
|
updateTextFieldColors()
|
||||||
needsDisplay = true
|
needsDisplay = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,6 +129,48 @@ class TimelineTableCellView: NSTableCellView {
|
||||||
|
|
||||||
private extension TimelineTableCellView {
|
private extension TimelineTableCellView {
|
||||||
|
|
||||||
|
static func singleLineTextField() -> NSTextField {
|
||||||
|
|
||||||
|
let textField = NSTextField(labelWithString: "")
|
||||||
|
textField.usesSingleLineMode = true
|
||||||
|
textField.maximumNumberOfLines = 1
|
||||||
|
textField.isEditable = false
|
||||||
|
textField.lineBreakMode = .byTruncatingTail
|
||||||
|
return textField
|
||||||
|
}
|
||||||
|
|
||||||
|
static func imageView(with image: NSImage?, scaling: NSImageScaling) -> NSImageView {
|
||||||
|
|
||||||
|
let imageView = image != nil ? NSImageView(image: image!) : NSImageView(frame: NSRect.zero)
|
||||||
|
imageView.animates = false
|
||||||
|
imageView.imageAlignment = .alignCenter
|
||||||
|
imageView.imageScaling = scaling
|
||||||
|
return imageView
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTextFieldColors() {
|
||||||
|
|
||||||
|
if isEmphasized && isSelected {
|
||||||
|
textFields.forEach { $0.textColor = NSColor.white }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
feedNameView.textColor = cellAppearance.feedNameColor
|
||||||
|
dateView.textColor = cellAppearance.dateColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTextFieldFonts() {
|
||||||
|
|
||||||
|
feedNameView.font = cellAppearance.feedNameFont
|
||||||
|
dateView.font = cellAppearance.dateFont
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTextFields() {
|
||||||
|
|
||||||
|
updateTextFieldColors()
|
||||||
|
updateTextFieldFonts()
|
||||||
|
}
|
||||||
|
|
||||||
func addSubviewAtInit(_ view: NSView, hidden: Bool) {
|
func addSubviewAtInit(_ view: NSView, hidden: Bool) {
|
||||||
|
|
||||||
addSubview(view)
|
addSubview(view)
|
||||||
|
@ -184,7 +213,7 @@ private extension TimelineTableCellView {
|
||||||
|
|
||||||
func updateDateView() {
|
func updateDateView() {
|
||||||
|
|
||||||
dateView.attributedStringValue = cellData.attributedDateString
|
dateView.stringValue = cellData.attributedDateString.string
|
||||||
needsLayout = true
|
needsLayout = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +223,7 @@ private extension TimelineTableCellView {
|
||||||
if feedNameView.isHidden {
|
if feedNameView.isHidden {
|
||||||
feedNameView.isHidden = false
|
feedNameView.isHidden = false
|
||||||
}
|
}
|
||||||
feedNameView.attributedStringValue = cellData.attributedFeedName
|
feedNameView.stringValue = cellData.attributedFeedName.string
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if !feedNameView.isHidden {
|
if !feedNameView.isHidden {
|
||||||
|
|
|
@ -64,17 +64,17 @@ class TimelineTableRowView : NSTableRowView {
|
||||||
path.stroke()
|
path.stroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func draw(_ dirtyRect: NSRect) {
|
// override func draw(_ dirtyRect: NSRect) {
|
||||||
|
//
|
||||||
super.draw(dirtyRect)
|
// super.draw(dirtyRect)
|
||||||
|
//
|
||||||
if cellAppearance.drawsGrid && !isSelected && !isNextRowSelected {
|
// if cellAppearance.drawsGrid && !isSelected && !isNextRowSelected {
|
||||||
drawSeparator(in: dirtyRect)
|
// drawSeparator(in: dirtyRect)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func invalidateGridRect() {
|
func invalidateGridRect() {
|
||||||
|
|
||||||
setNeedsDisplay(gridRect)
|
// setNeedsDisplay(gridRect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue