mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2025-01-22 15:20:00 +01:00
Show no-selection or multiple-selection text in the detail view when appropriate.
This commit is contained in:
parent
2bb3d5c6ca
commit
1ba2306b9c
@ -2,7 +2,9 @@
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14092" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14092"/>
|
||||
<capability name="box content view" minToolsVersion="7.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
<capability name="system font weights other than Regular or Bold" minToolsVersion="7.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Window Controller-->
|
||||
@ -699,11 +701,61 @@
|
||||
</customView>
|
||||
<connections>
|
||||
<outlet property="containerView" destination="cJ9-6s-66u" id="gXc-Pz-9sQ"/>
|
||||
<outlet property="noSelectionView" destination="aEc-1k-VmJ" id="KLS-ln-T1K"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="vzM-Vn-mEn" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
<customView id="aEc-1k-VmJ" customClass="NoSelectionView" customModule="Evergreen" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="613" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<box boxType="custom" borderType="none" borderWidth="0.0" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="Zb8-hH-gIZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="613" height="96"/>
|
||||
<view key="contentView" id="Gmh-qL-lA5">
|
||||
<rect key="frame" x="0.0" y="0.0" width="613" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ECt-Wi-zkb">
|
||||
<rect key="frame" x="266" y="40" width="109" height="22"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="No selection" usesSingleLineMode="YES" id="oNn-so-m7W">
|
||||
<font key="font" metaFont="systemSemibold" size="18"/>
|
||||
<color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1AW-Ay-XS8">
|
||||
<rect key="frame" x="408" y="39" width="154" height="22"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Multiple selection" usesSingleLineMode="YES" id="vhD-gM-NUy">
|
||||
<font key="font" metaFont="systemSemibold" size="18"/>
|
||||
<color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="1AW-Ay-XS8" firstAttribute="centerY" secondItem="Gmh-qL-lA5" secondAttribute="centerY" constant="-64" id="9uN-K3-d35"/>
|
||||
<constraint firstItem="ECt-Wi-zkb" firstAttribute="centerX" secondItem="Gmh-qL-lA5" secondAttribute="centerX" id="QDc-Y3-Nn4"/>
|
||||
<constraint firstItem="1AW-Ay-XS8" firstAttribute="centerX" secondItem="Gmh-qL-lA5" secondAttribute="centerX" id="QV5-qP-Ys6"/>
|
||||
<constraint firstItem="ECt-Wi-zkb" firstAttribute="centerY" secondItem="Gmh-qL-lA5" secondAttribute="centerY" constant="-64" id="yQr-wJ-pBL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<color key="borderColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<color key="fillColor" white="0.95999999999999996" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</box>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Zb8-hH-gIZ" firstAttribute="leading" secondItem="aEc-1k-VmJ" secondAttribute="leading" id="5Ay-6o-LZ1"/>
|
||||
<constraint firstItem="Zb8-hH-gIZ" firstAttribute="top" secondItem="aEc-1k-VmJ" secondAttribute="top" id="Wb7-Zp-hj0"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Zb8-hH-gIZ" secondAttribute="trailing" id="cYM-up-kUN"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Zb8-hH-gIZ" secondAttribute="bottom" id="gCy-5R-aK2"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="multipleSelectionLabel" destination="1AW-Ay-XS8" id="gK2-Ya-Dz7"/>
|
||||
<outlet property="noSelectionLabel" destination="ECt-Wi-zkb" id="Dvq-DU-MxC"/>
|
||||
</connections>
|
||||
</customView>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="62" y="774"/>
|
||||
<point key="canvasLocation" x="68" y="946"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
|
@ -12,14 +12,30 @@ import RSCore
|
||||
import Data
|
||||
import RSWeb
|
||||
|
||||
final class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDelegate {
|
||||
final class DetailViewController: NSViewController, WKUIDelegate {
|
||||
|
||||
@IBOutlet var containerView: DetailContainerView!
|
||||
@IBOutlet var noSelectionView: NoSelectionView!
|
||||
|
||||
var webview: DetailWebView!
|
||||
var noSelectionView: NoSelectionView!
|
||||
|
||||
var article: Article? {
|
||||
var articles: [Article]? {
|
||||
didSet {
|
||||
if let articles = articles, articles.count == 1 {
|
||||
article = articles.first!
|
||||
return
|
||||
}
|
||||
article = nil
|
||||
if let _ = articles {
|
||||
noSelectionView.showMultipleSelection()
|
||||
}
|
||||
else {
|
||||
noSelectionView.showNoSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var article: Article? {
|
||||
didSet {
|
||||
reloadHTML()
|
||||
showOrHideWebView()
|
||||
@ -62,8 +78,6 @@ final class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDe
|
||||
webview.customUserAgent = userAgent
|
||||
}
|
||||
|
||||
noSelectionView = NoSelectionView(frame: self.view.bounds)
|
||||
|
||||
containerView.viewController = self
|
||||
|
||||
showOrHideWebView()
|
||||
@ -91,7 +105,7 @@ final class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDe
|
||||
webview.scrollPageDown(sender)
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
// MARK: - Notifications
|
||||
|
||||
@objc func timelineSelectionDidChange(_ notification: Notification) {
|
||||
|
||||
@ -102,8 +116,8 @@ final class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDe
|
||||
return
|
||||
}
|
||||
|
||||
let timelineArticle = userInfo[UserInfoKey.article] as? Article
|
||||
article = timelineArticle
|
||||
let timelineArticles = userInfo[UserInfoKey.articles] as? ArticleArray
|
||||
articles = timelineArticles
|
||||
}
|
||||
|
||||
func viewWillStartLiveResize() {
|
||||
@ -115,39 +129,11 @@ final class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDe
|
||||
|
||||
webview.evaluateJavaScript("document.body.style.overflow = 'visible';", completionHandler: nil)
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func reloadHTML() {
|
||||
|
||||
if let article = article {
|
||||
let articleRenderer = ArticleRenderer(article: article, style: ArticleStylesManager.shared.currentStyle)
|
||||
webview.loadHTMLString(articleRenderer.html, baseURL: articleRenderer.baseURL)
|
||||
}
|
||||
else {
|
||||
webview.loadHTMLString("", baseURL: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func showOrHideWebView() {
|
||||
// MARK: - WKNavigationDelegate
|
||||
|
||||
if let _ = article {
|
||||
switchToView(webview)
|
||||
}
|
||||
else {
|
||||
switchToView(noSelectionView)
|
||||
}
|
||||
}
|
||||
|
||||
private func switchToView(_ view: NSView) {
|
||||
|
||||
if containerView.contentView == view {
|
||||
return
|
||||
}
|
||||
containerView.contentView = view
|
||||
}
|
||||
|
||||
// MARK: WKNavigationDelegate
|
||||
extension DetailViewController: WKNavigationDelegate {
|
||||
|
||||
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||
|
||||
@ -165,6 +151,8 @@ final class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDe
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WKScriptMessageHandler
|
||||
|
||||
extension DetailViewController: WKScriptMessageHandler {
|
||||
|
||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||
@ -200,8 +188,39 @@ extension DetailViewController: WKScriptMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private extension DetailViewController {
|
||||
|
||||
func reloadHTML() {
|
||||
|
||||
if let article = article {
|
||||
let articleRenderer = ArticleRenderer(article: article, style: ArticleStylesManager.shared.currentStyle)
|
||||
webview.loadHTMLString(articleRenderer.html, baseURL: articleRenderer.baseURL)
|
||||
}
|
||||
else {
|
||||
webview.loadHTMLString("", baseURL: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func showOrHideWebView() {
|
||||
|
||||
if let _ = article {
|
||||
switchToView(webview)
|
||||
}
|
||||
else {
|
||||
switchToView(noSelectionView)
|
||||
}
|
||||
}
|
||||
|
||||
func switchToView(_ view: NSView) {
|
||||
|
||||
if containerView.contentView == view {
|
||||
return
|
||||
}
|
||||
containerView.contentView = view
|
||||
}
|
||||
|
||||
func fetchScrollInfo(_ callback: @escaping (ScrollInfo?) -> Void) {
|
||||
|
||||
let javascriptString = "var x = {contentHeight: document.body.scrollHeight, offsetY: document.body.scrollTop}; x"
|
||||
@ -222,6 +241,8 @@ private extension DetailViewController {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
final class DetailContainerView: NSView {
|
||||
|
||||
@IBOutlet var detailStatusBarView: DetailStatusBarView!
|
||||
@ -270,28 +291,28 @@ final class DetailContainerView: NSView {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
final class NoSelectionView: NSView {
|
||||
|
||||
private var didConfigureLayer = false
|
||||
@IBOutlet var noSelectionLabel: NSTextField!
|
||||
@IBOutlet var multipleSelectionLabel: NSTextField!
|
||||
|
||||
override var wantsUpdateLayer: Bool {
|
||||
return true
|
||||
func showMultipleSelection() {
|
||||
|
||||
noSelectionLabel.isHidden = true
|
||||
multipleSelectionLabel.isHidden = false
|
||||
}
|
||||
|
||||
override func updateLayer() {
|
||||
func showNoSelection() {
|
||||
|
||||
guard !didConfigureLayer else {
|
||||
return
|
||||
}
|
||||
if let layer = layer {
|
||||
// let color = appDelegate.currentTheme.color(forKey: "MainWindow.Detail.noSelectionView.backgroundColor")
|
||||
let color = NSColor(calibratedWhite: 0.96, alpha: 1.0)
|
||||
layer.backgroundColor = color.cgColor
|
||||
didConfigureLayer = true
|
||||
}
|
||||
noSelectionLabel.isHidden = false
|
||||
multipleSelectionLabel.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
private struct ScrollInfo {
|
||||
|
||||
let contentHeight: CGFloat
|
||||
|
@ -110,6 +110,12 @@ extension Array where Element == Article {
|
||||
|
||||
return anyArticlePassesTest { !$0.status.starred }
|
||||
}
|
||||
|
||||
func unreadArticles() -> [Article]? {
|
||||
|
||||
let articles = self.filter{ !$0.status.read }
|
||||
return articles.isEmpty ? nil : articles
|
||||
}
|
||||
}
|
||||
|
||||
private extension Array where Element == Article {
|
||||
|
@ -516,30 +516,28 @@ extension TimelineViewController: NSTableViewDelegate {
|
||||
|
||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||
|
||||
tableView.redrawGrid()
|
||||
// tableView.redrawGrid()
|
||||
|
||||
let selectedRow = tableView.selectedRow
|
||||
if selectedRow < 0 || selectedRow == NSNotFound || tableView.numberOfSelectedRows != 1 {
|
||||
if selectedArticles.isEmpty {
|
||||
postTimelineSelectionDidChangeNotification(nil)
|
||||
return
|
||||
}
|
||||
|
||||
if let selectedArticle = articles.articleAtRow(selectedRow) {
|
||||
if (!selectedArticle.status.read) {
|
||||
markArticles(Set([selectedArticle]), statusKey: .read, flag: true)
|
||||
}
|
||||
postTimelineSelectionDidChangeNotification(selectedArticle)
|
||||
}
|
||||
else {
|
||||
postTimelineSelectionDidChangeNotification(nil)
|
||||
if selectedArticles.count == 1 {
|
||||
let article = selectedArticles.first!
|
||||
if !article.status.read {
|
||||
markArticles(Set([article]), statusKey: .read, flag: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func postTimelineSelectionDidChangeNotification(_ selectedArticle: Article?) {
|
||||
postTimelineSelectionDidChangeNotification(selectedArticles)
|
||||
}
|
||||
|
||||
private func postTimelineSelectionDidChangeNotification(_ selectedArticles: ArticleArray?) {
|
||||
|
||||
var userInfo = UserInfoDictionary()
|
||||
if let article = selectedArticle {
|
||||
userInfo[UserInfoKey.article] = article
|
||||
if let selectedArticles = selectedArticles {
|
||||
userInfo[UserInfoKey.articles] = selectedArticles
|
||||
}
|
||||
userInfo[UserInfoKey.view] = tableView
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user