Implement new article extractor button for Big Sur

This commit is contained in:
Maurice Parker 2020-08-10 17:34:12 -05:00
parent 4ce974daca
commit cb2eefa56a
27 changed files with 280 additions and 67 deletions

View File

@ -45,38 +45,18 @@ struct AppAssets {
return NSImage(systemSymbolName: "plus", accessibilityDescription: nil)!
}()
static var articleExtractor: RSImage! = {
return RSImage(named: "articleExtractor")
static var articleExtractorError: RSImage = {
return RSImage(named: "articleExtractorError")!
}()
static var articleExtractorError: RSImage! = {
return RSImage(named: "articleExtractorError")
static var articleExtractorOff: RSImage = {
return RSImage(named: "articleExtractorOff")!
}()
static var articleExtractorInactiveDark: RSImage! = {
return RSImage(named: "articleExtractorInactiveDark")
static var articleExtractorOn: RSImage = {
return RSImage(named: "articleExtractorOn")!
}()
static var articleExtractorInactiveLight: RSImage! = {
return RSImage(named: "articleExtractorInactiveLight")
}()
static var articleExtractorProgress1: RSImage! = {
return RSImage(named: "articleExtractorProgress1")
}()
static var articleExtractorProgress2: RSImage! = {
return RSImage(named: "articleExtractorProgress2")
}()
static var articleExtractorProgress3: RSImage! = {
return RSImage(named: "articleExtractorProgress3")
}()
static var articleExtractorProgress4: RSImage! = {
return RSImage(named: "articleExtractorProgress4")
}()
@available(macOS 11.0, *)
static var cleanUpImage: RSImage = {
return NSImage(systemSymbolName: "wind", accessibilityDescription: nil)!
@ -117,7 +97,39 @@ struct AppAssets {
static var iconDarkBackgroundColor: NSColor = {
return NSColor(named: NSColor.Name("iconDarkBackgroundColor"))!
}()
static var legacyArticleExtractor: RSImage! = {
return RSImage(named: "legacyArticleExtractor")
}()
static var legacyArticleExtractorError: RSImage! = {
return RSImage(named: "legacyArticleExtractorError")
}()
static var legacyArticleExtractorInactiveDark: RSImage! = {
return RSImage(named: "legacyArticleExtractorInactiveDark")
}()
static var legacyArticleExtractorInactiveLight: RSImage! = {
return RSImage(named: "legacyArticleExtractorInactiveLight")
}()
static var legacyArticleExtractorProgress1: RSImage! = {
return RSImage(named: "legacyArticleExtractorProgress1")
}()
static var legacyArticleExtractorProgress2: RSImage! = {
return RSImage(named: "legacyArticleExtractorProgress2")
}()
static var legacyArticleExtractorProgress3: RSImage! = {
return RSImage(named: "legacyArticleExtractorProgress3")
}()
static var legacyArticleExtractorProgress4: RSImage! = {
return RSImage(named: "legacyArticleExtractorProgress4")
}()
static var masterFolderImage: IconImage {
if #available(macOS 11.0, *) {
let image = NSImage(systemSymbolName: "folder", accessibilityDescription: nil)!

View File

@ -0,0 +1,108 @@
//
// ArticleExtractorButton.swift
// NetNewsWire
//
// Created by Maurice Parker on 8/10/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import AppKit
enum ArticleExtractorButtonState {
case error
case animated
case on
case off
}
class ArticleExtractorButton: NSButton {
private var animatedLayer: CALayer?
var buttonState: ArticleExtractorButtonState = .off {
didSet {
if buttonState != oldValue {
switch buttonState {
case .error:
stripAnimatedSublayer()
image = AppAssets.articleExtractorError
case .animated:
image = nil
needsLayout = true
case .on:
stripAnimatedSublayer()
image = AppAssets.articleExtractorOn
case .off:
stripAnimatedSublayer()
image = AppAssets.articleExtractorOff
}
}
}
}
override func accessibilityLabel() -> String? {
switch buttonState {
case .error:
return NSLocalizedString("Error - Reader View", comment: "Error - Reader View")
case .animated:
return NSLocalizedString("Processing - Reader View", comment: "Processing - Reader View")
case .on:
return NSLocalizedString("Selected - Reader View", comment: "Selected - Reader View")
case .off:
return NSLocalizedString("Reader View", comment: "Reader View")
}
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
wantsLayer = true
bezelStyle = .texturedRounded
image = AppAssets.articleExtractorOff
imageScaling = .scaleProportionallyDown
}
override func layout() {
super.layout()
guard case .animated = buttonState else {
return
}
stripAnimatedSublayer()
addAnimatedSublayer(to: layer!)
}
private func stripAnimatedSublayer() {
animatedLayer?.removeFromSuperlayer()
}
private func addAnimatedSublayer(to hostedLayer: CALayer) {
let image1 = AppAssets.articleExtractorOff.tinted(with: NSColor.controlTextColor)
let image2 = AppAssets.articleExtractorOn.tinted(with: NSColor.controlTextColor)
let images = [image1, image2, image1]
animatedLayer = CALayer()
let imageSize = AppAssets.articleExtractorOff.size
animatedLayer!.bounds = CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height)
animatedLayer!.position = CGPoint(x: bounds.midX, y: bounds.midY)
hostedLayer.addSublayer(animatedLayer!)
let animation = CAKeyframeAnimation(keyPath: "contents")
animation.calculationMode = CAAnimationCalculationMode.linear
animation.keyTimes = [0, 0.5, 1]
animation.duration = 2
animation.values = images
animation.repeatCount = HUGE
animatedLayer!.add(animation, forKey: "contents")
}
}

View File

@ -60,12 +60,12 @@ class LegacyArticleExtractorButton: NSButton {
addAnimatedSublayer(to: hostedLayer)
default:
if NSApplication.shared.isActive {
addImageSublayer(to: hostedLayer, image: AppAssets.articleExtractor, opacity: opacity)
addImageSublayer(to: hostedLayer, image: AppAssets.legacyArticleExtractor, opacity: opacity)
} else {
if NSApplication.shared.effectiveAppearance.isDarkMode {
addImageSublayer(to: hostedLayer, image: AppAssets.articleExtractorInactiveDark, opacity: opacity)
addImageSublayer(to: hostedLayer, image: AppAssets.legacyArticleExtractorInactiveDark, opacity: opacity)
} else {
addImageSublayer(to: hostedLayer, image: AppAssets.articleExtractorInactiveLight, opacity: opacity)
addImageSublayer(to: hostedLayer, image: AppAssets.legacyArticleExtractorInactiveLight, opacity: opacity)
}
}
}
@ -86,10 +86,10 @@ class LegacyArticleExtractorButton: NSButton {
}
private func addAnimatedSublayer(to hostedLayer: CALayer) {
let imageProgress1 = AppAssets.articleExtractorProgress1
let imageProgress2 = AppAssets.articleExtractorProgress2
let imageProgress3 = AppAssets.articleExtractorProgress3
let imageProgress4 = AppAssets.articleExtractorProgress4
let imageProgress1 = AppAssets.legacyArticleExtractorProgress1
let imageProgress2 = AppAssets.legacyArticleExtractorProgress2
let imageProgress3 = AppAssets.legacyArticleExtractorProgress3
let imageProgress4 = AppAssets.legacyArticleExtractorProgress4
let images = [imageProgress1, imageProgress2, imageProgress3, imageProgress4, imageProgress3, imageProgress2]
let imageLayer = CALayer()

View File

@ -757,6 +757,17 @@ extension MainWindowController: NSToolbarDelegate {
let title = NSLocalizedString("Star", comment: "Star")
return buildToolbarButton(.markStar, title, AppAssets.starOpenImage, "toggleStarred:")
case .readerView:
let toolbarItem = RSToolbarItem(itemIdentifier: .readerView)
toolbarItem.autovalidates = true
let description = NSLocalizedString("Reader View", comment: "Reader View")
toolbarItem.toolTip = description
toolbarItem.label = description
let button = ArticleExtractorButton()
button.action = #selector(toggleArticleExtractor(_:))
toolbarItem.view = button
return toolbarItem
case .openInBrowser:
let title = NSLocalizedString("Open in Browser", comment: "Open in Browser")
return buildToolbarButton(.openInBrowser, title, AppAssets.openInBrowserImage, "openArticleInBrowser:")
@ -791,6 +802,7 @@ extension MainWindowController: NSToolbarDelegate {
.nextUnread,
.markRead,
.markStar,
.readerView,
.openInBrowser,
.share,
.cleanUp
@ -828,6 +840,7 @@ extension MainWindowController: NSToolbarDelegate {
.nextUnread,
.markRead,
.markStar,
.readerView,
.openInBrowser,
.share
]
@ -1019,32 +1032,59 @@ private extension MainWindowController {
return false
}
guard let toolbarItem = item as? NSToolbarItem, let toolbarButton = toolbarItem.view as? LegacyArticleExtractorButton else {
if let menuItem = item as? NSMenuItem {
menuItem.state = isShowingExtractedArticle ? .on : .off
}
return currentLink != nil
}
toolbarButton.state = isShowingExtractedArticle ? .on : .off
if #available(macOS 11.0, *) {
guard let state = articleExtractor?.state else {
toolbarButton.isError = false
toolbarButton.isInProgress = false
toolbarButton.state = .off
return currentLink != nil
}
switch state {
case .processing:
toolbarButton.isError = false
toolbarButton.isInProgress = true
case .failedToParse:
toolbarButton.isError = true
toolbarButton.isInProgress = false
case .ready, .cancelled, .complete:
toolbarButton.isError = false
toolbarButton.isInProgress = false
guard let toolbarItem = item as? NSToolbarItem, let toolbarButton = toolbarItem.view as? ArticleExtractorButton else {
if let menuItem = item as? NSMenuItem {
menuItem.state = isShowingExtractedArticle ? .on : .off
}
return currentLink != nil
}
guard let state = articleExtractor?.state else {
toolbarButton.buttonState = .off
return currentLink != nil
}
switch state {
case .processing:
toolbarButton.buttonState = .animated
case .failedToParse:
toolbarButton.buttonState = .error
case .ready, .cancelled, .complete:
toolbarButton.buttonState = isShowingExtractedArticle ? .on : .off
}
} else {
guard let toolbarItem = item as? NSToolbarItem, let toolbarButton = toolbarItem.view as? LegacyArticleExtractorButton else {
if let menuItem = item as? NSMenuItem {
menuItem.state = isShowingExtractedArticle ? .on : .off
}
return currentLink != nil
}
toolbarButton.state = isShowingExtractedArticle ? .on : .off
guard let state = articleExtractor?.state else {
toolbarButton.isError = false
toolbarButton.isInProgress = false
toolbarButton.state = .off
return currentLink != nil
}
switch state {
case .processing:
toolbarButton.isError = false
toolbarButton.isInProgress = true
case .failedToParse:
toolbarButton.isError = true
toolbarButton.isInProgress = false
case .ready, .cancelled, .complete:
toolbarButton.isError = false
toolbarButton.isInProgress = false
}
}
return true

View File

@ -1,12 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ArticleExtractorError.pdf"
"filename" : "ArticleExtractorError.pdf",
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
}

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "ArticleExtractorOff.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "ArticleExtractorOn.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ArticleExtractorError.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -92,6 +92,8 @@
5110C37D2373A8D100A9C04F /* InspectorIconHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5110C37C2373A8D100A9C04F /* InspectorIconHeaderView.swift */; };
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */; };
5117715524E1EA0F00A2A836 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */; };
5117715624E1EA0F00A2A836 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */; };
511B9806237DCAC90028BCAA /* UserInfoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9805237DCAC90028BCAA /* UserInfoKey.swift */; };
511B9807237DCAC90028BCAA /* UserInfoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9805237DCAC90028BCAA /* UserInfoKey.swift */; };
511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */; };
@ -1458,6 +1460,7 @@
51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = "<group>"; };
51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContainerViewController.swift; sourceTree = "<group>"; };
51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RSImage-Extensions.swift"; sourceTree = "<group>"; };
5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
511B9805237DCAC90028BCAA /* UserInfoKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoKey.swift; sourceTree = "<group>"; };
511D43EE231FBDE800FB1562 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreenPad.storyboard; sourceTree = "<group>"; };
511D4410231FC02D00FB1562 /* KeyboardManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardManager.swift; sourceTree = "<group>"; };
@ -2905,6 +2908,7 @@
849A975D1ED9EB72007D329B /* MainWindowController.swift */,
519B8D322143397200FA689C /* SharingServiceDelegate.swift */,
849EE72020391F560082A1EA /* SharingServicePickerDelegate.swift */,
5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */,
51FA73B62332D5F70090D516 /* LegacyArticleExtractorButton.swift */,
847CD6C9232F4CBF00FAC46D /* IconView.swift */,
844B5B6B1FEA224B00C7C76A /* Keyboard */,
@ -4796,6 +4800,7 @@
510C43F8243D035C009F70C3 /* ExtensionPoint.swift in Sources */,
65ED4015235DEF6C0081F399 /* AccountsDetailViewController.swift in Sources */,
65ED4016235DEF6C0081F399 /* DetailViewController.swift in Sources */,
5117715624E1EA0F00A2A836 /* ArticleExtractorButton.swift in Sources */,
65ED4017235DEF6C0081F399 /* AppDelegate.swift in Sources */,
65ED4018235DEF6C0081F399 /* PreferencesTableViewBackgroundView.swift in Sources */,
65ED4019235DEF6C0081F399 /* FetchRequestOperation.swift in Sources */,
@ -5125,6 +5130,7 @@
5144EA382279FC6200D19003 /* AccountsAddLocalWindowController.swift in Sources */,
84AD1EAA2031617300BC20B7 /* PasteboardFolder.swift in Sources */,
515A5148243E64BA0089E588 /* ExtensionPointEnableWindowController.swift in Sources */,
5117715524E1EA0F00A2A836 /* ArticleExtractorButton.swift in Sources */,
5103A9F724225E4C00410853 /* AccountsAddCloudKitWindowController.swift in Sources */,
5144EA51227B8E4500D19003 /* AccountsFeedbinWindowController.swift in Sources */,
84AD1EBC2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift in Sources */,