From 2d7d903bca474f1aec3bd6ca4724d15830e31d0e Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 28 Jan 2018 16:09:18 -0800 Subject: [PATCH] Create and use a SidebarGearMenuDelegate to show a menu when the gear menu in the sidebar is clicked. --- Evergreen.xcodeproj/project.pbxproj | 4 ++ Evergreen/Base.lproj/MainWindow.storyboard | 11 +++++- ...MainWindowController+ContextualMenus.swift | 38 ++++++++++++++++-- .../Sidebar/SidebarGearMenuDelegate.swift | 39 +++++++++++++++++++ .../Sidebar/SidebarViewController.swift | 14 ++++++- 5 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 Evergreen/MainWindow/Sidebar/SidebarGearMenuDelegate.swift diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index cc6dd85df..ba0674228 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -119,6 +119,7 @@ 84CC08061FF5D2E000C0C0ED /* FeedListSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CC08051FF5D2E000C0C0ED /* FeedListSplitViewController.swift */; }; 84CC88181FE59CBF00644329 /* SmartFeedsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CC88171FE59CBF00644329 /* SmartFeedsController.swift */; }; 84D52E951FE588BB00D14F5B /* DetailStatusBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D52E941FE588BB00D14F5B /* DetailStatusBarView.swift */; }; + 84D5BA20201E8FB6009092BD /* SidebarGearMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D5BA1F201E8FB6009092BD /* SidebarGearMenuDelegate.swift */; }; 84DAEE301F86CAFE0058304B /* OPMLImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DAEE2F1F86CAFE0058304B /* OPMLImporter.swift */; }; 84DAEE321F870B390058304B /* DockBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DAEE311F870B390058304B /* DockBadge.swift */; }; 84E46C7D1F75EF7B005ECFB3 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E46C7C1F75EF7B005ECFB3 /* AppDefaults.swift */; }; @@ -577,6 +578,7 @@ 84CC08051FF5D2E000C0C0ED /* FeedListSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListSplitViewController.swift; sourceTree = ""; }; 84CC88171FE59CBF00644329 /* SmartFeedsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartFeedsController.swift; sourceTree = ""; }; 84D52E941FE588BB00D14F5B /* DetailStatusBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailStatusBarView.swift; sourceTree = ""; }; + 84D5BA1F201E8FB6009092BD /* SidebarGearMenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarGearMenuDelegate.swift; sourceTree = ""; }; 84DAEE2F1F86CAFE0058304B /* OPMLImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLImporter.swift; sourceTree = ""; }; 84DAEE311F870B390058304B /* DockBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DockBadge.swift; path = Evergreen/DockBadge.swift; sourceTree = ""; }; 84E46C7C1F75EF7B005ECFB3 /* AppDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDefaults.swift; path = Evergreen/AppDefaults.swift; sourceTree = ""; }; @@ -823,6 +825,7 @@ 849A97631ED9EB96007D329B /* UnreadCountView.swift */, 845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */, 849A97821ED9EC63007D329B /* SidebarStatusBarView.swift */, + 84D5BA1F201E8FB6009092BD /* SidebarGearMenuDelegate.swift */, 844B5B6A1FEA224000C7C76A /* Keyboard */, 845A29251FC928C7007B49E3 /* Cell */, ); @@ -1671,6 +1674,7 @@ 849A976D1ED9EBC8007D329B /* TimelineTableView.swift in Sources */, 84D52E951FE588BB00D14F5B /* DetailStatusBarView.swift in Sources */, 84B99C671FAE35E600ECDEDB /* FeedListTreeControllerDelegate.swift in Sources */, + 84D5BA20201E8FB6009092BD /* SidebarGearMenuDelegate.swift in Sources */, 84B99C691FAE36B800ECDEDB /* FeedListFolder.swift in Sources */, 84411E711FE5FBFA004B527F /* SmallIconProvider.swift in Sources */, 844B5B591FE9FE4F00C7C76A /* SidebarKeyboardDelegate.swift in Sources */, diff --git a/Evergreen/Base.lproj/MainWindow.storyboard b/Evergreen/Base.lproj/MainWindow.storyboard index 58236d0df..49801d380 100644 --- a/Evergreen/Base.lproj/MainWindow.storyboard +++ b/Evergreen/Base.lproj/MainWindow.storyboard @@ -393,6 +393,9 @@ + + + @@ -438,10 +441,16 @@ + + + + + + @@ -677,7 +686,7 @@ - + diff --git a/Evergreen/ContextualMenus/MainWindowController+ContextualMenus.swift b/Evergreen/ContextualMenus/MainWindowController+ContextualMenus.swift index e7a2d1788..58729d69e 100644 --- a/Evergreen/ContextualMenus/MainWindowController+ContextualMenus.swift +++ b/Evergreen/ContextualMenus/MainWindowController+ContextualMenus.swift @@ -9,6 +9,7 @@ import AppKit import Data import Account +import RSCore extension MainWindowController { @@ -38,10 +39,18 @@ extension MainWindowController { @objc func openHomePageFromContextualMenu(_ sender: Any?) { + guard let menuItem = sender as? NSMenuItem, let urlString = menuItem.representedObject as? String else { + return + } + Browser.open(urlString, inBackground: false) } - @objc func copyStringFromContextualMenu(_ sender: Any?) { + @objc func copyURLFromContextualMenu(_ sender: Any?) { + guard let menuItem = sender as? NSMenuItem, let urlString = menuItem.representedObject as? String else { + return + } + URLPasteboardWriter.write(urlString: urlString, to: NSPasteboard.general) } @objc func markObjectsReadFromContextualMenu(_ sender: Any?) { @@ -76,11 +85,11 @@ private extension MainWindowController { menu.addItem(NSMenuItem.separator()) } - let copyFeedURLItem = menuItem(NSLocalizedString("Copy Feed URL", comment: "Command"), #selector(copyStringFromContextualMenu(_:)), feed.url) + let copyFeedURLItem = menuItem(NSLocalizedString("Copy Feed URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), feed.url) menu.addItem(copyFeedURLItem) if let homePageURL = feed.homePageURL { - let item = menuItem(NSLocalizedString("Copy Home Page URL", comment: "Command"), #selector(copyStringFromContextualMenu(_:)), homePageURL) + let item = menuItem(NSLocalizedString("Copy Home Page URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), homePageURL) menu.addItem(item) } menu.addItem(NSMenuItem.separator()) @@ -100,11 +109,18 @@ private extension MainWindowController { menu.addItem(NSMenuItem.separator()) } + menu.addItem(renameMenuItem(folder)) + menu.addItem(deleteMenuItem([folder])) + return menu.numberOfItems > 0 ? menu : nil } func menuForMultipleObjects(_ objects: [Any]) -> NSMenu? { + guard allObjectsAreFeedsAndOrFolders(objects) else { + return nil + } + let menu = NSMenu(title: "") if anyObjectInArrayHasNonZeroUnreadCount(objects) { @@ -112,6 +128,7 @@ private extension MainWindowController { menu.addItem(NSMenuItem.separator()) } + menu.addItem(deleteMenuItem(objects)) return menu.numberOfItems > 0 ? menu : nil } @@ -143,6 +160,21 @@ private extension MainWindowController { return false } + func allObjectsAreFeedsAndOrFolders(_ objects: [Any]) -> Bool { + + for object in objects { + if !objectIsFeedOrFolder(object) { + return false + } + } + return true + } + + func objectIsFeedOrFolder(_ object: Any) -> Bool { + + return object is Feed || object is Folder + } + func menuItem(_ title: String, _ action: Selector, _ representedObject: Any) -> NSMenuItem { let item = NSMenuItem(title: title, action: action, keyEquivalent: "") diff --git a/Evergreen/MainWindow/Sidebar/SidebarGearMenuDelegate.swift b/Evergreen/MainWindow/Sidebar/SidebarGearMenuDelegate.swift new file mode 100644 index 000000000..1d33f3e37 --- /dev/null +++ b/Evergreen/MainWindow/Sidebar/SidebarGearMenuDelegate.swift @@ -0,0 +1,39 @@ +// +// SidebarGearMenuDelegate.swift +// Evergreen +// +// Created by Brent Simmons on 1/28/18. +// Copyright © 2018 Ranchero Software. All rights reserved. +// + +import AppKit + +@objc final class SidebarGearMenuDelegate: NSObject, NSMenuDelegate { + + @IBOutlet weak var sidebarViewController: SidebarViewController? + + public func menuNeedsUpdate(_ menu: NSMenu) { + + guard let sidebarViewController = sidebarViewController else { + return + } + + // Save the first item, since it’s the gear icon itself. + guard let gearMenuItem = menu.item(at: 0) else { + assertionFailure("Expected sidebar gear menu to have at least one item.") + return + } + menu.removeAllItems() + menu.addItem(gearMenuItem) + + guard let contextualMenu = sidebarViewController.contextualMenuForSelectedObjects() else { + return + } + + let items = contextualMenu.items + contextualMenu.removeAllItems() + for menuItem in items { + menu.addItem(menuItem) + } + } +} diff --git a/Evergreen/MainWindow/Sidebar/SidebarViewController.swift b/Evergreen/MainWindow/Sidebar/SidebarViewController.swift index 957b6d6d0..58721cefa 100644 --- a/Evergreen/MainWindow/Sidebar/SidebarViewController.swift +++ b/Evergreen/MainWindow/Sidebar/SidebarViewController.swift @@ -6,7 +6,7 @@ // Copyright © 2015 Ranchero Software, LLC. All rights reserved. // -import Cocoa +import AppKit import RSTree import Data import Account @@ -15,6 +15,8 @@ import RSCore @objc class SidebarViewController: NSViewController, NSOutlineViewDelegate, NSOutlineViewDataSource, UndoableCommandRunner { @IBOutlet var outlineView: SidebarOutlineView! + @IBOutlet var gearMenuDelegate: SidebarGearMenuDelegate! + let treeControllerDelegate = SidebarTreeControllerDelegate() lazy var treeController: TreeController = { TreeController(delegate: treeControllerDelegate) @@ -189,6 +191,16 @@ import RSCore window.makeFirstResponderUnlessDescendantIsFirstResponder(outlineView) } + // MARK: Contextual Menu + + func contextualMenuForSelectedObjects() -> NSMenu? { + + guard let mainWindowController = view.window?.windowController as? MainWindowController else { + return nil + } + return mainWindowController.menu(for: selectedObjects) + } + // MARK: NSOutlineViewDelegate func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {