aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoopesh Chander <roop@roopc.net>2019-05-06 19:08:57 +0530
committerRoopesh Chander <roop@roopc.net>2019-05-20 16:42:27 +0530
commit6e1f03e41cb20414113e01ee6c8810acbeba0a5d (patch)
treeb96d543a059d228f8e9ee7aed434b21a44c185dd
parentmacOS: Remove custom key event handling (diff)
downloadwireguard-apple-6e1f03e41cb20414113e01ee6c8810acbeba0a5d.tar.xz
wireguard-apple-6e1f03e41cb20414113e01ee6c8810acbeba0a5d.zip
macOS: Set a main menu for the app
The main menu would be shown only when the manage tunnels window is visible. Signed-off-by: Roopesh Chander <roop@roopc.net>
-rw-r--r--WireGuard/WireGuard.xcodeproj/project.pbxproj4
-rw-r--r--WireGuard/WireGuard/Base.lproj/Localizable.strings27
-rw-r--r--WireGuard/WireGuard/UI/macOS/AppDelegate.swift19
-rw-r--r--WireGuard/WireGuard/UI/macOS/MainMenu.swift129
-rw-r--r--WireGuard/WireGuard/UI/macOS/StatusMenu.swift20
5 files changed, 180 insertions, 19 deletions
diff --git a/WireGuard/WireGuard.xcodeproj/project.pbxproj b/WireGuard/WireGuard.xcodeproj/project.pbxproj
index 70ff8b1..5118ea2 100644
--- a/WireGuard/WireGuard.xcodeproj/project.pbxproj
+++ b/WireGuard/WireGuard.xcodeproj/project.pbxproj
@@ -53,6 +53,7 @@
6F1075642258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */; };
6F19D30422402B8700A126F2 /* ConfirmationAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */; };
6F2449E8226587B90047B9E9 /* MacAppStoreUpdateDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */; };
+ 6F3E02E9228000F6001FE7E3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3E02E8228000F6001FE7E3 /* MainMenu.swift */; };
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */; };
6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */; };
6F4DD16E21DBEA0700690EAE /* ManageTunnelsRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */; };
@@ -300,6 +301,7 @@
6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteTunnelsConfirmationAlert.swift; sourceTree = "<group>"; };
6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertPresenter.swift; sourceTree = "<group>"; };
6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppStoreUpdateDetector.swift; sourceTree = "<group>"; };
+ 6F3E02E8228000F6001FE7E3 /* MainMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reuse.swift"; sourceTree = "<group>"; };
6F4DD16A21DA558800690EAE /* TunnelListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelListRow.swift; sourceTree = "<group>"; };
6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageTunnelsRootViewController.swift; sourceTree = "<group>"; };
@@ -631,6 +633,7 @@
6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */,
6F89E17921EDEB0E00C97BB9 /* StatusItemController.swift */,
6FBA101621D655340051C35F /* StatusMenu.swift */,
+ 6F3E02E8228000F6001FE7E3 /* MainMenu.swift */,
6F89E17B21F090CC00C97BB9 /* TunnelsTracker.swift */,
6FBA104121D6BC210051C35F /* ErrorPresenter.swift */,
6FCD99AE21E0EA1700BA4C82 /* ImportPanelPresenter.swift */,
@@ -1289,6 +1292,7 @@
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */,
6FDB6D15224CB2CE00EE4BC3 /* LogViewCell.swift in Sources */,
6FE3661D21F64F6B00F78C7D /* ConfTextColorTheme.swift in Sources */,
+ 6F3E02E9228000F6001FE7E3 /* MainMenu.swift in Sources */,
5F52D0BF21E3788900283CEA /* NSColor+Hex.swift in Sources */,
6FB1BDBE21D50F0200A991BF /* Logger.swift in Sources */,
6FB1BDBF21D50F0200A991BF /* TunnelConfiguration+WgQuickConfig.swift in Sources */,
diff --git a/WireGuard/WireGuard/Base.lproj/Localizable.strings b/WireGuard/WireGuard/Base.lproj/Localizable.strings
index 9cfaef1..1d05a45 100644
--- a/WireGuard/WireGuard/Base.lproj/Localizable.strings
+++ b/WireGuard/WireGuard/Base.lproj/Localizable.strings
@@ -291,7 +291,7 @@
"alertSystemErrorMessageTunnelConfigurationReadWriteFailed" = "Reading or writing the configuration failed.";
"alertSystemErrorMessageTunnelConfigurationUnknown" = "Unknown system error.";
-// Mac status bar menu / pulldown menu
+// Mac status bar menu / pulldown menu / main menu
"macMenuNetworks (%@)" = "Networks: %@";
"macMenuNetworksNone" = "Networks: None";
@@ -305,6 +305,31 @@
"macMenuAbout" = "About WireGuard";
"macMenuQuit" = "Quit";
+"macMenuHideApp" = "Hide WireGuard";
+"macMenuHideOtherApps" = "Hide Others";
+"macMenuShowAllApps" = "Show All";
+
+"macMenuFile" = "File";
+"macMenuCloseWindow" = "Close Window";
+
+"macMenuEdit" = "Edit";
+"macMenuUndo" = "Undo";
+"macMenuRedo" = "Redo";
+"macMenuCut" = "Cut";
+"macMenuCopy" = "Copy";
+"macMenuPaste" = "Paste";
+"macMenuSelectAll" = "Select All";
+
+"macMenuTunnel" = "Tunnel";
+"macMenuToggleStatus" = "Toggle Status";
+"macMenuEditTunnel" = "Edit…";
+"macMenuDeleteSelected" = "Delete Selected";
+
+"macMenuWindow" = "Window";
+"macMenuMinimize" = "Minimize";
+"macMenuZoom" = "Zoom";
+"macMenuFullScreen" = "Full Screen";
+
// Mac manage tunnels window
"macWindowTitleManageTunnels" = "Manage WireGuard Tunnels";
diff --git a/WireGuard/WireGuard/UI/macOS/AppDelegate.swift b/WireGuard/WireGuard/UI/macOS/AppDelegate.swift
index 3f69ea3..8dc5825 100644
--- a/WireGuard/WireGuard/UI/macOS/AppDelegate.swift
+++ b/WireGuard/WireGuard/UI/macOS/AppDelegate.swift
@@ -17,6 +17,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
Logger.configureGlobal(tagged: "APP", withFilePath: FileManager.logFileURL?.path)
registerLoginItem(shouldLaunchAtLogin: true)
+ NSApp.mainMenu = MainMenu()
TunnelsManager.create { [weak self] result in
guard let self = self else { return }
@@ -104,6 +105,24 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
}
+extension AppDelegate {
+ @objc func aboutClicked() {
+ var appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
+ if let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
+ appVersion += " (\(appBuild))"
+ }
+ let appVersionString = [
+ tr(format: "macAppVersion (%@)", appVersion),
+ tr(format: "macGoBackendVersion (%@)", WIREGUARD_GO_VERSION)
+ ].joined(separator: "\n")
+ NSApp.activate(ignoringOtherApps: true)
+ NSApp.orderFrontStandardAboutPanel(options: [
+ .applicationVersion: appVersionString,
+ .version: ""
+ ])
+ }
+}
+
extension AppDelegate: StatusMenuWindowDelegate {
func manageTunnelsWindow() -> NSWindow {
if manageTunnelsWindowObject == nil {
diff --git a/WireGuard/WireGuard/UI/macOS/MainMenu.swift b/WireGuard/WireGuard/UI/macOS/MainMenu.swift
new file mode 100644
index 0000000..72bba71
--- /dev/null
+++ b/WireGuard/WireGuard/UI/macOS/MainMenu.swift
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Cocoa
+
+class MainMenu: NSMenu {
+ init() {
+ super.init(title: "")
+ addSubmenu(createApplicationMenu())
+ addSubmenu(createFileMenu())
+ addSubmenu(createEditMenu())
+ addSubmenu(createTunnelMenu())
+ addSubmenu(createWindowMenu())
+ }
+
+ required init(coder decoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ private func addSubmenu(_ menu: NSMenu) {
+ let menuItem = self.addItem(withTitle: "", action: nil, keyEquivalent: "")
+ self.setSubmenu(menu, for: menuItem)
+ }
+
+ private func createApplicationMenu() -> NSMenu {
+ let menu = NSMenu()
+
+ let aboutMenuItem = menu.addItem(withTitle: tr("macMenuAbout"),
+ action: #selector(AppDelegate.aboutClicked), keyEquivalent: "")
+ aboutMenuItem.target = NSApp.delegate
+
+ menu.addItem(NSMenuItem.separator())
+
+ menu.addItem(withTitle: tr("macMenuViewLog"),
+ action: #selector(TunnelsListTableViewController.handleViewLogAction), keyEquivalent: "")
+
+ menu.addItem(NSMenuItem.separator())
+
+ let hideMenuItem = menu.addItem(withTitle: tr("macMenuHideApp"),
+ action: #selector(NSApplication.hide), keyEquivalent: "h")
+ hideMenuItem.target = NSApp
+ let hideOthersMenuItem = menu.addItem(withTitle: tr("macMenuHideOtherApps"),
+ action: #selector(NSApplication.hideOtherApplications), keyEquivalent: "h")
+ hideOthersMenuItem.keyEquivalentModifierMask = [.command, .option]
+ hideOthersMenuItem.target = NSApp
+ let showAllMenuItem = menu.addItem(withTitle: tr("macMenuShowAllApps"),
+ action: #selector(NSApplication.unhideAllApplications), keyEquivalent: "")
+ showAllMenuItem.target = NSApp
+
+ menu.addItem(NSMenuItem.separator())
+
+ let quitMenuItem = menu.addItem(withTitle: tr("macMenuQuit"),
+ action: #selector(AppDelegate.quit), keyEquivalent: "q")
+ quitMenuItem.target = NSApp.delegate
+
+ return menu
+ }
+
+ private func createFileMenu() -> NSMenu {
+ let menu = NSMenu(title: tr("macMenuFile"))
+
+ menu.addItem(withTitle: tr("macMenuAddEmptyTunnel"),
+ action: #selector(TunnelsListTableViewController.handleAddEmptyTunnelAction), keyEquivalent: "n")
+ menu.addItem(withTitle: tr("macMenuImportTunnels"),
+ action: #selector(TunnelsListTableViewController.handleImportTunnelAction), keyEquivalent: "o")
+
+ menu.addItem(NSMenuItem.separator())
+
+ menu.addItem(withTitle: tr("macMenuExportTunnels"),
+ action: #selector(TunnelsListTableViewController.handleExportTunnelsAction), keyEquivalent: "")
+
+ menu.addItem(NSMenuItem.separator())
+
+ menu.addItem(withTitle: tr("macMenuCloseWindow"), action: #selector(NSWindow.performClose(_:)), keyEquivalent:"w")
+
+ return menu
+ }
+
+ private func createEditMenu() -> NSMenu {
+ let menu = NSMenu(title: tr("macMenuEdit"))
+
+ menu.addItem(withTitle: tr("macMenuUndo"), action: #selector(UndoActionRespondable.undo(_:)), keyEquivalent:"z")
+ menu.addItem(withTitle: tr("macMenuRedo"), action: #selector(UndoActionRespondable.redo(_:)), keyEquivalent:"Z")
+
+ menu.addItem(NSMenuItem.separator())
+
+ menu.addItem(withTitle: tr("macMenuCut"), action: #selector(NSText.cut(_:)), keyEquivalent:"x")
+ menu.addItem(withTitle: tr("macMenuCopy"), action: #selector(NSText.copy(_:)), keyEquivalent:"c")
+ menu.addItem(withTitle: tr("macMenuPaste"), action: #selector(NSText.paste(_:)), keyEquivalent:"v")
+
+ menu.addItem(NSMenuItem.separator())
+
+ menu.addItem(withTitle: tr("macMenuSelectAll"), action: #selector(NSText.selectAll(_:)), keyEquivalent:"a")
+
+ return menu
+ }
+
+ private func createTunnelMenu() -> NSMenu {
+ let menu = NSMenu(title: tr("macMenuTunnel"))
+
+ menu.addItem(withTitle: tr("macMenuToggleStatus"), action: #selector(TunnelDetailTableViewController.handleToggleActiveStatusAction), keyEquivalent:"t")
+
+ menu.addItem(NSMenuItem.separator())
+
+ menu.addItem(withTitle: tr("macMenuEditTunnel"), action: #selector(TunnelDetailTableViewController.handleEditTunnelAction), keyEquivalent:"e")
+ menu.addItem(withTitle: tr("macMenuDeleteSelected"), action: #selector(TunnelsListTableViewController.handleRemoveTunnelAction), keyEquivalent: "")
+
+ return menu
+ }
+
+ private func createWindowMenu() -> NSMenu {
+ let menu = NSMenu(title: tr("macMenuWindow"))
+
+ menu.addItem(withTitle: tr("macMenuMinimize"), action: #selector(NSWindow.performMiniaturize(_:)), keyEquivalent:"m")
+ menu.addItem(withTitle: tr("macMenuZoom"), action: #selector(NSWindow.performZoom(_:)), keyEquivalent:"")
+
+ menu.addItem(NSMenuItem.separator())
+
+ let fullScreenMenuItem = menu.addItem(withTitle: tr("macMenuFullScreen"), action: #selector(NSWindow.toggleFullScreen(_:)), keyEquivalent:"f")
+ fullScreenMenuItem.keyEquivalentModifierMask = [.command, .control]
+
+ return menu
+ }
+}
+
+@objc protocol UndoActionRespondable {
+ func undo(_ sender: AnyObject)
+ func redo(_ sender: AnyObject)
+}
diff --git a/WireGuard/WireGuard/UI/macOS/StatusMenu.swift b/WireGuard/WireGuard/UI/macOS/StatusMenu.swift
index 150c55e..4044d4e 100644
--- a/WireGuard/WireGuard/UI/macOS/StatusMenu.swift
+++ b/WireGuard/WireGuard/UI/macOS/StatusMenu.swift
@@ -127,8 +127,8 @@ class StatusMenu: NSMenu {
}
func addApplicationItems() {
- let aboutItem = NSMenuItem(title: tr("macMenuAbout"), action: #selector(aboutClicked), keyEquivalent: "")
- aboutItem.target = self
+ let aboutItem = NSMenuItem(title: tr("macMenuAbout"), action: #selector(AppDelegate.aboutClicked), keyEquivalent: "")
+ aboutItem.target = NSApp.delegate
addItem(aboutItem)
let quitItem = NSMenuItem(title: tr("macMenuQuit"), action: #selector(AppDelegate.quit), keyEquivalent: "")
quitItem.target = NSApp.delegate
@@ -164,22 +164,6 @@ class StatusMenu: NSMenu {
manageTunnelsWindow.makeKeyAndOrderFront(self)
ImportPanelPresenter.presentImportPanel(tunnelsManager: tunnelsManager, sourceVC: manageTunnelsWindow.contentViewController)
}
-
- @objc func aboutClicked() {
- var appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
- if let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
- appVersion += " (\(appBuild))"
- }
- let appVersionString = [
- tr(format: "macAppVersion (%@)", appVersion),
- tr(format: "macGoBackendVersion (%@)", WIREGUARD_GO_VERSION)
- ].joined(separator: "\n")
- NSApp.activate(ignoringOtherApps: true)
- NSApp.orderFrontStandardAboutPanel(options: [
- .applicationVersion: appVersionString,
- .version: ""
- ])
- }
}
extension StatusMenu {