diff --git a/src/App/AppDelegate.swift b/src/App/AppDelegate.swift index 65a334d52..5d1d00139 100644 --- a/src/App/AppDelegate.swift +++ b/src/App/AppDelegate.swift @@ -197,6 +197,64 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Insert it into the application menu builder.replace(menu: .preferences, with: settingsMenu) + + builder.remove(menu: .format) + builder.remove(menu: .open) + builder.remove(menu: .openRecent) + builder.remove(menu: .toolbar) + builder.remove(menu: .sidebar) + builder.remove(menu: .find) + if #available(iOS 16.0, *) { + builder.remove(menu: .document) + } + + builder.insertSibling(mapMenu(), afterMenu: .view) + } + + private func mapMenu() -> UIMenu { + func command(_ title: String, + _ action: Selector, + _ input: String, + _ modifiers: UIKeyModifierFlags = .command) -> UIKeyCommand + { + return UIKeyCommand(title: title, action: action, input: input, modifierFlags: modifiers) + } + func section(_ children: [UIMenuElement]) -> UIMenu { + return UIMenu(title: "", options: .displayInline, children: children) + } + let mvc = MainViewController.self + return UIMenu( + title: NSLocalizedString("Map", comment: "Map menu in Mac menu bar"), + children: [ + section([ + command(NSLocalizedString("Zoom In", comment: "Mac menu item"), + #selector(mvc.zoomInMenuItem(_:)), "+"), + command(NSLocalizedString("Zoom Out", comment: "Mac menu item"), + #selector(mvc.zoomOutMenuItem(_:)), "-") + ]), + section([ + command(NSLocalizedString("Center on My Location", comment: "Mac menu item"), + #selector(mvc.centerOnLocationMenuItem(_:)), "l"), + command(NSLocalizedString("GPS", comment: "GPS button label"), + #selector(mvc.followLocationMenuItem(_:)), "l", [.command, .alternate]), + command(NSLocalizedString("Face North", comment: "Mac menu item"), + #selector(mvc.faceNorthMenuItem(_:)), "n", [.command, .shift]) + ]), + section([ + command(NSLocalizedString("Search", comment: "Search button label") + "…", + #selector(mvc.searchMenuItem(_:)), "f"), + command(NSLocalizedString("Add node", comment: ""), + #selector(mvc.addNodeMenuItem(_:)), "n"), + command(NSLocalizedString("Tags", comment: "Edit action") + "…", + #selector(mvc.tagsMenuItem(_:)), "t") + ]), + section([ + command(NSLocalizedString("Display options", comment: "") + "…", + #selector(mvc.displayOptionsMenuItem(_:)), "d"), + command(NSLocalizedString("Upload your changes", comment: "") + "…", + #selector(mvc.uploadMenuItem(_:)), "u") + ]) + ]) } @objc func openPreferences() { diff --git a/src/App/Localizable.xcstrings b/src/App/Localizable.xcstrings index 0f4e6c25c..c4ec6a2c9 100644 --- a/src/App/Localizable.xcstrings +++ b/src/App/Localizable.xcstrings @@ -26410,6 +26410,9 @@ } } }, + "Center on My Location" : { + "comment" : "Mac menu item" + }, "Change" : { "comment" : "Tag value will change", "localizations" : { @@ -35138,6 +35141,9 @@ } } }, + "Face North" : { + "comment" : "Mac menu item" + }, "Filter" : { "comment" : "Header for quest filter section" }, @@ -40485,6 +40491,9 @@ } } }, + "Map" : { + "comment" : "Map menu in Mac menu bar" + }, "Map Tiles" : { "comment" : "Delete cached data", "localizations" : { @@ -67718,6 +67727,12 @@ } } }, + "Zoom In" : { + "comment" : "Mac menu item" + }, + "Zoom Out" : { + "comment" : "Mac menu item" + }, "Zoom to Edit" : { "localizations" : { "ar" : { @@ -67862,4 +67877,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/src/App/MainViewController.swift b/src/App/MainViewController.swift index a0e3abe98..b0f36d0e1 100644 --- a/src/App/MainViewController.swift +++ b/src/App/MainViewController.swift @@ -428,6 +428,9 @@ final class MainViewController: UIViewController, DPadDelegate, override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) +#if targetEnvironment(macCatalyst) + reclaimMenuFocus() +#endif if !isInitialized { isInitialized = true @@ -981,11 +984,105 @@ final class MainViewController: UIViewController, DPadDelegate, return mapView.selectedPrimary != nil && mapView.selections.relation == nil case #selector(showHelp(_:)): return true +#if targetEnvironment(macCatalyst) + case #selector(zoomInMenuItem(_:)), + #selector(zoomOutMenuItem(_:)), + #selector(followLocationMenuItem(_:)), + #selector(faceNorthMenuItem(_:)), + #selector(searchMenuItem(_:)), + #selector(displayOptionsMenuItem(_:)): + return presentedViewController == nil + case #selector(centerOnLocationMenuItem(_:)): + return presentedViewController == nil && LocationProvider.shared.currentLocation != nil + case #selector(addNodeMenuItem(_:)): + return presentedViewController == nil && !addNodeButton.isHidden + case #selector(tagsMenuItem(_:)): + return presentedViewController == nil && mapView.pushPin != nil + case #selector(uploadMenuItem(_:)): + return presentedViewController == nil && uploadButton.isEnabled +#endif default: return false } } +#if targetEnvironment(macCatalyst) + override var canBecomeFirstResponder: Bool { + return true + } + + func reclaimMenuFocus() { + guard presentedViewController == nil, + let window = view.window + else { + return + } + if !window.isKeyWindow { + window.makeKey() + } + if !isFirstResponder { + becomeFirstResponder() + } + } + + override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { + super.dismiss(animated: flag, completion: { [weak self] in + completion?() + self?.reclaimMenuFocus() + }) + } + + override func validate(_ command: UICommand) { + super.validate(command) + if command.action == #selector(followLocationMenuItem(_:)) { + command.state = gpsState == .NONE ? .off : .on + } + } + + @objc func zoomInMenuItem(_ sender: Any?) { + viewPort.adjustZoom(by: 2.0, aroundScreenPoint: mapView.bounds.center()) + } + + @objc func zoomOutMenuItem(_ sender: Any?) { + viewPort.adjustZoom(by: 0.5, aroundScreenPoint: mapView.bounds.center()) + } + + @objc func centerOnLocationMenuItem(_ sender: Any?) { + centerOnGPS(sender as Any) + } + + @objc func followLocationMenuItem(_ sender: Any?) { + toggleLocationButton() + } + + @objc func faceNorthMenuItem(_ sender: Any?) { + if gpsState == .HEADING { + gpsState = .LOCATION + } + viewPort.rotateToHeading(0.0) + } + + @objc func searchMenuItem(_ sender: Any?) { + searchButton.sendActions(for: .touchUpInside) + } + + @objc func addNodeMenuItem(_ sender: Any?) { + mapView.addNode(at: mapView.bounds.center()) + } + + @objc func tagsMenuItem(_ sender: Any?) { + mapView.presentTagEditor(sender) + } + + @objc func displayOptionsMenuItem(_ sender: Any?) { + displayButton.sendActions(for: .touchUpInside) + } + + @objc func uploadMenuItem(_ sender: Any?) { + uploadButton.sendActions(for: .touchUpInside) + } +#endif + @objc func undo(_ sender: Any?) { mapView.undo(sender) } @@ -1164,6 +1261,9 @@ final class MainViewController: UIViewController, DPadDelegate, } @objc func handleTapGesture(_ tap: UITapGestureRecognizer) { +#if targetEnvironment(macCatalyst) + reclaimMenuFocus() +#endif mapView.handleTapGesture(tap) }