import AppKit /** ProdDash Viewer: one window showing a ProdDash dashboard, a tab per installation when there's more than one, and Settings to say which. The menus are deliberately short — Back, Forward, Reload Page and Settings, plus what every Mac app has: the app menu, Edit (without which Copy and Paste do nothing in the dashboard's text fields), Window and Help. */ final class AppDelegate: NSObject, NSApplicationDelegate { static var version: String { Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "dev" } private var viewer: ViewerWindowController! private lazy var settingsWindow = SettingsWindowController() private lazy var helpWindow = HelpWindowController() func applicationWillFinishLaunching(_ notification: Notification) { NSWindow.allowsAutomaticWindowTabbing = false NSApp.mainMenu = makeMainMenu() } func applicationDidFinishLaunching(_ notification: Notification) { viewer = ViewerWindowController() viewer.onAddConnection = { [weak self] in self?.settingsWindow.show(addingConnection: true) } viewer.showWindow(nil) if ViewerSettings.shared.reopenInFullScreen { DispatchQueue.main.async { self.viewer.window?.toggleFullScreen(nil) } } } func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { if !flag { viewer.showWindow(nil) } return true } func applicationWillTerminate(_ notification: Notification) { viewer?.rememberFullScreen() } func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { true } @objc func showSettings(_ sender: Any?) { settingsWindow.show() } @objc func showViewerHelp(_ sender: Any?) { helpWindow.show() } // MARK: - Menus private func makeMainMenu() -> NSMenu { let name = "ProdDash Viewer" let main = NSMenu() let app = submenu(of: main, title: name) app.addItem(withTitle: "About \(name)", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: "") app.addItem(.separator()) app.addItem(withTitle: "Settings…", action: #selector(showSettings(_:)), keyEquivalent: ",") app.addItem(.separator()) let services = submenu(of: app, title: "Services") NSApp.servicesMenu = services app.addItem(.separator()) app.addItem(withTitle: "Hide \(name)", action: #selector(NSApplication.hide(_:)), keyEquivalent: "h") app.addItem(withTitle: "Hide Others", action: #selector(NSApplication.hideOtherApplications(_:)), keyEquivalent: "h").keyEquivalentModifierMask = [.command, .option] app.addItem(withTitle: "Show All", action: #selector(NSApplication.unhideAllApplications(_:)), keyEquivalent: "") app.addItem(.separator()) app.addItem(withTitle: "Quit \(name)", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q") let edit = submenu(of: main, title: "Edit") edit.addItem(withTitle: "Undo", action: Selector(("undo:")), keyEquivalent: "z") edit.addItem(withTitle: "Redo", action: Selector(("redo:")), keyEquivalent: "Z") edit.addItem(.separator()) edit.addItem(withTitle: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x") edit.addItem(withTitle: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c") edit.addItem(withTitle: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v") edit.addItem(withTitle: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a") let view = submenu(of: main, title: "View") view.addItem(withTitle: "Back", action: #selector(ViewerWindowController.goBackInPage(_:)), keyEquivalent: "[") view.addItem(withTitle: "Forward", action: #selector(ViewerWindowController.goForwardInPage(_:)), keyEquivalent: "]") view.addItem(withTitle: "Reload Page", action: #selector(ViewerWindowController.reloadPage(_:)), keyEquivalent: "r") view.addItem(.separator()) // AppKit retitles this "Exit Full Screen" while the window is in full screen. view.addItem(withTitle: "Enter Full Screen", action: #selector(NSWindow.toggleFullScreen(_:)), keyEquivalent: "f").keyEquivalentModifierMask = [.command, .control] let window = submenu(of: main, title: "Window") window.addItem(withTitle: "Close", action: #selector(NSWindow.performClose(_:)), keyEquivalent: "w") window.addItem(withTitle: "Minimize", action: #selector(NSWindow.performMiniaturize(_:)), keyEquivalent: "m") window.addItem(withTitle: "Zoom", action: #selector(NSWindow.performZoom(_:)), keyEquivalent: "") window.addItem(.separator()) window.addItem(withTitle: "Bring All to Front", action: #selector(NSApplication.arrangeInFront(_:)), keyEquivalent: "") NSApp.windowsMenu = window let help = submenu(of: main, title: "Help") help.addItem(withTitle: "\(name) Help", action: #selector(showViewerHelp(_:)), keyEquivalent: "?") NSApp.helpMenu = help return main } @discardableResult private func submenu(of parent: NSMenu, title: String) -> NSMenu { let menu = NSMenu(title: title) parent.addItem(withTitle: title, action: nil, keyEquivalent: "").submenu = menu return menu } }