diff --git a/Example/KeyboardShortcutsExample/MainScreen.swift b/Example/KeyboardShortcutsExample/MainScreen.swift index 79e4d85..2f2690a 100644 --- a/Example/KeyboardShortcutsExample/MainScreen.swift +++ b/Example/KeyboardShortcutsExample/MainScreen.swift @@ -57,6 +57,11 @@ private struct DynamicShortcut: View { Divider() DynamicShortcutRecorder(name: $shortcut.name, isPressed: $isPressed) } + Divider() + .padding(.vertical) + Button("Reset All") { + KeyboardShortcuts.resetAll() + } } .frame(maxWidth: 300) .padding() @@ -98,9 +103,6 @@ private struct DoubleShortcut: View { .offset(x: 90) } Spacer() - Button("Reset All") { - KeyboardShortcuts.reset(.testShortcut1, .testShortcut2) - } } .offset(x: -40) .frame(maxWidth: 300) diff --git a/Example/KeyboardShortcutsExample/Utilities.swift b/Example/KeyboardShortcutsExample/Utilities.swift index a94e3a8..d504fae 100644 --- a/Example/KeyboardShortcutsExample/Utilities.swift +++ b/Example/KeyboardShortcutsExample/Utilities.swift @@ -1,6 +1,5 @@ import SwiftUI - @MainActor final class CallbackMenuItem: NSMenuItem { private static var validateCallback: ((NSMenuItem) -> Bool)? @@ -50,7 +49,6 @@ extension CallbackMenuItem: NSMenuItemValidation { } } - extension NSMenuItem { convenience init( _ title: String, @@ -81,7 +79,6 @@ extension NSMenuItem { } } - extension NSMenu { @MainActor @discardableResult diff --git a/Sources/KeyboardShortcuts/KeyboardShortcuts.swift b/Sources/KeyboardShortcuts/KeyboardShortcuts.swift index 7e16c62..105c465 100644 --- a/Sources/KeyboardShortcuts/KeyboardShortcuts.swift +++ b/Sources/KeyboardShortcuts/KeyboardShortcuts.swift @@ -58,6 +58,19 @@ public enum KeyboardShortcuts { } } + static var allNames: Set { + UserDefaults.standard.dictionaryRepresentation() + .compactMap { key, _ in + guard key.hasPrefix(userDefaultsPrefix) else { + return nil + } + + let name = key.replacingPrefix(userDefaultsPrefix, with: "") + return .init(name) + } + .toSet() + } + /** Enable keyboard shortcuts to work even when an `NSMenu` is open by setting this property when the menu opens and closes. @@ -225,11 +238,8 @@ public enum KeyboardShortcuts { var body: some View { VStack { // … - Button("Reset All") { - KeyboardShortcuts.reset( - .toggleUnicornMode, - .showRainbow - ) + Button("Reset") { + KeyboardShortcuts.reset(.toggleUnicornMode) } } } @@ -255,11 +265,8 @@ public enum KeyboardShortcuts { var body: some View { VStack { // … - Button("Reset All") { - KeyboardShortcuts.reset( - .toggleUnicornMode, - .showRainbow - ) + Button("Reset") { + KeyboardShortcuts.reset(.toggleUnicornMode) } } } @@ -270,6 +277,31 @@ public enum KeyboardShortcuts { reset(names) } + /** + Reset the keyboard shortcut for all the names. + + Unlike `reset(…)`, this resets all the shortcuts to `nil`, not the `defaultValue`. + + ```swift + import SwiftUI + import KeyboardShortcuts + + struct SettingsScreen: View { + var body: some View { + VStack { + // … + Button("Reset All") { + KeyboardShortcuts.resetAll() + } + } + } + } + ``` + */ + public static func resetAll() { + reset(allNames.toArray()) + } + /** Set the keyboard shortcut for a name. diff --git a/Sources/KeyboardShortcuts/Utilities.swift b/Sources/KeyboardShortcuts/Utilities.swift index c5e7c83..b07ae54 100644 --- a/Sources/KeyboardShortcuts/Utilities.swift +++ b/Sources/KeyboardShortcuts/Utilities.swift @@ -14,7 +14,7 @@ extension String { extension Data { - var toString: String? { String(data: self, encoding: .utf8) } // swiftlint:disable:this non_optional_string_data_conversion + var toString: String? { String(data: self, encoding: .utf8) } } @@ -497,3 +497,30 @@ extension Dictionary { } } #endif + + +extension Sequence where Element: Hashable { + /** + Convert a `Sequence` with `Hashable` elements to a `Set`. + */ + func toSet() -> Set { Set(self) } +} + + +extension Set { + /** + Convert a `Set` to an `Array`. + */ + func toArray() -> [Element] { Array(self) } +} + + +extension StringProtocol { + func replacingPrefix(_ prefix: String, with replacement: String) -> String { + guard hasPrefix(prefix) else { + return String(self) + } + + return replacement + dropFirst(prefix.count) + } +}