// // KeyboardShortcuts.swift // App // // Created by James Magahern on 9/21/20. // import UIKit @objc protocol ShortcutResponder: AnyObject { @objc optional func focusURLBar(_ sender: Any?) @objc optional func focusWebView(_ sender: Any?) @objc optional func goBack(_ sender: Any?) @objc optional func goForward(_ sender: Any?) @objc optional func createTab(_ sender: Any?) @objc optional func previousTab(_ sender: Any?) @objc optional func nextTab(_ sender: Any?) @objc optional func closeTab(_ sender: Any?) @objc optional func findOnPage(_ sender: Any?) @objc optional func refresh(_ sender: Any?) @objc optional func stop(_ sender: Any?) @objc optional func showPreferences(_ sender: Any?) @objc optional func toggleDarkMode(_ sender: Any?) } public class KeyboardShortcuts { public enum Category: CaseIterable { case application case file case go case view } public static func menu(for category: Category) -> [UIMenuElement] { switch category { case .application: return [ // Preferences UIKeyCommand( modifiers: .command, input: ",", title: "Preferences", action: #selector(ShortcutResponder.showPreferences) ) ] case .file: return [ // Open Location... UIKeyCommand( modifiers: .command, input: "L", title: "Open Location", action: #selector(ShortcutResponder.focusURLBar) ), // Tabs UIMenu(options: .displayInline, children: [ // Create Tab UIKeyCommand( modifiers: .command, input: "T", title: "New Tab", action: #selector(ShortcutResponder.createTab) ), // Close tab UIKeyCommand( modifiers: [.command], input: "W", title: "Close Tab", action: #selector(ShortcutResponder.closeTab) ), ]), // Find on page (FindOnPageViewController.isEnabled() ? UIKeyCommand( modifiers: [.command], input: "F", title: "Find on Page", action: #selector(ShortcutResponder.findOnPage) ) : UIKeyCommand.null() ), UIMenu(options: .displayInline, children: [ // Refresh UIKeyCommand( modifiers: [.command], input: "R", title: "Refresh", action: #selector(ShortcutResponder.refresh) ), // Stop UIKeyCommand( modifiers: [.command], input: ".", title: "Stop", action: #selector(ShortcutResponder.stop) ) ]), ] case .go: return [ // Focus Web View UIKeyCommand( modifiers: .command, input: "e", title: "Focus Web View", action: #selector(ShortcutResponder.focusWebView) ), // Back/Forward UIMenu(options: .displayInline, children: [ // Go Back UIKeyCommand( modifiers: .command, input: "[", title: "Go Back", action: #selector(ShortcutResponder.goBack) ), // Go Forward UIKeyCommand( modifiers: .command, input: "]", title: "Go Forward", action: #selector(ShortcutResponder.goForward) ), ]), // Tab Navigation UIMenu(options: .displayInline, children: [ // Previous Tab UIKeyCommand( modifiers: [.command, .shift], input: "[", title: "Previous Tab", action: #selector(ShortcutResponder.previousTab) ), // Next Tab UIKeyCommand( modifiers: [.command, .shift], input: "]", title: "Next Tab", action: #selector(ShortcutResponder.nextTab) ), ]) ] case .view: return [ // Toggle Dark Mode UIKeyCommand( modifiers: [.command], input: "M", title: "Toggle Dark Mode", action: #selector(ShortcutResponder.toggleDarkMode) ), ] } } public static func hiddenKeyCommands() -> [UIKeyCommand] { return [ // Go Back UIKeyCommand(input: UIKeyCommand.inputLeftArrow, modifierFlags: [.command, .control], action: #selector(ShortcutResponder.goBack)), // Go Forward UIKeyCommand(input: UIKeyCommand.inputRightArrow, modifierFlags: [.command, .control], action: #selector(ShortcutResponder.goForward)), ] } public static func allKeyCommands() -> [UIKeyCommand] { var commands: [UIKeyCommand] = [] for category in Category.allCases { let menuElements = menu(for: category) for element in menuElements { if let command = element as? UIKeyCommand { commands.append(command) } else if let menu = element as? UIMenu { commands.append(contentsOf: menu.children as! [UIKeyCommand]) } } } return commands + Self.hiddenKeyCommands() } }