Implement the Feed Directory’s split view delegate.

This commit is contained in:
Brent Simmons 2017-12-28 13:37:52 -08:00
parent f0f24c0a99
commit 7ae2345073
6 changed files with 170 additions and 92 deletions

View File

@ -38,7 +38,7 @@
846E77411F6EF6A100A165E2 /* Database.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846E77211F6EF5D100A165E2 /* Database.framework */; }; 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, ); }; }; 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 */; }; 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 */; }; 848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */; };
849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */; }; 849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */; };
849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97511ED9EAC0007D329B /* AddFeedController.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 = "<group>"; }; 846E77161F6EF5D000A165E2 /* Database.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Database.xcodeproj; path = Frameworks/Database/Database.xcodeproj; sourceTree = "<group>"; };
846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; sourceTree = "<group>"; }; 846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; sourceTree = "<group>"; };
84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkReadOrUnreadCommand.swift; sourceTree = "<group>"; }; 84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkReadOrUnreadCommand.swift; sourceTree = "<group>"; };
847C4C9B1FF44F790090D517 /* FeedListSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListSplitViewController.swift; sourceTree = "<group>"; }; 847C4C9B1FF44F790090D517 /* FeedListSplitViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListSplitViewDelegate.swift; sourceTree = "<group>"; };
848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconDownloader.swift; sourceTree = "<group>"; }; 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconDownloader.swift; sourceTree = "<group>"; };
849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFolderWindowController.swift; sourceTree = "<group>"; }; 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFolderWindowController.swift; sourceTree = "<group>"; };
849A97511ED9EAC0007D329B /* AddFeedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedController.swift; path = AddFeed/AddFeedController.swift; sourceTree = "<group>"; }; 849A97511ED9EAC0007D329B /* AddFeedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedController.swift; path = AddFeed/AddFeedController.swift; sourceTree = "<group>"; };
@ -791,9 +791,9 @@
children = ( children = (
84B2FAA31FF47BAC009C4878 /* FeedListWindow.xib */, 84B2FAA31FF47BAC009C4878 /* FeedListWindow.xib */,
849A978C1ED9EE4D007D329B /* FeedListWindowController.swift */, 849A978C1ED9EE4D007D329B /* FeedListWindowController.swift */,
847C4C9B1FF44F790090D517 /* FeedListSplitViewDelegate.swift */,
84F204CD1FAACB660076E152 /* FeedListViewController.swift */, 84F204CD1FAACB660076E152 /* FeedListViewController.swift */,
84F204DD1FAACB8B0076E152 /* FeedListTimelineViewController.swift */, 84F204DD1FAACB8B0076E152 /* FeedListTimelineViewController.swift */,
847C4C9B1FF44F790090D517 /* FeedListSplitViewController.swift */,
84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */, 84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */,
84B99C661FAE35E600ECDEDB /* FeedListTreeControllerDelegate.swift */, 84B99C661FAE35E600ECDEDB /* FeedListTreeControllerDelegate.swift */,
84B99C681FAE36B800ECDEDB /* FeedListFolder.swift */, 84B99C681FAE36B800ECDEDB /* FeedListFolder.swift */,
@ -1444,7 +1444,7 @@
84B99C6B1FAE370B00ECDEDB /* FeedListFeed.swift in Sources */, 84B99C6B1FAE370B00ECDEDB /* FeedListFeed.swift in Sources */,
842611A01FCB72600086A189 /* FeaturedImageDownloader.swift in Sources */, 842611A01FCB72600086A189 /* FeaturedImageDownloader.swift in Sources */,
849A97781ED9EC04007D329B /* TimelineCellLayout.swift in Sources */, 849A97781ED9EC04007D329B /* TimelineCellLayout.swift in Sources */,
847C4C9C1FF44F790090D517 /* FeedListSplitViewController.swift in Sources */, 847C4C9C1FF44F790090D517 /* FeedListSplitViewDelegate.swift in Sources */,
849A976C1ED9EBC8007D329B /* TimelineTableRowView.swift in Sources */, 849A976C1ED9EBC8007D329B /* TimelineTableRowView.swift in Sources */,
849A977B1ED9EC04007D329B /* UnreadIndicatorView.swift in Sources */, 849A977B1ED9EC04007D329B /* UnreadIndicatorView.swift in Sources */,
84B99C9D1FAE83C600ECDEDB /* DeleteFromSidebarCommand.swift in Sources */, 84B99C9D1FAE83C600ECDEDB /* DeleteFromSidebarCommand.swift in Sources */,

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -25,11 +25,9 @@ final class FeedListViewController: NSViewController {
sidebarCellAppearance = SidebarCellAppearance(theme: appDelegate.currentTheme, fontSize: AppDefaults.shared.sidebarFontSize) sidebarCellAppearance = SidebarCellAppearance(theme: appDelegate.currentTheme, fontSize: AppDefaults.shared.sidebarFontSize)
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
outlineView.needsLayout = true
} }
override func viewWillLayout() {
super.viewWillLayout()
}
// MARK: - Notifications // MARK: - Notifications
@objc func faviconDidBecomeAvailable(_ note: Notification) { @objc func faviconDidBecomeAvailable(_ note: Notification) {

View File

@ -41,18 +41,12 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</searchFieldCell> </searchFieldCell>
</searchField> </searchField>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="wey-fS-dNY">
<rect key="frame" x="0.0" y="-2" width="128" height="5"/>
</box>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstAttribute="height" constant="48" id="62f-Ja-njN"/> <constraint firstAttribute="height" constant="48" id="62f-Ja-njN"/>
<constraint firstItem="wey-fS-dNY" firstAttribute="leading" secondItem="Sxr-eg-boM" secondAttribute="leading" id="8Ls-P9-pxs"/>
<constraint firstItem="791-YX-E38" firstAttribute="centerY" secondItem="Sxr-eg-boM" secondAttribute="centerY" id="Z9W-jw-zkI"/> <constraint firstItem="791-YX-E38" firstAttribute="centerY" secondItem="Sxr-eg-boM" secondAttribute="centerY" id="Z9W-jw-zkI"/>
<constraint firstItem="791-YX-E38" firstAttribute="leading" secondItem="Sxr-eg-boM" secondAttribute="leading" constant="20" symbolic="YES" id="d50-xe-TO4"/> <constraint firstItem="791-YX-E38" firstAttribute="leading" secondItem="Sxr-eg-boM" secondAttribute="leading" constant="20" symbolic="YES" id="d50-xe-TO4"/>
<constraint firstAttribute="trailing" secondItem="791-YX-E38" secondAttribute="trailing" constant="20" symbolic="YES" id="i0h-PY-4SC"/> <constraint firstAttribute="trailing" secondItem="791-YX-E38" secondAttribute="trailing" constant="20" symbolic="YES" id="i0h-PY-4SC"/>
<constraint firstAttribute="bottom" secondItem="wey-fS-dNY" secondAttribute="bottom" id="plH-tG-c0g"/>
<constraint firstAttribute="trailing" secondItem="wey-fS-dNY" secondAttribute="trailing" id="xao-N6-CQq"/>
</constraints> </constraints>
</customView> </customView>
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="26" horizontalPageScroll="10" verticalLineScroll="26" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="OF2-9S-uq1"> <scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="26" horizontalPageScroll="10" verticalLineScroll="26" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="OF2-9S-uq1">
@ -160,9 +154,12 @@
</customView> </customView>
</subviews> </subviews>
<holdingPriorities> <holdingPriorities>
<real value="260"/> <real value="489"/>
<real value="250"/> <real value="251"/>
</holdingPriorities> </holdingPriorities>
<connections>
<outlet property="delegate" destination="foD-R8-nCX" id="8ds-kY-h3r"/>
</connections>
</splitView> </splitView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="6se-0m-JxY" customClass="FeedListControlsView" customModule="Evergreen" customModuleProvider="target"> <customView translatesAutoresizingMaskIntoConstraints="NO" id="6se-0m-JxY" customClass="FeedListControlsView" customModule="Evergreen" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="480" height="32"/> <rect key="frame" x="0.0" y="0.0" width="480" height="32"/>
@ -214,6 +211,13 @@
<outlet property="view" destination="OF2-9S-uq1" id="yfP-zX-T6z"/> <outlet property="view" destination="OF2-9S-uq1" id="yfP-zX-T6z"/>
</connections> </connections>
</viewController> </viewController>
<customObject id="foD-R8-nCX" customClass="FeedListSplitViewDelegate" customModule="Evergreen" customModuleProvider="target">
<connections>
<outlet property="sidebarView" destination="fSs-xC-S8k" id="EPm-FO-BI3"/>
<outlet property="splitView" destination="LmW-op-ndf" id="X2z-RD-rkV"/>
<outlet property="timelineView" destination="asI-cc-kYO" id="sw2-Qf-oMo"/>
</connections>
</customObject>
</objects> </objects>
<resources> <resources>
<image name="NSActionTemplate" width="14" height="14"/> <image name="NSActionTemplate" width="14" height="14"/>

View File

@ -133,12 +133,17 @@
<dict> <dict>
<key>sidebar</key> <key>sidebar</key>
<dict> <dict>
<key>initialWidth</key> <key>bestWidth</key>
<integer>285</integer> <integer>285</integer>
<key>minimumThickness</key> <key>minimumThickness</key>
<string>220</string> <integer>240</integer>
<key>maximumThickness</key> <key>maximumThickness</key>
<string>512</string> <integer>512</integer>
</dict>
<key>timeline</key>
<dict>
<key>minimumThickness</key>
<integer>240</integer>
</dict> </dict>
</dict> </dict>
</dict> </dict>