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 @@
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 = ""; };
51B54AB524B5B33C0014348B /* WebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewController.swift; sourceTree = ""; };
51B54ABB24B5BEF20014348B /* ArticleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleView.swift; sourceTree = ""; };
+ 51B54B6624B6A7960014348B /* WebStatusBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebStatusBarView.swift; sourceTree = ""; };
51B5C87623F22B8200032075 /* ExtensionContainers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionContainers.swift; sourceTree = ""; };
51B5C87A23F2317700032075 /* ExtensionFeedAddRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionFeedAddRequest.swift; sourceTree = ""; };
51B5C87C23F2346200032075 /* ExtensionContainersFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionContainersFile.swift; sourceTree = ""; };
@@ -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 = "";
@@ -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 */,