diff --git a/Multiplatform/Shared/AppAssets.swift b/Multiplatform/Shared/AppAssets.swift index 60fb7767a..e680fbafc 100644 --- a/Multiplatform/Shared/AppAssets.swift +++ b/Multiplatform/Shared/AppAssets.swift @@ -282,6 +282,12 @@ struct AppAssets { #endif } + #if os(macOS) + static var webStatusBarBackground: NSColor = { + return NSColor(named: "WebStatusBarBackground")! + }() + #endif + static func image(for accountType: AccountType) -> RSImage? { switch accountType { case .onMyMac: diff --git a/Multiplatform/Shared/Article/main_multiplatform.js b/Multiplatform/Shared/Article/main_multiplatform.js index 2de36d94b..e1f0f2d0a 100644 --- a/Multiplatform/Shared/Article/main_multiplatform.js +++ b/Multiplatform/Shared/Article/main_multiplatform.js @@ -140,9 +140,26 @@ function showFeedInspectorSetup() { } } +function linkHover() { + window.onmouseover = function(event) { + var closestAnchor = event.target.closest('a') + if (closestAnchor) { + window.webkit.messageHandlers.mouseDidEnter.postMessage(closestAnchor.href); + } + } + window.onmouseout = function(event) { + var closestAnchor = event.target.closest('a') + if (closestAnchor) { + window.webkit.messageHandlers.mouseDidExit.postMessage(closestAnchor.href); + } + } +} + + function postRenderProcessing() { ImageViewer.init(); showFeedInspectorSetup(); + linkHover(); } diff --git a/Multiplatform/Shared/Article/page.html b/Multiplatform/Shared/Article/page.html index 0d7d9676d..cc2894ab5 100644 --- a/Multiplatform/Shared/Article/page.html +++ b/Multiplatform/Shared/Article/page.html @@ -10,7 +10,6 @@ <script src="newsfoot.js" async="async"></script> <script type="text/javascript"> document.addEventListener("DOMContentLoaded", function(event) { - window.scrollTo(0, [[windowScrollY]]); processPage(); }) </script> diff --git a/Multiplatform/Shared/Assets.xcassets/WebStatusBarBackground.colorset/Contents.json b/Multiplatform/Shared/Assets.xcassets/WebStatusBarBackground.colorset/Contents.json new file mode 100644 index 000000000..9928937fb --- /dev/null +++ b/Multiplatform/Shared/Assets.xcassets/WebStatusBarBackground.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.940", + "green" : "0.940", + "red" : "0.940" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Multiplatform/macOS/Article/ArticleView.swift b/Multiplatform/macOS/Article/ArticleView.swift index 7ee5e401b..758d5a81b 100644 --- a/Multiplatform/macOS/Article/ArticleView.swift +++ b/Multiplatform/macOS/Article/ArticleView.swift @@ -9,7 +9,7 @@ import SwiftUI import Articles -final class ArticleView: NSViewControllerRepresentable { +struct ArticleView: NSViewControllerRepresentable { var sceneModel: SceneModel var articleModel: ArticleModel diff --git a/Multiplatform/macOS/Article/WebStatusBarView.swift b/Multiplatform/macOS/Article/WebStatusBarView.swift new file mode 100644 index 000000000..a8b7924a1 --- /dev/null +++ b/Multiplatform/macOS/Article/WebStatusBarView.swift @@ -0,0 +1,102 @@ +// +// WebStatusBarView.swift +// Multiplatform macOS +// +// Created by Maurice Parker on 7/8/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import AppKit +import Articles + +final class WebStatusBarView: NSView { + + var urlLabel = NSTextField(labelWithString: "") + + var mouseoverLink: String? { + didSet { + updateLinkForDisplay() + } + } + + private var linkForDisplay: String? { + didSet { + needsLayout = true + if let link = linkForDisplay { + urlLabel.stringValue = link + self.isHidden = false + } + else { + urlLabel.stringValue = "" + self.isHidden = true + } + } + } + + private var didConfigureLayerRadius = false + + override var isOpaque: Bool { + return false + } + + override var isFlipped: Bool { + return true + } + + override var wantsUpdateLayer: Bool { + return true + } + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + override func updateLayer() { + guard let layer = layer else { + return + } + if !didConfigureLayerRadius { + layer.cornerRadius = 4.0 + didConfigureLayerRadius = true + } + + let color = self.effectiveAppearance.isDarkMode ? NSColor.textBackgroundColor : AppAssets.webStatusBarBackground + layer.backgroundColor = color.cgColor + } +} + +// MARK: - Private + +private extension WebStatusBarView { + + func commonInit() { + self.isHidden = true + urlLabel.translatesAutoresizingMaskIntoConstraints = false + urlLabel.lineBreakMode = .byTruncatingMiddle + urlLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + + addSubview(urlLabel) + NSLayoutConstraint.activate([ + leadingAnchor.constraint(equalTo: urlLabel.leadingAnchor, constant: -6), + trailingAnchor.constraint(equalTo: urlLabel.trailingAnchor, constant: 6), + centerYAnchor.constraint(equalTo: urlLabel.centerYAnchor) + ]) + } + + func updateLinkForDisplay() { + if let mouseoverLink = mouseoverLink, !mouseoverLink.isEmpty { + linkForDisplay = mouseoverLink.strippingHTTPOrHTTPSScheme + } + else { + linkForDisplay = nil + } + } +} + + diff --git a/Multiplatform/macOS/Article/WebViewController.swift b/Multiplatform/macOS/Article/WebViewController.swift index 920433386..a967062db 100644 --- a/Multiplatform/macOS/Article/WebViewController.swift +++ b/Multiplatform/macOS/Article/WebViewController.swift @@ -27,9 +27,13 @@ class WebViewController: NSViewController { private struct MessageName { static let imageWasClicked = "imageWasClicked" static let imageWasShown = "imageWasShown" + static let mouseDidEnter = "mouseDidEnter" + static let mouseDidExit = "mouseDidExit" static let showFeedInspector = "showFeedInspector" } + var statusBarView: WebStatusBarView! + private var webView: PreloadedWebView? private var articleExtractor: ArticleExtractor? = nil @@ -60,6 +64,16 @@ class WebViewController: NSViewController { DistributedNotificationCenter.default().addObserver(self, selector: #selector(appleColorPreferencesChanged(_:)), name: .appleColorPreferencesChangedNotification, object: nil) DistributedNotificationCenter.default().addObserver(self, selector: #selector(appleInterfaceThemeChanged(_:)), name: .appleInterfaceThemeChangedNotification, object: nil) + statusBarView = WebStatusBarView() + statusBarView.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(statusBarView) + NSLayoutConstraint.activate([ + self.view.leadingAnchor.constraint(equalTo: statusBarView.leadingAnchor, constant: -6), + self.view.trailingAnchor.constraint(greaterThanOrEqualTo: statusBarView.trailingAnchor, constant: 6), + self.view.bottomAnchor.constraint(equalTo: statusBarView.bottomAnchor, constant: 2), + statusBarView.heightAnchor.constraint(equalToConstant: 20) + ]) + loadWebView() } @@ -178,6 +192,12 @@ extension WebViewController: WKScriptMessageHandler { return case MessageName.imageWasClicked: return + case MessageName.mouseDidEnter: + if let link = message.body as? String { + statusBarView.mouseoverLink = link + } + case MessageName.mouseDidExit: + statusBarView.mouseoverLink = nil case MessageName.showFeedInspector: return default: @@ -201,7 +221,7 @@ private extension WebViewController { // Add the webview webView.translatesAutoresizingMaskIntoConstraints = false - self.view.addSubview(webView) + self.view.addSubview(webView, positioned: .below, relativeTo: self.statusBarView) NSLayoutConstraint.activate([ self.view.leadingAnchor.constraint(equalTo: webView.leadingAnchor), self.view.trailingAnchor.constraint(equalTo: webView.trailingAnchor), @@ -211,6 +231,8 @@ private extension WebViewController { webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked) webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.mouseDidEnter) + webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.mouseDidExit) webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.showFeedInspector) self.renderPage(webView) diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 020deeeb6..36b3086de 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -333,6 +333,7 @@ 51B54AB324B5AC830014348B /* ArticleExtractorButtonState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5177472124B38CAE00EB0F74 /* ArticleExtractorButtonState.swift */; }; 51B54AB624B5B33C0014348B /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B54AB524B5B33C0014348B /* WebViewController.swift */; }; 51B54ABC24B5BEF20014348B /* ArticleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B54ABB24B5BEF20014348B /* ArticleView.swift */; }; + 51B54B6724B6A7960014348B /* WebStatusBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B54B6624B6A7960014348B /* WebStatusBarView.swift */; }; 51B5C87723F22B8200032075 /* ExtensionContainers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87623F22B8200032075 /* ExtensionContainers.swift */; }; 51B5C87B23F2317700032075 /* ExtensionFeedAddRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87A23F2317700032075 /* ExtensionFeedAddRequest.swift */; }; 51B5C87D23F2346200032075 /* ExtensionContainersFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87C23F2346200032075 /* ExtensionContainersFile.swift */; }; @@ -1981,6 +1982,7 @@ 51B54A6824B54A490014348B /* IconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconView.swift; sourceTree = "<group>"; }; 51B54AB524B5B33C0014348B /* WebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewController.swift; sourceTree = "<group>"; }; 51B54ABB24B5BEF20014348B /* ArticleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleView.swift; sourceTree = "<group>"; }; + 51B54B6624B6A7960014348B /* WebStatusBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebStatusBarView.swift; sourceTree = "<group>"; }; 51B5C87623F22B8200032075 /* ExtensionContainers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionContainers.swift; sourceTree = "<group>"; }; 51B5C87A23F2317700032075 /* ExtensionFeedAddRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionFeedAddRequest.swift; sourceTree = "<group>"; }; 51B5C87C23F2346200032075 /* ExtensionContainersFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionContainersFile.swift; sourceTree = "<group>"; }; @@ -2734,9 +2736,10 @@ 5177470C24B2FF3B00EB0F74 /* Article */ = { isa = PBXGroup; children = ( - 51B54AB524B5B33C0014348B /* WebViewController.swift */, - 51B54A6824B54A490014348B /* IconView.swift */, 51B54ABB24B5BEF20014348B /* ArticleView.swift */, + 51B54A6824B54A490014348B /* IconView.swift */, + 51B54B6624B6A7960014348B /* WebStatusBarView.swift */, + 51B54AB524B5B33C0014348B /* WebViewController.swift */, ); path = Article; sourceTree = "<group>"; @@ -4135,46 +4138,46 @@ TargetAttributes = { 51314636235A7BBE00387FDC = { CreatedOnToolsVersion = 11.2; - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; LastSwiftMigration = 1120; ProvisioningStyle = Automatic; }; 513C5CE5232571C2003D4054 = { CreatedOnToolsVersion = 11.0; - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; }; 518B2ED12351B3DD00400001 = { CreatedOnToolsVersion = 11.2; - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; TestTargetID = 840D617B2029031C009BC708; }; 51C0513C24A77DF800194D5E = { CreatedOnToolsVersion = 12.0; - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; }; 51C0514324A77DF800194D5E = { CreatedOnToolsVersion = 12.0; - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; }; 6581C73220CED60000F4AD34 = { - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; }; 65ED3FA2235DEF6C0081F399 = { - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; }; 65ED4090235DEF770081F399 = { - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { @@ -4184,7 +4187,7 @@ }; 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.HardenedRuntime = { @@ -4194,7 +4197,7 @@ }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = FQLBNX3GP7; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; @@ -5184,6 +5187,7 @@ 51E4991F24A8094300B667CB /* RSImage-AppIcons.swift in Sources */, 51A5769724AE617200078888 /* ArticleContainerView.swift in Sources */, 51E4991224A808FB00B667CB /* AddWebFeedDefaultContainer.swift in Sources */, + 51B54B6724B6A7960014348B /* WebStatusBarView.swift in Sources */, 51E4993E24A870F900B667CB /* UserNotificationManager.swift in Sources */, 51E4992E24A8676300B667CB /* FetchRequestQueue.swift in Sources */, 51E498CF24A8085D00B667CB /* SmartFeed.swift in Sources */,