diff --git a/NetNewsWire/AppDefaults.swift b/NetNewsWire/AppDefaults.swift index 43283ae7b..a1788c90a 100644 --- a/NetNewsWire/AppDefaults.swift +++ b/NetNewsWire/AppDefaults.swift @@ -53,6 +53,7 @@ struct AppDefaults { static let timelineSortDirection = "timelineSortDirection" static let detailFontSize = "detailFontSize" static let openInBrowserInBackground = "openInBrowserInBackground" + static let mainWindowWidths = "mainWindowWidths" static let refreshInterval = "refreshInterval" // Hidden prefs @@ -119,6 +120,15 @@ struct AppDefaults { } } + static var mainWindowWidths: [Int]? { + get { + return UserDefaults.standard.object(forKey: Key.mainWindowWidths) as? [Int] + } + set { + UserDefaults.standard.set(newValue, forKey: Key.mainWindowWidths) + } + } + static var refreshInterval: RefreshInterval { get { let rawValue = UserDefaults.standard.integer(forKey: Key.refreshInterval) @@ -144,7 +154,9 @@ struct AppDefaults { // to NetNewsWire preserving this state, we'll force the preference on. If this becomes // an issue, this could be changed to proactively look for whether the default has been // set _by the user_ to false, and respect that default if it is so-set. - UserDefaults.standard.set(true, forKey: "NSQuitAlwaysKeepsWindows") +// UserDefaults.standard.set(true, forKey: "NSQuitAlwaysKeepsWindows") + + // TODO: revisit the above when coming back to state restoration issues. } static func actualFontSize(for fontSize: FontSize) -> CGFloat { diff --git a/NetNewsWire/AppDelegate.swift b/NetNewsWire/AppDelegate.swift index b2faf8c8a..4bf72559e 100644 --- a/NetNewsWire/AppDelegate.swift +++ b/NetNewsWire/AppDelegate.swift @@ -584,6 +584,7 @@ private extension AppDelegate { func saveState() { inspectorWindowController?.saveState() + mainWindowController?.saveState() } func updateSortMenuItems() { diff --git a/NetNewsWire/MainWindow/MainWindowController.swift b/NetNewsWire/MainWindow/MainWindowController.swift index 6ddc8dfe3..8b5b1d01f 100644 --- a/NetNewsWire/MainWindow/MainWindowController.swift +++ b/NetNewsWire/MainWindow/MainWindowController.swift @@ -15,10 +15,11 @@ enum TimelineSourceMode { case regular, search } -class MainWindowController : NSWindowController, NSUserInterfaceValidations, NSWindowDelegate { +class MainWindowController : NSWindowController, NSUserInterfaceValidations { private var sharingServicePickerDelegate: NSSharingServicePickerDelegate? + private let windowAutosaveName = NSWindow.FrameAutosaveName("MainWindow") static var didPositionWindowOnFirstRun = false private var currentFeedOrFolder: AnyObject? { @@ -60,6 +61,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations, NSW window?.titleVisibility = .hidden } + window?.setFrameUsingName(windowAutosaveName, force: true) if AppDefaults.isFirstRun && !MainWindowController.didPositionWindowOnFirstRun { if let window = window { @@ -72,6 +74,9 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations, NSW } detailSplitViewItem?.minimumThickness = CGFloat(MainWindowController.detailViewMinimumThickness) + restoreSplitViewState() + + NotificationCenter.default.addObserver(self, selector: #selector(applicationWillTerminate(_:)), name: NSApplication.willTerminateNotification, object: nil) sidebarViewController = splitViewController?.splitViewItems[0].viewController as? SidebarViewController sidebarViewController!.delegate = self @@ -95,6 +100,11 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations, NSW // MARK: - API + func saveState() { + + saveSplitViewState() + } + func selectedObjectsInSidebar() -> [AnyObject]? { return sidebarViewController?.selectedObjects @@ -102,18 +112,23 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations, NSW // MARK: - Notifications - func window(_ window: NSWindow, willEncodeRestorableState state: NSCoder) { +// func window(_ window: NSWindow, willEncodeRestorableState state: NSCoder) { +// +// saveSplitViewState(to: state) +// } +// +// func window(_ window: NSWindow, didDecodeRestorableState state: NSCoder) { +// +// restoreSplitViewState(from: state) +// +// // Make sure the timeline view is first responder if possible, to start out viewing +// // whatever preserved selection might have been restored +// makeTimelineViewFirstResponder() +// } - saveSplitViewState(to: state) - } - - func window(_ window: NSWindow, didDecodeRestorableState state: NSCoder) { - - restoreSplitViewState(from: state) - - // Make sure the timeline view is first responder if possible, to start out viewing - // whatever preserved selection might have been restored - makeTimelineViewFirstResponder() + @objc func applicationWillTerminate(_ note: Notification) { + saveState() + window?.saveFrame(usingName: windowAutosaveName) } @objc func refreshProgressDidChange(_ note: Notification) { @@ -523,8 +538,6 @@ extension MainWindowController: NSToolbarDelegate { private extension MainWindowController { - static let mainWindowWidthsStateKey = "mainWindowWidths" - var splitViewController: NSSplitViewController? { guard let viewController = contentViewController else { return nil @@ -703,37 +716,24 @@ private extension MainWindowController { } - func saveSplitViewState(to coder: NSCoder) { - + func saveSplitViewState() { // TODO: Update this for multiple windows. - + // Also: use standard state restoration mechanism. guard let splitView = splitViewController?.splitView else { return - } - + } let widths = splitView.arrangedSubviews.map{ Int(floor($0.frame.width)) } - coder.encode(widths, forKey: MainWindowController.mainWindowWidthsStateKey) - + if AppDefaults.mainWindowWidths != widths { + AppDefaults.mainWindowWidths = widths + } } - func arrayOfIntFromCoder(_ coder: NSCoder, withKey: String) -> [Int]? { - let decodedFloats: [Int]? - do { - decodedFloats = try coder.decodeTopLevelObject(forKey: MainWindowController.mainWindowWidthsStateKey) as? [Int]? ?? nil - } - catch { - decodedFloats = nil - } - return decodedFloats - } - - func restoreSplitViewState(from coder: NSCoder) { - + func restoreSplitViewState() { // TODO: Update this for multiple windows. - guard let splitView = splitViewController?.splitView, let widths = arrayOfIntFromCoder(coder, withKey: MainWindowController.mainWindowWidthsStateKey), widths.count == 3, let window = window else { + // Also: use standard state restoration mechanism. + guard let splitView = splitViewController?.splitView, let widths = AppDefaults.mainWindowWidths, widths.count == 3, let window = window else { return } - let windowWidth = Int(floor(window.frame.width)) let dividerThickness: Int = Int(splitView.dividerThickness) let sidebarWidth: Int = widths[0] @@ -748,5 +748,49 @@ private extension MainWindowController { splitView.setPosition(CGFloat(sidebarWidth + dividerThickness + timelineWidth), ofDividerAt: 1) } +// func saveSplitViewState(to coder: NSCoder) { +// +// // TODO: Update this for multiple windows. +// +// guard let splitView = splitViewController?.splitView else { +// return +// } +// +// let widths = splitView.arrangedSubviews.map{ Int(floor($0.frame.width)) } +// coder.encode(widths, forKey: MainWindowController.mainWindowWidthsStateKey) +// +// } + +// func arrayOfIntFromCoder(_ coder: NSCoder, withKey: String) -> [Int]? { +// let decodedFloats: [Int]? +// do { +// decodedFloats = try coder.decodeTopLevelObject(forKey: MainWindowController.mainWindowWidthsStateKey) as? [Int]? ?? nil +// } +// catch { +// decodedFloats = nil +// } +// return decodedFloats +// } + +// func restoreSplitViewState(from coder: NSCoder) { +// +// // TODO: Update this for multiple windows. +// guard let splitView = splitViewController?.splitView, let widths = arrayOfIntFromCoder(coder, withKey: MainWindowController.mainWindowWidthsStateKey), widths.count == 3, let window = window else { +// return +// } +// +// let windowWidth = Int(floor(window.frame.width)) +// let dividerThickness: Int = Int(splitView.dividerThickness) +// let sidebarWidth: Int = widths[0] +// let timelineWidth: Int = widths[1] +// +// // Make sure the detail view has its mimimum thickness, at least. +// if windowWidth < sidebarWidth + dividerThickness + timelineWidth + dividerThickness + MainWindowController.detailViewMinimumThickness { +// return +// } +// +// splitView.setPosition(CGFloat(sidebarWidth), ofDividerAt: 0) +// splitView.setPosition(CGFloat(sidebarWidth + dividerThickness + timelineWidth), ofDividerAt: 1) +// } } diff --git a/NetNewsWire/MainWindow/Sidebar/SidebarViewController.swift b/NetNewsWire/MainWindow/Sidebar/SidebarViewController.swift index c312ea91a..d77c69df0 100644 --- a/NetNewsWire/MainWindow/Sidebar/SidebarViewController.swift +++ b/NetNewsWire/MainWindow/Sidebar/SidebarViewController.swift @@ -78,23 +78,23 @@ protocol SidebarDelegate: class { // MARK: State Restoration - private static let stateRestorationSelectedRowIndexes = "selectedRowIndexes" - - override func encodeRestorableState(with coder: NSCoder) { - - super.encodeRestorableState(with: coder) - - coder.encode(outlineView.selectedRowIndexes, forKey: SidebarViewController.stateRestorationSelectedRowIndexes) - } - - override func restoreState(with coder: NSCoder) { - - super.restoreState(with: coder) - - if let restoredRowIndexes = coder.decodeObject(of: [NSIndexSet.self], forKey: SidebarViewController.stateRestorationSelectedRowIndexes) as? IndexSet { - outlineView.selectRowIndexes(restoredRowIndexes, byExtendingSelection: false) - } - } +// private static let stateRestorationSelectedRowIndexes = "selectedRowIndexes" +// +// override func encodeRestorableState(with coder: NSCoder) { +// +// super.encodeRestorableState(with: coder) +// +// coder.encode(outlineView.selectedRowIndexes, forKey: SidebarViewController.stateRestorationSelectedRowIndexes) +// } +// +// override func restoreState(with coder: NSCoder) { +// +// super.restoreState(with: coder) +// +// if let restoredRowIndexes = coder.decodeObject(of: [NSIndexSet.self], forKey: SidebarViewController.stateRestorationSelectedRowIndexes) as? IndexSet { +// outlineView.selectRowIndexes(restoredRowIndexes, byExtendingSelection: false) +// } +// } // MARK: - Notifications @@ -303,7 +303,7 @@ protocol SidebarDelegate: class { func outlineViewSelectionDidChange(_ notification: Notification) { selectionDidChange(selectedObjects.isEmpty ? nil : selectedObjects) - self.invalidateRestorableState() +// self.invalidateRestorableState() } //MARK: - Node Manipulation diff --git a/NetNewsWire/MainWindow/Timeline/TimelineViewController.swift b/NetNewsWire/MainWindow/Timeline/TimelineViewController.swift index 174477134..5e1da1213 100644 --- a/NetNewsWire/MainWindow/Timeline/TimelineViewController.swift +++ b/NetNewsWire/MainWindow/Timeline/TimelineViewController.swift @@ -156,23 +156,23 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner { // MARK: State Restoration - private static let stateRestorationSelectedArticles = "selectedArticles" - - override func encodeRestorableState(with coder: NSCoder) { - - super.encodeRestorableState(with: coder) - - coder.encode(self.selectedArticleIDs(), forKey: TimelineViewController.stateRestorationSelectedArticles) - } - - override func restoreState(with coder: NSCoder) { - - super.restoreState(with: coder) - - if let restoredArticleIDs = (try? coder.decodeTopLevelObject(forKey: TimelineViewController.stateRestorationSelectedArticles)) as? [String] { - self.restoreSelection(restoredArticleIDs) - } - } +// private static let stateRestorationSelectedArticles = "selectedArticles" +// +// override func encodeRestorableState(with coder: NSCoder) { +// +// super.encodeRestorableState(with: coder) +// +// coder.encode(self.selectedArticleIDs(), forKey: TimelineViewController.stateRestorationSelectedArticles) +// } +// +// override func restoreState(with coder: NSCoder) { +// +// super.restoreState(with: coder) +// +// if let restoredArticleIDs = (try? coder.decodeTopLevelObject(forKey: TimelineViewController.stateRestorationSelectedArticles)) as? [String] { +// self.restoreSelection(restoredArticleIDs) +// } +// } // MARK: Appearance Change @@ -647,7 +647,7 @@ extension TimelineViewController: NSTableViewDelegate { selectionDidChange(selectedArticles) - self.invalidateRestorableState() +// self.invalidateRestorableState() } private func selectionDidChange(_ selectedArticles: ArticleArray?) {