From 7ae23450732461ec0cde2cbf3c5364eb71f299e8 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Thu, 28 Dec 2017 13:37:52 -0800 Subject: [PATCH] =?UTF-8?q?Implement=20the=20Feed=20Directory=E2=80=99s=20?= =?UTF-8?q?split=20view=20delegate.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Evergreen.xcodeproj/project.pbxproj | 8 +- .../FeedListSplitViewController.swift | 74 --------- .../FeedList/FeedListSplitViewDelegate.swift | 145 ++++++++++++++++++ .../FeedList/FeedListViewController.swift | 4 +- Evergreen/FeedList/FeedListWindow.xib | 20 ++- Evergreen/Resources/DB5.plist | 11 +- 6 files changed, 170 insertions(+), 92 deletions(-) delete mode 100644 Evergreen/FeedList/FeedListSplitViewController.swift create mode 100644 Evergreen/FeedList/FeedListSplitViewDelegate.swift diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index 95ef17b16..27dca7f64 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -38,7 +38,7 @@ 846E77411F6EF6A100A165E2 /* Database.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846E77211F6EF5D100A165E2 /* Database.framework */; }; 846E77421F6EF6A100A165E2 /* Database.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 846E77211F6EF5D100A165E2 /* Database.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 84702AA41FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */; }; - 847C4C9C1FF44F790090D517 /* FeedListSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847C4C9B1FF44F790090D517 /* FeedListSplitViewController.swift */; }; + 847C4C9C1FF44F790090D517 /* FeedListSplitViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847C4C9B1FF44F790090D517 /* FeedListSplitViewDelegate.swift */; }; 848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */; }; 849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */; }; 849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97511ED9EAC0007D329B /* AddFeedController.swift */; }; @@ -444,7 +444,7 @@ 846E77161F6EF5D000A165E2 /* Database.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Database.xcodeproj; path = Frameworks/Database/Database.xcodeproj; sourceTree = ""; }; 846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; sourceTree = ""; }; 84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkReadOrUnreadCommand.swift; sourceTree = ""; }; - 847C4C9B1FF44F790090D517 /* FeedListSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListSplitViewController.swift; sourceTree = ""; }; + 847C4C9B1FF44F790090D517 /* FeedListSplitViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListSplitViewDelegate.swift; sourceTree = ""; }; 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconDownloader.swift; sourceTree = ""; }; 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFolderWindowController.swift; sourceTree = ""; }; 849A97511ED9EAC0007D329B /* AddFeedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedController.swift; path = AddFeed/AddFeedController.swift; sourceTree = ""; }; @@ -791,9 +791,9 @@ children = ( 84B2FAA31FF47BAC009C4878 /* FeedListWindow.xib */, 849A978C1ED9EE4D007D329B /* FeedListWindowController.swift */, + 847C4C9B1FF44F790090D517 /* FeedListSplitViewDelegate.swift */, 84F204CD1FAACB660076E152 /* FeedListViewController.swift */, 84F204DD1FAACB8B0076E152 /* FeedListTimelineViewController.swift */, - 847C4C9B1FF44F790090D517 /* FeedListSplitViewController.swift */, 84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */, 84B99C661FAE35E600ECDEDB /* FeedListTreeControllerDelegate.swift */, 84B99C681FAE36B800ECDEDB /* FeedListFolder.swift */, @@ -1444,7 +1444,7 @@ 84B99C6B1FAE370B00ECDEDB /* FeedListFeed.swift in Sources */, 842611A01FCB72600086A189 /* FeaturedImageDownloader.swift in Sources */, 849A97781ED9EC04007D329B /* TimelineCellLayout.swift in Sources */, - 847C4C9C1FF44F790090D517 /* FeedListSplitViewController.swift in Sources */, + 847C4C9C1FF44F790090D517 /* FeedListSplitViewDelegate.swift in Sources */, 849A976C1ED9EBC8007D329B /* TimelineTableRowView.swift in Sources */, 849A977B1ED9EC04007D329B /* UnreadIndicatorView.swift in Sources */, 84B99C9D1FAE83C600ECDEDB /* DeleteFromSidebarCommand.swift in Sources */, diff --git a/Evergreen/FeedList/FeedListSplitViewController.swift b/Evergreen/FeedList/FeedListSplitViewController.swift deleted file mode 100644 index ef0f9c178..000000000 --- a/Evergreen/FeedList/FeedListSplitViewController.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// FeedListSplitViewController.swift -// Evergreen -// -// Created by Brent Simmons on 12/27/17. -// Copyright © 2017 Ranchero Software. All rights reserved. -// - -import Cocoa -import DB5 - -final class FeedListSplitViewController: NSSplitViewController { - - @IBOutlet var sidebarSplitViewItem: NSSplitViewItem! - @IBOutlet var timelineSplitViewItem: NSSplitViewItem! - - private var sidebarView: NSView { - return sidebarSplitViewItem.viewController.view - } - - private var timelineView: NSView { - return timelineSplitViewItem.viewController.view - } - - override func viewDidLoad() { - - super.viewDidLoad() - - sidebarView.translatesAutoresizingMaskIntoConstraints = false - timelineView.translatesAutoresizingMaskIntoConstraints = false - - sidebarSplitViewItem.preferredThicknessFraction = NSSplitViewItem.unspecifiedDimension - sidebarSplitViewItem.canCollapse = false - timelineSplitViewItem.preferredThicknessFraction = NSSplitViewItem.unspecifiedDimension - timelineSplitViewItem.canCollapse = false - - let sidebarMinimumThickness = appDelegate.currentTheme.float(forKey: "FeedDirectory.sidebar.minimumThickness") - sidebarSplitViewItem.minimumThickness = sidebarMinimumThickness - let sidebarMaximumThickness = appDelegate.currentTheme.float(forKey: "FeedDirectory.sidebar.maximumThickness") - timelineSplitViewItem.maximumThickness = sidebarMaximumThickness - - let sidebarWidth = appDelegate.currentTheme.float(forKey: "FeedDirectory.sidebar.initialWidth") - splitView.setPosition(sidebarWidth, ofDividerAt: 0) - - let constraints = timelineView.constraintsAffectingLayout(for: .horizontal) - print(constraints) - } - - // MARK: - NSSplitViewDelegate - - override func splitView(_ splitView: NSSplitView, canCollapseSubview subview: NSView) -> Bool { - - super.splitView(splitView, canCollapseSubview: subview) - let constraints = timelineView.constraintsAffectingLayout(for: .horizontal) - print(constraints) - return false - } - - override func splitView(_ splitView: NSSplitView, shouldCollapseSubview subview: NSView, forDoubleClickOnDividerAt dividerIndex: Int) -> Bool { - - super.splitView(splitView, shouldCollapseSubview: view, forDoubleClickOnDividerAt: dividerIndex) - let constraints = timelineView.constraintsAffectingLayout(for: .horizontal) - print(constraints) - return false - } - - override func splitView(_ splitView: NSSplitView, shouldHideDividerAt dividerIndex: Int) -> Bool { - - super.splitView(splitView, shouldHideDividerAt: dividerIndex) - let constraints = timelineView.constraintsAffectingLayout(for: .horizontal) - print(constraints) - return false - } -} diff --git a/Evergreen/FeedList/FeedListSplitViewDelegate.swift b/Evergreen/FeedList/FeedListSplitViewDelegate.swift new file mode 100644 index 000000000..1fe9da91f --- /dev/null +++ b/Evergreen/FeedList/FeedListSplitViewDelegate.swift @@ -0,0 +1,145 @@ +// +// FeedListSplitViewDelegate.swift +// Evergreen +// +// Created by Brent Simmons on 12/27/17. +// Copyright © 2017 Ranchero Software. All rights reserved. +// + +import Cocoa +import DB5 +import RSCore + +final class FeedListSplitViewDelegate: NSObject, NSSplitViewDelegate { + + @IBOutlet weak var sidebarView: NSView? + @IBOutlet weak var timelineView: NSView? + @IBOutlet weak var splitView: NSSplitView? + let sidebarMinimumThickness: CGFloat + let sidebarMaximumThickness: CGFloat + let sidebarBestWidth: CGFloat + let timelineMinimumThickness: CGFloat + + override init() { + + sidebarMinimumThickness = appDelegate.currentTheme.float(forKey: "FeedDirectory.sidebar.minimumThickness") + sidebarMaximumThickness = appDelegate.currentTheme.float(forKey: "FeedDirectory.sidebar.maximumThickness") + sidebarBestWidth = appDelegate.currentTheme.float(forKey: "FeedDirectory.sidebar.bestWidth") + timelineMinimumThickness = appDelegate.currentTheme.float(forKey: "FeedDirectory.timeline.minimumThickness") + + super.init() + } + +// override func awakeFromNib() { +// +// let highestAllowedPriority = NSLayoutConstraint.Priority(rawValue: NSLayoutConstraint.Priority.dragThatCannotResizeWindow.rawValue - 1) +// splitView?.setHoldingPriority(highestAllowedPriority, forSubviewAt: 0) +// splitView?.setHoldingPriority(.defaultLow, forSubviewAt: 1) +// } + + // MARK: - NSSplitViewDelegate + + func splitView(_ splitView: NSSplitView, canCollapseSubview subview: NSView) -> Bool { + + return false + } + + func splitView(_ splitView: NSSplitView, shouldCollapseSubview subview: NSView, forDoubleClickOnDividerAt dividerIndex: Int) -> Bool { + + return false + } + + func splitView(_ splitView: NSSplitView, shouldHideDividerAt dividerIndex: Int) -> Bool { + + return false + } + + func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { + + return sidebarMinimumThickness + } + + func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { + + return sidebarMaximumThickness + } + + func splitView(_ splitView: NSSplitView, constrainSplitPosition proposedPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { + + if proposedPosition < sidebarMinimumThickness { + return sidebarMinimumThickness + } + + // If proposedPosition makes timeline too small, then adjust. + let proposedTimelineWidth = proposedTimelineThickness(splitView, proposedPosition) + if proposedTimelineWidth < timelineMinimumThickness { + return min(sidebarMaximumThickness, proposedPosition - (timelineMinimumThickness - proposedTimelineWidth)) + } + + if proposedPosition > sidebarMaximumThickness { + return sidebarMaximumThickness + } + + return proposedPosition + } + +// func splitView(_ splitView: NSSplitView, shouldAdjustSizeOfSubview view: NSView) -> Bool { +// +// guard let sidebarView = sidebarView, let timelineView = timelineView else { +// assertionFailure("Expected sidebarView and timelineView as non-nil, but at least one is nil.") +// return true +// } +// +// if view === sidebarView { +// if timelineView.frame.width <= timelineMinimumThickness { +// return true +// } +// return false +// } +// +// return true +// } + + func splitView(_ splitView: NSSplitView, resizeSubviewsWithOldSize oldSize: NSSize) { + + guard let sidebarView = sidebarView, let timelineView = timelineView else { + assertionFailure("Expected sidebarView and timelineView as non-nil, but at least one is nil.") + return + } + + let splitViewFrame = splitView.frame + let splitViewWidth = splitViewFrame.width + let dividerThickness = splitView.dividerThickness + + var sidebarViewFrame = sidebarView.frame + sidebarViewFrame.size.height = splitViewFrame.height + var timelineViewFrame = timelineView.frame + timelineViewFrame.size.height = splitViewFrame.height + + sidebarViewFrame.size.width = max(sidebarViewFrame.width, sidebarMinimumThickness) + sidebarViewFrame.size.width = ceil(min(sidebarViewFrame.width, sidebarMaximumThickness)) + timelineViewFrame.size.width = floor(min(timelineViewFrame.width, timelineMinimumThickness)) + + let totalProposedWidth = sidebarViewFrame.width + dividerThickness + timelineViewFrame.width + let difference = splitViewWidth - totalProposedWidth + if difference < 0 { + timelineViewFrame.size.width += difference + if timelineViewFrame.width < 0 { + timelineViewFrame.size.width = 0 + sidebarViewFrame.size.width += difference + } + } + + sidebarView.rs_setFrameIfNotEqual(sidebarViewFrame) + timelineView.rs_setFrameIfNotEqual(timelineViewFrame) + } +} + +private extension FeedListSplitViewDelegate { + + func proposedTimelineThickness(_ splitView: NSSplitView, _ proposedPosition: CGFloat) -> CGFloat { + + return (splitView.frame.width - splitView.dividerThickness) - proposedPosition + } + +} diff --git a/Evergreen/FeedList/FeedListViewController.swift b/Evergreen/FeedList/FeedListViewController.swift index b3a56b2cb..2b22dc60f 100644 --- a/Evergreen/FeedList/FeedListViewController.swift +++ b/Evergreen/FeedList/FeedListViewController.swift @@ -25,11 +25,9 @@ final class FeedListViewController: NSViewController { sidebarCellAppearance = SidebarCellAppearance(theme: appDelegate.currentTheme, fontSize: AppDefaults.shared.sidebarFontSize) NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil) + outlineView.needsLayout = true } - override func viewWillLayout() { - super.viewWillLayout() - } // MARK: - Notifications @objc func faviconDidBecomeAvailable(_ note: Notification) { diff --git a/Evergreen/FeedList/FeedListWindow.xib b/Evergreen/FeedList/FeedListWindow.xib index d48f2a1df..81d418604 100644 --- a/Evergreen/FeedList/FeedListWindow.xib +++ b/Evergreen/FeedList/FeedListWindow.xib @@ -41,18 +41,12 @@ - - - - - - @@ -160,9 +154,12 @@ - - + + + + + @@ -214,6 +211,13 @@ + + + + + + + diff --git a/Evergreen/Resources/DB5.plist b/Evergreen/Resources/DB5.plist index b0f6d6b8b..8399d79f0 100644 --- a/Evergreen/Resources/DB5.plist +++ b/Evergreen/Resources/DB5.plist @@ -133,12 +133,17 @@ sidebar - initialWidth + bestWidth 285 minimumThickness - 220 + 240 maximumThickness - 512 + 512 + + timeline + + minimumThickness + 240