From affae1cd7154c8f61a38f2a453e7707fa6588379 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 7 May 2020 22:49:06 +0800 Subject: [PATCH] Init --- .editorconfig | 12 + .gitattributes | 1 + .github/funding.yml | 3 + .github/workflows/jazzy.yml | 14 + .github/workflows/swiftlint.yml | 14 + .gitignore | 4 + .jazzy.yml | 12 + .swiftlint.yml | 187 +++++ .../contents.xcworkspacedata | 7 + KeyboardShortcuts.podspec | 14 + .../KeyboardShortcuts_Info.plist | 26 + KeyboardShortcuts.xcodeproj/project.pbxproj | 692 ++++++++++++++++ .../KeyboardShortcuts-Package.xcscheme | 58 ++ .../KeyboardShortcutsExample.xcscheme | 78 ++ KeyboardShortcutsExample/AppDelegate.swift | 29 + .../AppIcon.appiconset/Contents.json | 58 ++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/Main.storyboard | 337 ++++++++ KeyboardShortcutsExample/ContentView.swift | 52 ++ KeyboardShortcutsExample/Info.plist | 32 + .../KeyboardShortcutsExample.entitlements | 10 + Package.swift | 22 + .../CarbonKeyboardShortcuts.swift | 181 +++++ Sources/KeyboardShortcuts/Key.swift | 743 ++++++++++++++++++ .../KeyboardShortcuts/KeyboardShortcuts.swift | 201 +++++ Sources/KeyboardShortcuts/Name.swift | 33 + Sources/KeyboardShortcuts/Recorder.swift | 51 ++ Sources/KeyboardShortcuts/RecorderCocoa.swift | 168 ++++ Sources/KeyboardShortcuts/Shortcut.swift | 190 +++++ Sources/KeyboardShortcuts/util.swift | 276 +++++++ license | 9 + logo.png | Bin 0 -> 25562 bytes readme.md | 156 ++++ screenshot.png | Bin 0 -> 80672 bytes 34 files changed, 3676 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/funding.yml create mode 100644 .github/workflows/jazzy.yml create mode 100644 .github/workflows/swiftlint.yml create mode 100644 .gitignore create mode 100644 .jazzy.yml create mode 100644 .swiftlint.yml create mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata create mode 100644 KeyboardShortcuts.podspec create mode 100644 KeyboardShortcuts.xcodeproj/KeyboardShortcuts_Info.plist create mode 100644 KeyboardShortcuts.xcodeproj/project.pbxproj create mode 100644 KeyboardShortcuts.xcodeproj/xcshareddata/xcschemes/KeyboardShortcuts-Package.xcscheme create mode 100644 KeyboardShortcuts.xcodeproj/xcshareddata/xcschemes/KeyboardShortcutsExample.xcscheme create mode 100644 KeyboardShortcutsExample/AppDelegate.swift create mode 100644 KeyboardShortcutsExample/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 KeyboardShortcutsExample/Assets.xcassets/Contents.json create mode 100644 KeyboardShortcutsExample/Base.lproj/Main.storyboard create mode 100644 KeyboardShortcutsExample/ContentView.swift create mode 100644 KeyboardShortcutsExample/Info.plist create mode 100644 KeyboardShortcutsExample/KeyboardShortcutsExample.entitlements create mode 100644 Package.swift create mode 100644 Sources/KeyboardShortcuts/CarbonKeyboardShortcuts.swift create mode 100644 Sources/KeyboardShortcuts/Key.swift create mode 100644 Sources/KeyboardShortcuts/KeyboardShortcuts.swift create mode 100644 Sources/KeyboardShortcuts/Name.swift create mode 100644 Sources/KeyboardShortcuts/Recorder.swift create mode 100644 Sources/KeyboardShortcuts/RecorderCocoa.swift create mode 100644 Sources/KeyboardShortcuts/Shortcut.swift create mode 100644 Sources/KeyboardShortcuts/util.swift create mode 100644 license create mode 100644 logo.png create mode 100644 readme.md create mode 100644 screenshot.png diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1c6314a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = tab +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/funding.yml b/.github/funding.yml new file mode 100644 index 0000000..1a630e9 --- /dev/null +++ b/.github/funding.yml @@ -0,0 +1,3 @@ +github: sindresorhus +open_collective: sindresorhus +custom: https://sindresorhus.com/donate diff --git a/.github/workflows/jazzy.yml b/.github/workflows/jazzy.yml new file mode 100644 index 0000000..439e4e7 --- /dev/null +++ b/.github/workflows/jazzy.yml @@ -0,0 +1,14 @@ +name: Public docs +on: + release: + types: [published] +jobs: + deploy: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: Publish docs + uses: steven0351/publish-jazzy-docs@v1 + with: + personal_access_token: ${{ secrets.ACCESS_TOKEN }} + config: .jazzy.yml diff --git a/.github/workflows/swiftlint.yml b/.github/workflows/swiftlint.yml new file mode 100644 index 0000000..abc192e --- /dev/null +++ b/.github/workflows/swiftlint.yml @@ -0,0 +1,14 @@ +name: SwiftLint +on: + pull_request: + paths: + - '.github/workflows/swiftlint.yml' + - '.swiftlint.yml' + - '**/*.swift' +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: SwiftLint + uses: norio-nomura/action-swiftlint@3 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0854237 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.build +/Packages +xcuserdata +project.xcworkspace diff --git a/.jazzy.yml b/.jazzy.yml new file mode 100644 index 0000000..4a6f464 --- /dev/null +++ b/.jazzy.yml @@ -0,0 +1,12 @@ +module: KeyboardShortcuts +github_url: https://github.com/sindresorhus/KeyboardShortcuts +root_url: https://sindresorhus.com/KeyboardShortcuts +copyright: MIT License © [Sindre Sorhus](https://sindresorhus.com) +swift_build_tool: spm +build_tool_arguments: + - '-Xswiftc' + - '-swift-version' + - '-Xswiftc' + - '5' +theme: fullwidth +hide_documentation_coverage: true diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..1003465 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,187 @@ +whitelist_rules: + - anyobject_protocol + - array_init + - attributes + - block_based_kvo + - class_delegate_protocol + - closing_brace + - closure_end_indentation + - closure_parameter_position + - closure_spacing + - collection_alignment + - colon + - comma + - compiler_protocol_init + - conditional_returns_on_newline + - contains_over_filter_count + - contains_over_filter_is_empty + - contains_over_first_not_nil + - contains_over_range_nil_comparison + - control_statement + - custom_rules + - deployment_target + - discarded_notification_center_observer + - discouraged_direct_init + - discouraged_object_literal + - discouraged_optional_boolean + - discouraged_optional_collection + - duplicate_enum_cases + - duplicate_imports + - dynamic_inline + - empty_collection_literal + - empty_count + - empty_enum_arguments + - empty_parameters + - empty_parentheses_with_trailing_closure + - empty_string + - empty_xctest_method + - enum_case_associated_value_count + - explicit_init + - fallthrough + - fatal_error_message + - first_where + - flatmap_over_map_reduce + - for_where + - generic_type_name + - identical_operands + - identifier_name + - implicit_getter + - implicit_return + - inert_defer + - is_disjoint + - joined_default_parameter + - last_where + - leading_whitespace + - legacy_cggeometry_functions + - legacy_constant + - legacy_constructor + - legacy_hashing + - legacy_multiple + - legacy_nsgeometry_functions + - legacy_random + - literal_expression_end_indentation + - lower_acl_than_parent + - mark + - modifier_order + - multiline_arguments + - multiline_function_chains + - multiline_literal_brackets + - multiline_parameters + - multiline_parameters_brackets + - nimble_operator + - no_extension_access_modifier + - no_fallthrough_only + - no_space_in_method_call + - notification_center_detachment + - nsobject_prefer_isequal + - number_separator + - object_literal + - opening_brace + - operator_usage_whitespace + - operator_whitespace + - orphaned_doc_comment + - overridden_super_call + - pattern_matching_keywords + - prefer_self_type_over_type_of_self + - private_action + - private_outlet + - private_unit_test + - prohibited_nan_comparison + - prohibited_super_call + - protocol_property_accessors_order + - reduce_boolean + - reduce_into + - redundant_discardable_let + - redundant_nil_coalescing + - redundant_objc_attribute + - redundant_optional_initialization + - redundant_set_access_control + - redundant_string_enum_value + - redundant_type_annotation + - redundant_void_return + - required_enum_case + - return_value_from_void_function + - return_arrow_whitespace + - shorthand_operator + - sorted_first_last + - statement_position + - static_operator + - strong_iboutlet + - superfluous_disable_command + - switch_case_alignment + - switch_case_on_newline + - syntactic_sugar + - toggle_bool + - trailing_closure + - trailing_comma + - trailing_newline + - trailing_semicolon + - trailing_whitespace + - tuple_pattern + - unavailable_function + - unneeded_break_in_switch + - unneeded_parentheses_in_closure_argument + - unowned_variable_capture + - untyped_error_in_catch + - unused_capture_list + - unused_closure_parameter + - unused_control_flow_label + - unused_enumerated + - unused_optional_binding + - unused_setter_value + - valid_ibinspectable + - vertical_parameter_alignment + - vertical_parameter_alignment_on_call + - vertical_whitespace_closing_braces + - vertical_whitespace_opening_braces + - void_return + - weak_delegate + - xct_specific_matcher + - xctfail_message + - yoda_condition +analyzer_rules: + - unused_declaration + - unused_import +force_cast: warning +force_try: warning +force_unwrapping: warning +number_separator: + minimum_length: 5 +object_literal: + image_literal: false +discouraged_object_literal: + color_literal: false +identifier_name: + max_length: + warning: 100 + error: 100 + min_length: + warning: 2 + error: 2 + validates_start_with_lowercase: false + allowed_symbols: + - '_' + excluded: + - 'x' + - 'y' + - 'a' + - 'b' + - 'x1' + - 'x2' + - 'y1' + - 'y2' +deployment_target: + macOS_deployment_target: '10.11' +custom_rules: + no_nsrect: + regex: '\bNSRect\b' + match_kinds: typeidentifier + message: 'Use CGRect instead of NSRect' + no_nssize: + regex: '\bNSSize\b' + match_kinds: typeidentifier + message: 'Use CGSize instead of NSSize' + no_nspoint: + regex: '\bNSPoint\b' + match_kinds: typeidentifier + message: 'Use CGPoint instead of NSPoint' diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/KeyboardShortcuts.podspec b/KeyboardShortcuts.podspec new file mode 100644 index 0000000..12c646d --- /dev/null +++ b/KeyboardShortcuts.podspec @@ -0,0 +1,14 @@ +Pod::Spec.new do |s| + s.name = 'KeyboardShortcuts' + s.version = '0.0.0' + s.summary = 'Add user-customizable global keyboard shortcuts to your macOS app in minutes' + s.license = 'MIT' + s.homepage = 'https://github.com/sindresorhus/KeyboardShortcuts' + s.social_media_url = 'https://twitter.com/sindresorhus' + s.authors = { 'Sindre Sorhus' => 'sindresorhus@gmail.com' } + s.source = { :git => 'https://github.com/sindresorhus/KeyboardShortcuts.git', :tag => "v#{s.version}" } + s.source_files = 'Sources/**/*.swift' + s.swift_version = '5.2' + s.platform = :macos, '10.11' + s.weak_framework = 'Combine' +end diff --git a/KeyboardShortcuts.xcodeproj/KeyboardShortcuts_Info.plist b/KeyboardShortcuts.xcodeproj/KeyboardShortcuts_Info.plist new file mode 100644 index 0000000..ca23c84 --- /dev/null +++ b/KeyboardShortcuts.xcodeproj/KeyboardShortcuts_Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/KeyboardShortcuts.xcodeproj/project.pbxproj b/KeyboardShortcuts.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a829cb9 --- /dev/null +++ b/KeyboardShortcuts.xcodeproj/project.pbxproj @@ -0,0 +1,692 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 53; + objects = { + +/* Begin PBXBuildFile section */ + E38103FE246449180023E9A8 /* Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38103FD246449180023E9A8 /* Name.swift */; }; + E3BF5627245C23840024D9BF /* Recorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3BF5626245C23840024D9BF /* Recorder.swift */; }; + E3BF5629245C24450024D9BF /* RecorderCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3BF5628245C24450024D9BF /* RecorderCocoa.swift */; }; + E3BF562B245C28BD0024D9BF /* Shortcut.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3BF562A245C28BD0024D9BF /* Shortcut.swift */; }; + E3BF562D245C29C30024D9BF /* CarbonKeyboardShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3BF562C245C29C30024D9BF /* CarbonKeyboardShortcuts.swift */; }; + E3BF5635245C2BB30024D9BF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3BF5634245C2BB30024D9BF /* AppDelegate.swift */; }; + E3BF5637245C2BB30024D9BF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3BF5636245C2BB30024D9BF /* ContentView.swift */; }; + E3BF5639245C2BB50024D9BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E3BF5638245C2BB50024D9BF /* Assets.xcassets */; }; + E3BF563F245C2BB50024D9BF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E3BF563D245C2BB50024D9BF /* Main.storyboard */; }; + E3BF5646245C2D550024D9BF /* KeyboardShortcuts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "KeyboardShortcuts::KeyboardShortcuts::Product" /* KeyboardShortcuts.framework */; }; + E3BF5647245C2D550024D9BF /* KeyboardShortcuts.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = "KeyboardShortcuts::KeyboardShortcuts::Product" /* KeyboardShortcuts.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E3BF564C245C34B30024D9BF /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3BF564B245C34B30024D9BF /* Key.swift */; }; + OBJ_23 /* KeyboardShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* KeyboardShortcuts.swift */; }; + OBJ_24 /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* util.swift */; }; + OBJ_31 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E3BF5648245C2D550024D9BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = OBJ_1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = "KeyboardShortcuts::KeyboardShortcuts"; + remoteInfo = KeyboardShortcuts; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + E3BF564A245C2D550024D9BF /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + E3BF5647245C2D550024D9BF /* KeyboardShortcuts.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + E38103FD246449180023E9A8 /* Name.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Name.swift; sourceTree = ""; usesTabs = 1; }; + E3BF5626245C23840024D9BF /* Recorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Recorder.swift; sourceTree = ""; usesTabs = 1; }; + E3BF5628245C24450024D9BF /* RecorderCocoa.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = RecorderCocoa.swift; sourceTree = ""; usesTabs = 1; }; + E3BF562A245C28BD0024D9BF /* Shortcut.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Shortcut.swift; sourceTree = ""; usesTabs = 1; }; + E3BF562C245C29C30024D9BF /* CarbonKeyboardShortcuts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = CarbonKeyboardShortcuts.swift; sourceTree = ""; usesTabs = 1; }; + E3BF5632245C2BB30024D9BF /* KeyboardShortcutsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KeyboardShortcutsExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E3BF5634245C2BB30024D9BF /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = ""; usesTabs = 1; }; + E3BF5636245C2BB30024D9BF /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ContentView.swift; sourceTree = ""; usesTabs = 1; }; + E3BF5638245C2BB50024D9BF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + E3BF563E245C2BB50024D9BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + E3BF5640245C2BB50024D9BF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E3BF5641245C2BB50024D9BF /* KeyboardShortcutsExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = KeyboardShortcutsExample.entitlements; sourceTree = ""; }; + E3BF564B245C34B30024D9BF /* Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Key.swift; sourceTree = ""; usesTabs = 1; }; + "KeyboardShortcuts::KeyboardShortcuts::Product" /* KeyboardShortcuts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KeyboardShortcuts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + OBJ_10 /* util.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = util.swift; sourceTree = ""; usesTabs = 1; }; + OBJ_16 /* readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; lineEnding = 0; path = readme.md; sourceTree = ""; usesTabs = 1; }; + OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = Package.swift; sourceTree = ""; usesTabs = 1; }; + OBJ_9 /* KeyboardShortcuts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = KeyboardShortcuts.swift; sourceTree = ""; usesTabs = 1; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E3BF562F245C2BB30024D9BF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E3BF5646245C2D550024D9BF /* KeyboardShortcuts.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + OBJ_25 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E3BF5633245C2BB30024D9BF /* KeyboardShortcutsExample */ = { + isa = PBXGroup; + children = ( + E3BF5634245C2BB30024D9BF /* AppDelegate.swift */, + E3BF5636245C2BB30024D9BF /* ContentView.swift */, + E3BF5638245C2BB50024D9BF /* Assets.xcassets */, + E3BF563D245C2BB50024D9BF /* Main.storyboard */, + E3BF5640245C2BB50024D9BF /* Info.plist */, + E3BF5641245C2BB50024D9BF /* KeyboardShortcutsExample.entitlements */, + ); + path = KeyboardShortcutsExample; + sourceTree = ""; + }; + E3BF5645245C2D550024D9BF /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + OBJ_11 /* Tests */ = { + isa = PBXGroup; + children = ( + ); + name = Tests; + sourceTree = SOURCE_ROOT; + }; + OBJ_12 /* Products */ = { + isa = PBXGroup; + children = ( + "KeyboardShortcuts::KeyboardShortcuts::Product" /* KeyboardShortcuts.framework */, + E3BF5632245C2BB30024D9BF /* KeyboardShortcutsExample.app */, + ); + name = Products; + sourceTree = BUILT_PRODUCTS_DIR; + }; + OBJ_5 = { + isa = PBXGroup; + children = ( + OBJ_16 /* readme.md */, + OBJ_6 /* Package.swift */, + OBJ_7 /* Sources */, + OBJ_11 /* Tests */, + E3BF5633245C2BB30024D9BF /* KeyboardShortcutsExample */, + OBJ_12 /* Products */, + E3BF5645245C2D550024D9BF /* Frameworks */, + ); + sourceTree = ""; + }; + OBJ_7 /* Sources */ = { + isa = PBXGroup; + children = ( + OBJ_8 /* KeyboardShortcuts */, + ); + name = Sources; + sourceTree = SOURCE_ROOT; + }; + OBJ_8 /* KeyboardShortcuts */ = { + isa = PBXGroup; + children = ( + OBJ_9 /* KeyboardShortcuts.swift */, + E3BF562C245C29C30024D9BF /* CarbonKeyboardShortcuts.swift */, + E38103FD246449180023E9A8 /* Name.swift */, + E3BF564B245C34B30024D9BF /* Key.swift */, + E3BF562A245C28BD0024D9BF /* Shortcut.swift */, + E3BF5628245C24450024D9BF /* RecorderCocoa.swift */, + E3BF5626245C23840024D9BF /* Recorder.swift */, + OBJ_10 /* util.swift */, + ); + name = KeyboardShortcuts; + path = Sources/KeyboardShortcuts; + sourceTree = SOURCE_ROOT; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E3BF5631245C2BB30024D9BF /* KeyboardShortcutsExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = E3BF5642245C2BB50024D9BF /* Build configuration list for PBXNativeTarget "KeyboardShortcutsExample" */; + buildPhases = ( + E381040124645AC80023E9A8 /* SwiftLint */, + E3BF562E245C2BB30024D9BF /* Sources */, + E3BF562F245C2BB30024D9BF /* Frameworks */, + E3BF5630245C2BB30024D9BF /* Resources */, + E3BF564A245C2D550024D9BF /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + E3BF5649245C2D550024D9BF /* PBXTargetDependency */, + ); + name = KeyboardShortcutsExample; + productName = KeyboardShortcutsExample; + productReference = E3BF5632245C2BB30024D9BF /* KeyboardShortcutsExample.app */; + productType = "com.apple.product-type.application"; + }; + "KeyboardShortcuts::KeyboardShortcuts" /* KeyboardShortcuts */ = { + isa = PBXNativeTarget; + buildConfigurationList = OBJ_19 /* Build configuration list for PBXNativeTarget "KeyboardShortcuts" */; + buildPhases = ( + E38103FF24645A410023E9A8 /* SwiftLint */, + OBJ_22 /* Sources */, + OBJ_25 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KeyboardShortcuts; + productName = KeyboardShortcuts; + productReference = "KeyboardShortcuts::KeyboardShortcuts::Product" /* KeyboardShortcuts.framework */; + productType = "com.apple.product-type.framework"; + }; + "KeyboardShortcuts::SwiftPMPackageDescription" /* KeyboardShortcutsPackageDescription */ = { + isa = PBXNativeTarget; + buildConfigurationList = OBJ_27 /* Build configuration list for PBXNativeTarget "KeyboardShortcutsPackageDescription" */; + buildPhases = ( + OBJ_30 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KeyboardShortcutsPackageDescription; + productName = KeyboardShortcutsPackageDescription; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + OBJ_1 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftMigration = 9999; + LastSwiftUpdateCheck = 1140; + LastUpgradeCheck = 9999; + TargetAttributes = { + E3BF5631245C2BB30024D9BF = { + CreatedOnToolsVersion = 11.4.1; + }; + }; + }; + buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "KeyboardShortcuts" */; + compatibilityVersion = "Xcode 11.4"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = OBJ_5; + productRefGroup = OBJ_12 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + "KeyboardShortcuts::KeyboardShortcuts" /* KeyboardShortcuts */, + "KeyboardShortcuts::SwiftPMPackageDescription" /* KeyboardShortcutsPackageDescription */, + E3BF5631245C2BB30024D9BF /* KeyboardShortcutsExample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E3BF5630245C2BB30024D9BF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E3BF563F245C2BB50024D9BF /* Main.storyboard in Resources */, + E3BF5639245C2BB50024D9BF /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + E38103FF24645A410023E9A8 /* SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftLint; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed\"\nfi\n"; + }; + E381040124645AC80023E9A8 /* SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftLint; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed\"\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E3BF562E245C2BB30024D9BF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E3BF5637245C2BB30024D9BF /* ContentView.swift in Sources */, + E3BF5635245C2BB30024D9BF /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + OBJ_22 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 0; + files = ( + OBJ_23 /* KeyboardShortcuts.swift in Sources */, + E3BF562D245C29C30024D9BF /* CarbonKeyboardShortcuts.swift in Sources */, + OBJ_24 /* util.swift in Sources */, + E3BF5629245C24450024D9BF /* RecorderCocoa.swift in Sources */, + E3BF564C245C34B30024D9BF /* Key.swift in Sources */, + E3BF5627245C23840024D9BF /* Recorder.swift in Sources */, + E38103FE246449180023E9A8 /* Name.swift in Sources */, + E3BF562B245C28BD0024D9BF /* Shortcut.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + OBJ_30 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 0; + files = ( + OBJ_31 /* Package.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + E3BF5649245C2D550024D9BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = "KeyboardShortcuts::KeyboardShortcuts" /* KeyboardShortcuts */; + targetProxy = E3BF5648245C2D550024D9BF /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + E3BF563D245C2BB50024D9BF /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E3BF563E245C2BB50024D9BF /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + E3BF5643245C2BB50024D9BF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = KeyboardShortcutsExample/KeyboardShortcutsExample.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = KeyboardShortcutsExample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 1.0.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.sindresorhus.KeyboardShortcutsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + E3BF5644245C2BB50024D9BF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = KeyboardShortcutsExample/KeyboardShortcutsExample.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_PREVIEWS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = KeyboardShortcutsExample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 1.0.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.sindresorhus.KeyboardShortcutsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + OBJ_20 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PLATFORM_DIR)/Developer/Library/Frameworks", + ); + HEADER_SEARCH_PATHS = "$(inherited)"; + INFOPLIST_FILE = KeyboardShortcuts.xcodeproj/KeyboardShortcuts_Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx", + ); + MARKETING_VERSION = 0.0.0; + OTHER_CFLAGS = "$(inherited)"; + OTHER_LDFLAGS = "$(inherited)"; + OTHER_SWIFT_FLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = KeyboardShortcuts; + PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; + SWIFT_VERSION = 5.0; + TARGET_NAME = KeyboardShortcuts; + TVOS_DEPLOYMENT_TARGET = 9.0; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Debug; + }; + OBJ_21 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PLATFORM_DIR)/Developer/Library/Frameworks", + ); + HEADER_SEARCH_PATHS = "$(inherited)"; + INFOPLIST_FILE = KeyboardShortcuts.xcodeproj/KeyboardShortcuts_Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx", + ); + MARKETING_VERSION = 0.0.0; + OTHER_CFLAGS = "$(inherited)"; + OTHER_LDFLAGS = "$(inherited)"; + OTHER_SWIFT_FLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = KeyboardShortcuts; + PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; + SWIFT_VERSION = 5.0; + TARGET_NAME = KeyboardShortcuts; + TVOS_DEPLOYMENT_TARGET = 9.0; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Release; + }; + OBJ_28 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD = /usr/bin/true; + OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 5.2.0"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + OBJ_29 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LD = /usr/bin/true; + OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 5.2.0"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + OBJ_3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SWIFT_PACKAGE=1", + "DEBUG=1", + ); + MACOSX_DEPLOYMENT_TARGET = 10.11; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ( + "-weak_framework", + Combine, + ); + OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE DEBUG"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + USE_HEADERMAP = NO; + VALID_ARCHS = x86_64; + }; + name = Debug; + }; + OBJ_4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SWIFT_PACKAGE=1", + ); + MACOSX_DEPLOYMENT_TARGET = 10.11; + OTHER_LDFLAGS = ( + "-weak_framework", + Combine, + ); + OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + USE_HEADERMAP = NO; + VALID_ARCHS = x86_64; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E3BF5642245C2BB50024D9BF /* Build configuration list for PBXNativeTarget "KeyboardShortcutsExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E3BF5643245C2BB50024D9BF /* Debug */, + E3BF5644245C2BB50024D9BF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + OBJ_19 /* Build configuration list for PBXNativeTarget "KeyboardShortcuts" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + OBJ_20 /* Debug */, + OBJ_21 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + OBJ_2 /* Build configuration list for PBXProject "KeyboardShortcuts" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + OBJ_3 /* Debug */, + OBJ_4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + OBJ_27 /* Build configuration list for PBXNativeTarget "KeyboardShortcutsPackageDescription" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + OBJ_28 /* Debug */, + OBJ_29 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = OBJ_1 /* Project object */; +} diff --git a/KeyboardShortcuts.xcodeproj/xcshareddata/xcschemes/KeyboardShortcuts-Package.xcscheme b/KeyboardShortcuts.xcodeproj/xcshareddata/xcschemes/KeyboardShortcuts-Package.xcscheme new file mode 100644 index 0000000..4a8d02e --- /dev/null +++ b/KeyboardShortcuts.xcodeproj/xcshareddata/xcschemes/KeyboardShortcuts-Package.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KeyboardShortcuts.xcodeproj/xcshareddata/xcschemes/KeyboardShortcutsExample.xcscheme b/KeyboardShortcuts.xcodeproj/xcshareddata/xcschemes/KeyboardShortcutsExample.xcscheme new file mode 100644 index 0000000..0bc1c6f --- /dev/null +++ b/KeyboardShortcuts.xcodeproj/xcshareddata/xcschemes/KeyboardShortcutsExample.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KeyboardShortcutsExample/AppDelegate.swift b/KeyboardShortcutsExample/AppDelegate.swift new file mode 100644 index 0000000..b8ee573 --- /dev/null +++ b/KeyboardShortcutsExample/AppDelegate.swift @@ -0,0 +1,29 @@ +import Cocoa +import SwiftUI + +@NSApplicationMain +final class AppDelegate: NSObject, NSApplicationDelegate { + var window: NSWindow! + + func applicationDidFinishLaunching(_ notification: Notification) { + let contentView = ContentView() + + window = NSWindow( + contentRect: CGRect(x: 0, y: 0, width: 480, height: 300), + styleMask: [ + .titled, + .closable, + .miniaturizable, + .fullSizeContentView + ], + backing: .buffered, + defer: false + ) + + window.title = "KeyboardShortcuts Example" + window.center() + window.setFrameAutosaveName("Main Window") + window.contentView = NSHostingView(rootView: contentView) + window.makeKeyAndOrderFront(nil) + } +} diff --git a/KeyboardShortcutsExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/KeyboardShortcutsExample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..3f00db4 --- /dev/null +++ b/KeyboardShortcutsExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/KeyboardShortcutsExample/Assets.xcassets/Contents.json b/KeyboardShortcutsExample/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/KeyboardShortcutsExample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/KeyboardShortcutsExample/Base.lproj/Main.storyboard b/KeyboardShortcutsExample/Base.lproj/Main.storyboard new file mode 100644 index 0000000..042a849 --- /dev/null +++ b/KeyboardShortcutsExample/Base.lproj/Main.storyboard @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KeyboardShortcutsExample/ContentView.swift b/KeyboardShortcutsExample/ContentView.swift new file mode 100644 index 0000000..cba09b6 --- /dev/null +++ b/KeyboardShortcutsExample/ContentView.swift @@ -0,0 +1,52 @@ +import SwiftUI +import KeyboardShortcuts + +extension KeyboardShortcuts.Name { + static let testShortcut1 = Name("testShortcut1") + static let testShortcut2 = Name("testShortcut2") +} + +struct ContentView: View { + @State private var isPressed1 = false + @State private var isPressed2 = false + + var body: some View { + VStack { + HStack { + KeyboardShortcuts.Recorder(for: .testShortcut1) + Text("Is pressed? \(isPressed1 ? "Yes" : "No")") + .frame(width: 100, alignment: .leading) + } + HStack { + KeyboardShortcuts.Recorder(for: .testShortcut2) + Text("Is pressed? \(isPressed2 ? "Yes" : "No")") + .frame(width: 100, alignment: .leading) + } + } + .frame(maxWidth: 300) + .padding(60) + .onAppear { + KeyboardShortcuts.onKeyDown(for: .testShortcut1) { + self.isPressed1 = true + } + + KeyboardShortcuts.onKeyUp(for: .testShortcut1) { + self.isPressed1 = false + } + + KeyboardShortcuts.onKeyDown(for: .testShortcut2) { + self.isPressed2 = true + } + + KeyboardShortcuts.onKeyUp(for: .testShortcut2) { + self.isPressed2 = false + } + } + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/KeyboardShortcutsExample/Info.plist b/KeyboardShortcutsExample/Info.plist new file mode 100644 index 0000000..4816187 --- /dev/null +++ b/KeyboardShortcutsExample/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + NSSupportsAutomaticTermination + + NSSupportsSuddenTermination + + + diff --git a/KeyboardShortcutsExample/KeyboardShortcutsExample.entitlements b/KeyboardShortcutsExample/KeyboardShortcutsExample.entitlements new file mode 100644 index 0000000..8e790e7 --- /dev/null +++ b/KeyboardShortcutsExample/KeyboardShortcutsExample.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.disable-library-validation + + + diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..f28fd76 --- /dev/null +++ b/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version:5.2 +import PackageDescription + +let package = Package( + name: "KeyboardShortcuts", + platforms: [ + .macOS(.v10_11) + ], + products: [ + .library( + name: "KeyboardShortcuts", + targets: [ + "KeyboardShortcuts" + ] + ) + ], + targets: [ + .target( + name: "KeyboardShortcuts" + ) + ] +) diff --git a/Sources/KeyboardShortcuts/CarbonKeyboardShortcuts.swift b/Sources/KeyboardShortcuts/CarbonKeyboardShortcuts.swift new file mode 100644 index 0000000..03b7cd6 --- /dev/null +++ b/Sources/KeyboardShortcuts/CarbonKeyboardShortcuts.swift @@ -0,0 +1,181 @@ +import Carbon.HIToolbox + +private func carbonKeyboardShortcutsEventHandler(eventHandlerCall: EventHandlerCallRef?, event: EventRef?, userData: UnsafeMutableRawPointer?) -> OSStatus { + CarbonKeyboardShortcuts.handleEvent(event) +} + +final class CarbonKeyboardShortcuts { + private final class HotKey { + let shortcut: KeyboardShortcuts.Shortcut + let carbonHotKeyId: Int + let carbonHotKey: EventHotKeyRef + let onKeyDown: (KeyboardShortcuts.Shortcut) -> Void + let onKeyUp: (KeyboardShortcuts.Shortcut) -> Void + + init( + shortcut: KeyboardShortcuts.Shortcut, + carbonHotKeyID: Int, + carbonHotKey: EventHotKeyRef, + onKeyDown: @escaping (KeyboardShortcuts.Shortcut) -> Void, + onKeyUp: @escaping (KeyboardShortcuts.Shortcut) -> Void + ) { + self.shortcut = shortcut + self.carbonHotKeyId = carbonHotKeyID + self.carbonHotKey = carbonHotKey + self.onKeyDown = onKeyDown + self.onKeyUp = onKeyUp + } + } + + private static var hotKeys = [Int: HotKey]() + + // `SSKS` is just short for `Sindre Sorhus Keyboard Shortcuts`. + private static let hotKeySignature = UTGetOSTypeFromString("SSKS" as CFString) + + private static var hotKeyId = 0 + private static var eventHandler: EventHandlerRef? + + private static func setUpEventHandlerIfNeeded() { + guard + eventHandler == nil, + let dispatcher = GetEventDispatcherTarget() + else { + return + } + + let eventSpecs = [ + EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyPressed)), + EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyReleased)) + ] + + InstallEventHandler( + dispatcher, + carbonKeyboardShortcutsEventHandler, + eventSpecs.count, + eventSpecs, + nil, + &eventHandler + ) + } + + static func register( + _ shortcut: KeyboardShortcuts.Shortcut, + onKeyDown: @escaping (KeyboardShortcuts.Shortcut) -> Void, + onKeyUp: @escaping (KeyboardShortcuts.Shortcut) -> Void + ) { + hotKeyId += 1 + + var eventHotKey: EventHotKeyRef? + let registerError = RegisterEventHotKey( + UInt32(shortcut.carbonKeyCode), + UInt32(shortcut.carbonModifiers), + EventHotKeyID(signature: hotKeySignature, id: UInt32(hotKeyId)), + GetEventDispatcherTarget(), + 0, + &eventHotKey + ) + + guard + registerError == noErr, + let carbonHotKey = eventHotKey + else { + return + } + + hotKeys[hotKeyId] = HotKey( + shortcut: shortcut, + carbonHotKeyID: hotKeyId, + carbonHotKey: carbonHotKey, + onKeyDown: onKeyDown, + onKeyUp: onKeyUp + ) + + setUpEventHandlerIfNeeded() + } + + private static func unregisterHotKey(_ hotKey: HotKey) { + UnregisterEventHotKey(hotKey.carbonHotKey) + hotKeys.removeValue(forKey: hotKey.carbonHotKeyId) + } + + static func unregister(_ shortcut: KeyboardShortcuts.Shortcut) { + for hotKey in hotKeys.values where hotKey.shortcut == shortcut { + unregisterHotKey(hotKey) + } + } + + static func unregisterAll() { + for hotKey in hotKeys.values { + unregisterHotKey(hotKey) + } + } + + fileprivate static func handleEvent(_ event: EventRef?) -> OSStatus { + guard let event = event else { + return OSStatus(eventNotHandledErr) + } + + var eventHotKeyId = EventHotKeyID() + let error = GetEventParameter( + event, + UInt32(kEventParamDirectObject), + UInt32(typeEventHotKeyID), + nil, + MemoryLayout.size, + nil, + &eventHotKeyId + ) + + guard error == noErr else { + return error + } + + guard + eventHotKeyId.signature == hotKeySignature, + let hotKey = hotKeys[Int(eventHotKeyId.id)] + else { + return OSStatus(eventNotHandledErr) + } + + switch Int(GetEventKind(event)) { + case kEventHotKeyPressed: + hotKey.onKeyDown(hotKey.shortcut) + return noErr + case kEventHotKeyReleased: + hotKey.onKeyUp(hotKey.shortcut) + return noErr + default: + break + } + + return OSStatus(eventNotHandledErr) + } +} + +extension CarbonKeyboardShortcuts { + static var system: [KeyboardShortcuts.Shortcut] { + var shortcutsUnmanaged: Unmanaged? + guard + CopySymbolicHotKeys(&shortcutsUnmanaged) == noErr, + let shortcuts = shortcutsUnmanaged?.takeRetainedValue() as? [[String: Any]] + else { + assertionFailure("Could not get system keyboard shortcuts") + return [] + } + + return shortcuts.compactMap { + guard + ($0[kHISymbolicHotKeyEnabled] as? Bool) == true, + let carbonKeyCode = $0[kHISymbolicHotKeyCode] as? Int, + let carbonModifiers = $0[kHISymbolicHotKeyModifiers] as? Int + else { + return nil + } + + return KeyboardShortcuts.Shortcut( + carbonKeyCode: carbonKeyCode, + carbonModifiers: carbonModifiers + ) + } + } +} diff --git a/Sources/KeyboardShortcuts/Key.swift b/Sources/KeyboardShortcuts/Key.swift new file mode 100644 index 0000000..2a95c00 --- /dev/null +++ b/Sources/KeyboardShortcuts/Key.swift @@ -0,0 +1,743 @@ +import Cocoa +import Carbon.HIToolbox + +extension KeyboardShortcuts { + // swiftlint:disable identifier_name + /// Represents a key on the keyboard. + public enum Key: RawRepresentable { + case a + case b + case c + case d + case e + case f + case g + case h + case i + case j + case k + case l + case m + case n + case o + case p + case q + case r + case s + case t + case u + case v + case w + case x + case y + case z + + case zero + case one + case two + case three + case four + case five + case six + case seven + case eight + case nine + + case backslash + case backtick + case comma + case equal + case escape + case leftBracket + case minus + case period + case quote + case rightBracket + case semicolon + case slash + case space + + // `NSEvent.SpecialKey` + case backTab + case backspace + case begin + case `break` + case carriageReturn + case clearDisplay + case clearLine + case delete + case deleteCharacter + case deleteForward + case deleteLine + case downArrow + case end + case enter + case execute + case f1 + case f2 + case f3 + case f4 + case f5 + case f6 + case f7 + case f8 + case f9 + case f10 + case f11 + case f12 + case f13 + case f14 + case f15 + case f16 + case f17 + case f18 + case f19 + case f20 + case f21 + case f22 + case f23 + case f24 + case f25 + case f26 + case f27 + case f28 + case f29 + case f30 + case f31 + case f32 + case f33 + case f34 + case f35 + case find + case formFeed + case help + case home + case insert + case insertCharacter + case insertLine + case leftArrow + case lineSeparator + case menu + case modeSwitch + case newline + case next + case pageDown + case pageUp + case paragraphSeparator + case pause + case prev + case print + case printScreen + case redo + case reset + case rightArrow + case scrollLock + case select + case stop + case sysReq + case system + case tab + case undo + case upArrow + case user + // swiftlint:enable identifier_name + + /// Create a `Key` from a key code. + public init?(rawValue: Int) { + switch rawValue { + case kVK_ANSI_A: + self = .a + case kVK_ANSI_B: + self = .b + case kVK_ANSI_C: + self = .c + case kVK_ANSI_D: + self = .d + case kVK_ANSI_E: + self = .e + case kVK_ANSI_F: + self = .f + case kVK_ANSI_G: + self = .g + case kVK_ANSI_H: + self = .h + case kVK_ANSI_I: + self = .i + case kVK_ANSI_J: + self = .j + case kVK_ANSI_K: + self = .k + case kVK_ANSI_L: + self = .l + case kVK_ANSI_M: + self = .m + case kVK_ANSI_N: + self = .n + case kVK_ANSI_O: + self = .o + case kVK_ANSI_P: + self = .p + case kVK_ANSI_Q: + self = .q + case kVK_ANSI_R: + self = .r + case kVK_ANSI_S: + self = .s + case kVK_ANSI_T: + self = .t + case kVK_ANSI_U: + self = .u + case kVK_ANSI_V: + self = .v + case kVK_ANSI_W: + self = .w + case kVK_ANSI_X: + self = .x + case kVK_ANSI_Y: + self = .y + case kVK_ANSI_Z: + self = .z + case kVK_ANSI_0: + self = .zero + case kVK_ANSI_1: + self = .one + case kVK_ANSI_2: + self = .two + case kVK_ANSI_3: + self = .three + case kVK_ANSI_4: + self = .four + case kVK_ANSI_5: + self = .five + case kVK_ANSI_6: + self = .six + case kVK_ANSI_7: + self = .seven + case kVK_ANSI_8: + self = .eight + case kVK_ANSI_9: + self = .nine + case kVK_ANSI_Backslash: + self = .backslash + case kVK_ANSI_Grave: + self = .backtick + case kVK_ANSI_Comma: + self = .comma + case kVK_ANSI_Equal: + self = .equal + case kVK_Escape: + self = .escape + case kVK_ANSI_LeftBracket: + self = .leftBracket + case kVK_ANSI_Minus: + self = .minus + case kVK_ANSI_Period: + self = .period + case kVK_ANSI_Quote: + self = .quote + case kVK_ANSI_RightBracket: + self = .rightBracket + case kVK_ANSI_Semicolon: + self = .semicolon + case kVK_ANSI_Slash: + self = .slash + case kVK_Space: + self = .space + default: + self.init(specialKey: .init(rawValue: rawValue)) + } + } + + private init?(specialKey: NSEvent.SpecialKey) { + switch specialKey { + case .backTab: + self = .backTab + case .backspace: + self = .backspace + case .begin: + self = .begin + case .break: + self = .break + case .carriageReturn: + self = .carriageReturn + case .clearDisplay: + self = .clearDisplay + case .clearLine: + self = .clearLine + case .delete: + self = .delete + case .deleteCharacter: + self = .deleteCharacter + case .deleteForward: + self = .deleteForward + case .deleteLine: + self = .deleteLine + case .downArrow: + self = .downArrow + case .end: + self = .end + case .enter: + self = .enter + case .execute: + self = .execute + case .f1: + self = .f1 + case .f2: + self = .f2 + case .f3: + self = .f3 + case .f4: + self = .f4 + case .f5: + self = .f5 + case .f6: + self = .f6 + case .f7: + self = .f7 + case .f8: + self = .f8 + case .f9: + self = .f9 + case .f10: + self = .f10 + case .f11: + self = .f11 + case .f12: + self = .f12 + case .f13: + self = .f13 + case .f14: + self = .f14 + case .f15: + self = .f15 + case .f16: + self = .f16 + case .f17: + self = .f17 + case .f18: + self = .f18 + case .f19: + self = .f19 + case .f20: + self = .f20 + case .f21: + self = .f21 + case .f22: + self = .f22 + case .f23: + self = .f23 + case .f24: + self = .f24 + case .f25: + self = .f25 + case .f26: + self = .f26 + case .f27: + self = .f27 + case .f28: + self = .f28 + case .f29: + self = .f29 + case .f30: + self = .f30 + case .f31: + self = .f31 + case .f32: + self = .f32 + case .f33: + self = .f33 + case .f34: + self = .f34 + case .f35: + self = .f35 + case .find: + self = .find + case .formFeed: + self = .formFeed + case .help: + self = .help + case .home: + self = .home + case .insert: + self = .insert + case .insertCharacter: + self = .insertCharacter + case .insertLine: + self = .insertLine + case .leftArrow: + self = .leftArrow + case .lineSeparator: + self = .lineSeparator + case .menu: + self = .menu + case .modeSwitch: + self = .modeSwitch + case .newline: + self = .newline + case .next: + self = .next + case .pageDown: + self = .pageDown + case .pageUp: + self = .pageUp + case .paragraphSeparator: + self = .paragraphSeparator + case .pause: + self = .pause + case .prev: + self = .prev + case .print: + self = .print + case .printScreen: + self = .printScreen + case .redo: + self = .redo + case .reset: + self = .reset + case .rightArrow: + self = .rightArrow + case .scrollLock: + self = .scrollLock + case .select: + self = .select + case .stop: + self = .stop + case .sysReq: + self = .sysReq + case .system: + self = .system + case .tab: + self = .tab + case .undo: + self = .undo + case .upArrow: + self = .upArrow + case .user: + self = .user + default: + return nil + } + } + + private var specialKey: NSEvent.SpecialKey? { + switch self { + case .backTab: + return .backTab + case .backspace: + return .backspace + case .begin: + return .begin + case .break: + return .break + case .carriageReturn: + return .carriageReturn + case .clearDisplay: + return .clearDisplay + case .clearLine: + return .clearLine + case .delete: + return .delete + case .deleteCharacter: + return .deleteCharacter + case .deleteForward: + return .deleteForward + case .deleteLine: + return .deleteLine + case .downArrow: + return .downArrow + case .end: + return .end + case .enter: + return .enter + case .execute: + return .execute + case .f1: + return .f1 + case .f2: + return .f2 + case .f3: + return .f3 + case .f4: + return .f4 + case .f5: + return .f5 + case .f6: + return .f6 + case .f7: + return .f7 + case .f8: + return .f8 + case .f9: + return .f9 + case .f10: + return .f10 + case .f11: + return .f11 + case .f12: + return .f12 + case .f13: + return .f13 + case .f14: + return .f14 + case .f15: + return .f15 + case .f16: + return .f16 + case .f17: + return .f17 + case .f18: + return .f18 + case .f19: + return .f19 + case .f20: + return .f20 + case .f21: + return .f21 + case .f22: + return .f22 + case .f23: + return .f23 + case .f24: + return .f24 + case .f25: + return .f25 + case .f26: + return .f26 + case .f27: + return .f27 + case .f28: + return .f28 + case .f29: + return .f29 + case .f30: + return .f30 + case .f31: + return .f31 + case .f32: + return .f32 + case .f33: + return .f33 + case .f34: + return .f34 + case .f35: + return .f35 + case .find: + return .find + case .formFeed: + return .formFeed + case .help: + return .help + case .home: + return .home + case .insert: + return .insert + case .insertCharacter: + return .insertCharacter + case .insertLine: + return .insertLine + case .leftArrow: + return .leftArrow + case .lineSeparator: + return .lineSeparator + case .menu: + return .menu + case .modeSwitch: + return .modeSwitch + case .newline: + return .newline + case .next: + return .next + case .pageDown: + return .pageDown + case .pageUp: + return .pageUp + case .paragraphSeparator: + return .paragraphSeparator + case .pause: + return .pause + case .prev: + return .prev + case .print: + return .print + case .printScreen: + return .printScreen + case .redo: + return .redo + case .reset: + return .reset + case .rightArrow: + return .rightArrow + case .scrollLock: + return .scrollLock + case .select: + return .select + case .stop: + return .stop + case .sysReq: + return .sysReq + case .system: + return .system + case .tab: + return .tab + case .undo: + return .undo + case .upArrow: + return .upArrow + case .user: + return .user + default: + return nil + } + } + + /// The raw key code. + public var rawValue: Int { + switch self { + case .a: + return kVK_ANSI_A + case .b: + return kVK_ANSI_B + case .c: + return kVK_ANSI_C + case .d: + return kVK_ANSI_D + case .e: + return kVK_ANSI_E + case .f: + return kVK_ANSI_F + case .g: + return kVK_ANSI_G + case .h: + return kVK_ANSI_H + case .i: + return kVK_ANSI_I + case .j: + return kVK_ANSI_J + case .k: + return kVK_ANSI_K + case .l: + return kVK_ANSI_L + case .m: + return kVK_ANSI_M + case .n: + return kVK_ANSI_N + case .o: + return kVK_ANSI_O + case .p: + return kVK_ANSI_P + case .q: + return kVK_ANSI_Q + case .r: + return kVK_ANSI_R + case .s: + return kVK_ANSI_S + case .t: + return kVK_ANSI_T + case .u: + return kVK_ANSI_U + case .v: + return kVK_ANSI_V + case .w: + return kVK_ANSI_W + case .x: + return kVK_ANSI_X + case .y: + return kVK_ANSI_Y + case .z: + return kVK_ANSI_Z + case .zero: + return kVK_ANSI_0 + case .one: + return kVK_ANSI_1 + case .two: + return kVK_ANSI_2 + case .three: + return kVK_ANSI_3 + case .four: + return kVK_ANSI_4 + case .five: + return kVK_ANSI_5 + case .six: + return kVK_ANSI_6 + case .seven: + return kVK_ANSI_7 + case .eight: + return kVK_ANSI_8 + case .nine: + return kVK_ANSI_9 + case .backslash: + return kVK_ANSI_Backslash + case .backtick: + return kVK_ANSI_Grave + case .comma: + return kVK_ANSI_Comma + case .equal: + return kVK_ANSI_Equal + case .escape: + return kVK_Escape + case .leftBracket: + return kVK_ANSI_LeftBracket + case .minus: + return kVK_ANSI_Minus + case .period: + return kVK_ANSI_Period + case .quote: + return kVK_ANSI_Quote + case .rightBracket: + return kVK_ANSI_RightBracket + case .semicolon: + return kVK_ANSI_Semicolon + case .slash: + return kVK_ANSI_Slash + case .space: + return kVK_Space + case .backTab, .backspace, .begin, .break, .carriageReturn, .clearDisplay, .clearLine, .delete, .deleteCharacter, .deleteForward, .deleteLine, .downArrow, .end, .enter, .execute, .f1, .f2, .f3, .f4, .f5, .f6, .f7, .f8, .f9, .f10, .f11, .f12, .f13, .f14, .f15, .f16, .f17, .f18, .f19, .f20, .f21, .f22, .f23, .f24, .f25, .f26, .f27, .f28, .f29, .f30, .f31, .f32, .f33, .f34, .f35, .find, .formFeed, .help, .home, .insert, .insertCharacter, .insertLine, .leftArrow, .lineSeparator, .menu, .modeSwitch, .newline, .next, .pageDown, .pageUp, .paragraphSeparator, .pause, .prev, .print, .printScreen, .redo, .reset, .rightArrow, .scrollLock, .select, .stop, .sysReq, .system, .tab, .undo, .upArrow, .user: + return specialKey?.rawValue ?? 0 + } + } + } +} + +extension KeyboardShortcuts.Key { + /// All the function keys. + static let functionKeys: Set = [ + .f1, + .f2, + .f3, + .f4, + .f5, + .f6, + .f7, + .f8, + .f9, + .f10, + .f11, + .f12, + .f13, + .f14, + .f15, + .f16, + .f17, + .f18, + .f19, + .f20, + .f21, + .f22, + .f23, + .f24, + .f25, + .f26, + .f27, + .f28, + .f29, + .f30, + .f31, + .f32, + .f33, + .f34, + .f35 + ] + + /// Returns true if the key is a function key. For example, `F1`. + var isFunctionKey: Bool { Self.functionKeys.contains(self) } +} diff --git a/Sources/KeyboardShortcuts/KeyboardShortcuts.swift b/Sources/KeyboardShortcuts/KeyboardShortcuts.swift new file mode 100644 index 0000000..ed4eee7 --- /dev/null +++ b/Sources/KeyboardShortcuts/KeyboardShortcuts.swift @@ -0,0 +1,201 @@ +import Cocoa + +/** +Global keyboard shortcuts for your macOS app. +*/ +public final class KeyboardShortcuts { + /// :nodoc: + public typealias KeyAction = () -> Void + + private static var registeredShortcuts = Set() + + // Not currently used. For the future. + private static var keyDownHandlers = [Shortcut: [KeyAction]]() + private static var keyUpHandlers = [Shortcut: [KeyAction]]() + + private static var userDefaultsKeyDownHandlers = [Name: [KeyAction]]() + private static var userDefaultsKeyUpHandlers = [Name: [KeyAction]]() + + private static func register(_ shortcut: Shortcut) { + guard !registeredShortcuts.contains(shortcut) else { + return + } + + CarbonKeyboardShortcuts.register( + shortcut, + onKeyDown: handleOnKeyDown, + onKeyUp: handleOnKeyUp + ) + + registeredShortcuts.insert(shortcut) + } + + private static func unregister(_ shortcut: Shortcut) { + CarbonKeyboardShortcuts.unregister(shortcut) + registeredShortcuts.remove(shortcut) + } + + // TODO: Doc comment and make this public. + static func unregisterAll() { + CarbonKeyboardShortcuts.unregisterAll() + registeredShortcuts.removeAll() + + // TODO: Should remove user defaults too. + } + + // TODO: Also add `.isEnabled(_ name: Name)`. + static func disable(_ name: Name) { + guard let shortcut = userDefaultsGet(name: name) else { + return + } + + unregister(shortcut) + } + + static func enable(_ name: Name) { + guard let shortcut = userDefaultsGet(name: name) else { + return + } + + register(shortcut) + } + + private static func handleOnKeyDown(_ shortcut: Shortcut) { + if let handlers = keyDownHandlers[shortcut] { + for handler in handlers { + handler() + } + } + + for (name, handlers) in userDefaultsKeyDownHandlers { + guard userDefaultsGet(name: name) == shortcut else { + continue + } + + for handler in handlers { + handler() + } + } + } + + private static func handleOnKeyUp(_ shortcut: Shortcut) { + if let handlers = keyUpHandlers[shortcut] { + for handler in handlers { + handler() + } + } + + for (name, handlers) in userDefaultsKeyUpHandlers { + guard userDefaultsGet(name: name) == shortcut else { + continue + } + + for handler in handlers { + handler() + } + } + } + + /** + Listen to the keyboard shortcut with the given name being pressed. + + You can register multiple listeners. + + You can safely call this even if the user has not yet set a keyboard shortcut. It will just be inactive until they do. + + ``` + import Cocoa + import KeyboardShortcuts + + @NSApplicationMain + final class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + KeyboardShortcuts.onKeyDown(for: .toggleUnicornMode) { + self.isUnicornMode.toggle() + } + } + } + ``` + */ + public static func onKeyDown(for name: Name, action: @escaping KeyAction) { + if userDefaultsKeyDownHandlers[name] == nil { + userDefaultsKeyDownHandlers[name] = [] + } + + userDefaultsKeyDownHandlers[name]?.append(action) + + // If the keyboard shortcut already exist, we register it. + if let shortcut = userDefaultsGet(name: name) { + register(shortcut) + } + } + + /** + Listen to the keyboard shortcut with the given name being pressed. + + You can register multiple listeners. + + You can safely call this even if the user has not yet set a keyboard shortcut. It will just be inactive until they do. + + ``` + import Cocoa + import KeyboardShortcuts + + @NSApplicationMain + final class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + KeyboardShortcuts.onKeyUp(for: .toggleUnicornMode) { + self.isUnicornMode.toggle() + } + } + } + ``` + */ + public static func onKeyUp(for name: Name, action: @escaping KeyAction) { + if userDefaultsKeyUpHandlers[name] == nil { + userDefaultsKeyUpHandlers[name] = [] + } + + userDefaultsKeyUpHandlers[name]?.append(action) + + // If the keyboard shortcut already exist, we register it. + if let shortcut = userDefaultsGet(name: name) { + register(shortcut) + } + } + + private static let userDefaultsPrefix = "KeyboardShortcuts_" + + private static func userDefaultsKey(for shortcutName: Name) -> String { "\(userDefaultsPrefix)\(shortcutName.rawValue)" + } + + // TODO: Should these be on `Shortcut` instead? + static func userDefaultsGet(name: Name) -> Shortcut? { + guard + let data = UserDefaults.standard.string(forKey: userDefaultsKey(for: name))?.data(using: .utf8), + let decoded = try? JSONDecoder().decode(Shortcut.self, from: data) + else { + return nil + } + + return decoded + } + + static func userDefaultsSet(name: Name, shortcut: Shortcut) { + guard let encoded = try? JSONEncoder().encode(shortcut).string else { + return + } + + UserDefaults.standard.set(encoded, forKey: userDefaultsKey(for: name)) + register(shortcut) + } + + static func userDefaultsRemove(name: Name) { + guard let shortcut = userDefaultsGet(name: name) else { + return + } + + UserDefaults.standard.removeObject(forKey: userDefaultsKey(for: name)) + unregister(shortcut) + } +} diff --git a/Sources/KeyboardShortcuts/Name.swift b/Sources/KeyboardShortcuts/Name.swift new file mode 100644 index 0000000..b8d0317 --- /dev/null +++ b/Sources/KeyboardShortcuts/Name.swift @@ -0,0 +1,33 @@ +extension KeyboardShortcuts { + /** + The strongly-typed name of the keyboard shortcut. + + After registering it, you can use it in, for example, `KeyboardShortcut.Recorder` and `KeyboardShortcut.onKeyUp()`. + + ``` + import KeyboardShortcuts + + extension KeyboardShortcuts.Name { + static let toggleUnicornMode = Name("toggleUnicornMode") + } + ``` + */ + public struct Name: Hashable { + // This is to allow `extension KeyboardShortcuts.Name { static let x = Name("x") }`. + /// :nodoc: + public typealias Name = KeyboardShortcuts.Name + + public let rawValue: String + + public init(_ name: String) { + self.rawValue = name + } + } +} + +extension KeyboardShortcuts.Name: RawRepresentable { + /// :nodoc: + public init?(rawValue: String) { + self.init(rawValue) + } +} diff --git a/Sources/KeyboardShortcuts/Recorder.swift b/Sources/KeyboardShortcuts/Recorder.swift new file mode 100644 index 0000000..93a075d --- /dev/null +++ b/Sources/KeyboardShortcuts/Recorder.swift @@ -0,0 +1,51 @@ +import SwiftUI + +@available(macOS 10.15, *) +extension KeyboardShortcuts { + /** + A SwiftUI `View` that lets the user record a keyboard shortcut. + + You would usually put this in your preferences window. + + It automatically prevents choosing a keyboard shortcut that is already taken by the system or by the app's main menu by showing a user-friendly alert to the user. + + It takes care of storing the keyboard shortcut in `UserDefaults` for you. + + ``` + import SwiftUI + import KeyboardShortcuts + + struct PreferencesView: View { + var body: some View { + HStack { + Text("Toggle Unicorn Mode:") + KeyboardShortcuts.Recorder(for: .toggleUnicornMode) + } + } + } + ``` + */ + public struct Recorder: NSViewRepresentable { // swiftlint:disable:this type_name + /// :nodoc: + public typealias NSViewType = RecorderCocoa + + private let name: Name + + public init(for name: Name) { + self.name = name + } + + /// :nodoc: + public func makeNSView(context: Context) -> NSViewType { .init(for: name) } + + /// :nodoc: + public func updateNSView(_ nsView: NSViewType, context: Context) {} + } +} + +@available(macOS 10.15, *) +struct SwiftUI_Previews: PreviewProvider { + static var previews: some View { + KeyboardShortcuts.Recorder(for: .Name("xcodePreview")) + } +} diff --git a/Sources/KeyboardShortcuts/RecorderCocoa.swift b/Sources/KeyboardShortcuts/RecorderCocoa.swift new file mode 100644 index 0000000..6505a1a --- /dev/null +++ b/Sources/KeyboardShortcuts/RecorderCocoa.swift @@ -0,0 +1,168 @@ +import Cocoa +import Carbon.HIToolbox + +extension KeyboardShortcuts { + /** + A `NSView` that lets the user record a keyboard shortcut. + + You would usually put this in your preferences window. + + It automatically prevents choosing a keyboard shortcut that is already taken by the system or by the app's main menu by showing a user-friendly alert to the user. + + It takes care of storing the keyboard shortcut in `UserDefaults` for you. + + ``` + import Cocoa + import KeyboardShortcuts + + final class PreferencesViewController: NSViewController { + override func loadView() { + view = NSView() + + let recorder = KeyboardShortcuts.RecorderCocoa(for: .toggleUnicornMode) + view.addSubview(recorder) + } + } + ``` + */ + public final class RecorderCocoa: NSSearchField, NSSearchFieldDelegate { + private let minimumWidth: Double = 130 + private var eventMonitor: LocalEventMonitor? + private let shortcutName: Name + + /// :nodoc: + override public var canBecomeKeyView: Bool { false } + + /// :nodoc: + override public var intrinsicContentSize: CGSize { + var size = super.intrinsicContentSize + size.width = CGFloat(minimumWidth) + return size + } + + public required init(for name: Name) { + self.shortcutName = name + + super.init(frame: .zero) + self.delegate = self + self.placeholderString = "Click to Record" + self.centersPlaceholder = true + self.alignment = .center + (self.cell as? NSSearchFieldCell)?.searchButtonCell = nil + + if let shortcut = userDefaultsGet(name: shortcutName) { + self.stringValue = "\(shortcut)" + } + + self.wantsLayer = true + self.translatesAutoresizingMaskIntoConstraints = false + self.setContentHuggingPriority(.defaultHigh, for: .vertical) + self.setContentHuggingPriority(.defaultHigh, for: .horizontal) + self.widthAnchor.constraint(greaterThanOrEqualToConstant: CGFloat(minimumWidth)).isActive = true + } + + @available(*, unavailable) + public required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + /// :nodoc: + public func controlTextDidChange(_ object: Notification) { + if stringValue.isEmpty { + userDefaultsRemove(name: shortcutName) + } + } + + /// :nodoc: + public func controlTextDidEndEditing(_ object: Notification) { + eventMonitor = nil + placeholderString = "Click to Record" + } + + /// :nodoc: + override public func becomeFirstResponder() -> Bool { + let shouldBecomeFirstResponder = super.becomeFirstResponder() + + guard shouldBecomeFirstResponder else { + return shouldBecomeFirstResponder + } + + placeholderString = "Press Shortcut" + hideCaret() + + eventMonitor = LocalEventMonitor(events: [.keyDown]) { [weak self] event in + guard let self = self else { + return nil + } + + if + event.modifiers.isEmpty, + event.specialKey == .tab + { + return event + } + + if + event.modifiers.isEmpty, + event.keyCode == kVK_Escape // TODO: Make this strongly typed. + { + self.blur() + return nil + } + + if + event.modifiers.isEmpty && + ( + event.specialKey == .delete || + event.specialKey == .deleteForward || + event.specialKey == .backspace + ) + { + self.clear() + return nil + } + + guard + ( + !event.modifiers.isEmpty || + event.specialKey?.isFunctionKey == true + ), + let shortcut = Shortcut(event: event) + else { + NSSound.beep() + return nil + } + + if let menuItem = shortcut.takenByMainMenu { + NSAlert.showModal( + for: self.window, + message: "This keyboard shortcut cannot be used as it's already used by the “\(menuItem.title)” menu item." + ) + return nil + } + + guard !shortcut.isTakenBySystem else { + NSAlert.showModal( + for: self.window, + message: "This keyboard shortcut cannot be used as it's already a system-wide keyboard shortcut.", + // TODO: Add button to offer to open the relevant system preference pane for the user. + informativeText: "Most system-wide keyboard shortcuts can be changed in “System Preferences › Keyboard › Shortcuts“." + ) + return nil + } + + self.stringValue = "\(shortcut)" + userDefaultsSet(name: self.shortcutName, shortcut: shortcut) + self.blur() + + return nil + }.start() + + return shouldBecomeFirstResponder + } + + private func blur() { + window?.makeFirstResponder(nil) + } + } +} diff --git a/Sources/KeyboardShortcuts/Shortcut.swift b/Sources/KeyboardShortcuts/Shortcut.swift new file mode 100644 index 0000000..788587a --- /dev/null +++ b/Sources/KeyboardShortcuts/Shortcut.swift @@ -0,0 +1,190 @@ +import Cocoa +import Carbon.HIToolbox + +extension KeyboardShortcuts { + /// A keyboard shortcut. + public struct Shortcut: Hashable, Codable { + /// Carbon modifiers are not always stored as the same number. + /// For example, the system has `⌃F2` stored with the modifiers number `135168`, but if you press the keyboard shortcut, you get `4096`. + private static func normalizeModifiers(_ carbonModifiers: Int) -> Int { + NSEvent.ModifierFlags(carbon: carbonModifiers).carbon + } + + public let carbonKeyCode: Int + public let carbonModifiers: Int + + public var key: Key? { Key(rawValue: carbonKeyCode) } + public var modifiers: NSEvent.ModifierFlags { NSEvent.ModifierFlags(carbon: carbonModifiers) } + + /// Create a shortcut from a key code number and modifier code. + public init(carbonKeyCode: Int, carbonModifiers: Int = 0) { + self.carbonKeyCode = carbonKeyCode + self.carbonModifiers = Self.normalizeModifiers(carbonModifiers) + } + + /// Create a keyboard shortcut from a strongly-typed key and modifiers. + public init(_ key: Key, modifiers: NSEvent.ModifierFlags = []) { + self.init( + carbonKeyCode: key.rawValue, + carbonModifiers: modifiers.carbon + ) + } + + /// Create a keyboard shortcut from a key event. + public init?(event: NSEvent) { + guard event.isKeyEvent else { + return nil + } + + self.init( + carbonKeyCode: Int(event.keyCode), + carbonModifiers: event.modifierFlags.carbon + ) + } + } +} + +extension KeyboardShortcuts.Shortcut { + /// System-defined keyboard shortcuts. + static var system: [Self] { CarbonKeyboardShortcuts.system } + + /// Check whether the keyboard shortcut is already taken by the system. + var isTakenBySystem: Bool { Self.system.contains(self) } +} + +extension KeyboardShortcuts.Shortcut { + /// Recursively finds a menu item in the given menu that has a matching key equivalent and modifier. + func menuItemWithMatchingShortcut(in menu: NSMenu) -> NSMenuItem? { + for item in menu.items { + if + keyToCharacter() == item.keyEquivalent, + modifiers == item.keyEquivalentModifierMask + { + return item + } + + if + let submenu = item.submenu, + let menuItem = menuItemWithMatchingShortcut(in: submenu) + { + return menuItem + } + } + + return nil + } + + /// Returns a menu item in the app's main menu that has a matching key equivalent and modifier. + var takenByMainMenu: NSMenuItem? { + guard let mainMenu = NSApp.mainMenu else { + return nil + } + + return menuItemWithMatchingShortcut(in: mainMenu) + } +} + +private var keyToCharacterMapping: [KeyboardShortcuts.Key: String] = [ + .carriageReturn: "↩", + .delete: "⌫", + .deleteForward: "⌦", + .downArrow: "↓", + .end: "↘", + .escape: "⎋", + .help: "?⃝", + .home: "↖", + .leftArrow: "←", + .pageDown: "⇟", + .pageUp: "⇞", + .rightArrow: "→", + .space: "Space", + .tab: "⇥", + .upArrow: "↑", + .f1: "F1", + .f2: "F2", + .f3: "F3", + .f4: "F4", + .f5: "F5", + .f6: "F6", + .f7: "F7", + .f8: "F8", + .f9: "F9", + .f10: "F10", + .f11: "F11", + .f12: "F12", + .f13: "F13", + .f14: "F14", + .f15: "F15", + .f16: "F16", + .f17: "F17", + .f18: "F18", + .f19: "F19", + .f20: "F20", + .f21: "F21", + .f22: "F22", + .f23: "F23", + .f24: "F24", + .f25: "F25", + .f26: "F26", + .f27: "F27", + .f28: "F28", + .f29: "F29", + .f30: "F30", + .f31: "F31", + .f32: "F32", + .f33: "F33", + .f34: "F34", + .f35: "F35" +] + +extension KeyboardShortcuts.Shortcut: CustomStringConvertible { + fileprivate func keyToCharacter() -> String? { + // Some characters cannot be automatically translated. + if + let key = key, + let character = keyToCharacterMapping[key] + { + return character + } + + let source = TISCopyCurrentASCIICapableKeyboardLayoutInputSource().takeUnretainedValue() + let layoutData = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) + let dataRef = unsafeBitCast(layoutData, to: CFData.self) + let keyLayout = unsafeBitCast(CFDataGetBytePtr(dataRef), to: UnsafePointer.self) + var deadKeyState: UInt32 = 0 + let maxCharacters = 256 + var length = 0 + var characters = [UniChar](repeating: 0, count: maxCharacters) + + let error = CoreServices.UCKeyTranslate( + keyLayout, + UInt16(carbonKeyCode), + UInt16(CoreServices.kUCKeyActionDisplay), + UInt32(carbonModifiers), + UInt32(LMGetKbdType()), + OptionBits(CoreServices.kUCKeyTranslateNoDeadKeysBit), + &deadKeyState, + maxCharacters, + &length, + &characters + ) + + guard error == noErr else { + return nil + } + + return String(utf16CodeUnits: characters, count: length) + } + + /** + The string representation of the keyboard shortcut. + + ``` + print(Shortcut(.a, modifiers: [.command])) + //=> "⌘A" + ``` + */ + public var description: String { + modifiers.description + (keyToCharacter()?.uppercased() ?? "�") + } +} diff --git a/Sources/KeyboardShortcuts/util.swift b/Sources/KeyboardShortcuts/util.swift new file mode 100644 index 0000000..a47918d --- /dev/null +++ b/Sources/KeyboardShortcuts/util.swift @@ -0,0 +1,276 @@ +import Cocoa +import Carbon.HIToolbox + + +extension Data { + var string: String? { String(data: self, encoding: .utf8) } +} + + +extension NSEvent { + var isKeyEvent: Bool { type == .keyDown || type == .keyUp } +} + + +extension NSTextField { + func hideCaret() { + (currentEditor() as? NSTextView)?.insertionPointColor = .clear + } +} + + +/** +Listen to local events. + +- Important: Don't foret to call `.start()`. + +``` +eventMonitor = LocalEventMonitor(events: [.leftMouseDown, .rightMouseDown]) { event in + // Do something + + return event +}.start() +``` +*/ +final class LocalEventMonitor { + private let events: NSEvent.EventTypeMask + private let callback: (NSEvent) -> NSEvent? + private weak var monitor: AnyObject? + + init(events: NSEvent.EventTypeMask, callback: @escaping (NSEvent) -> NSEvent?) { + self.events = events + self.callback = callback + } + + deinit { + stop() + } + + @discardableResult + func start() -> Self { + monitor = NSEvent.addLocalMonitorForEvents(matching: events, handler: callback) as AnyObject + return self + } + + func stop() { + guard let monitor = monitor else { + return + } + + NSEvent.removeMonitor(monitor) + } +} + + +extension NSEvent { + static var modifiers: ModifierFlags { + modifierFlags + .intersection(.deviceIndependentFlagsMask) + // We remove `capsLock` as it shouldn't affect the modifiers. + // We remove `numericPad`/`function` as arrow keys trigger it, use `event.specialKeys` instead. + .subtracting([.capsLock, .numericPad, .function]) + } + + /** + Real modifiers. + + - Note: Prefer this over `.modifierFlags`. + + ``` + // Check if Command is one of possible more modifiers keys + event.modifiers.contains(.command) + + // Check if Command is the only modifier key + event.modifiers == .command + + // Check if Command and Shift are the only modifiers + event.modifiers == [.command, .shift] + ``` + */ + var modifiers: ModifierFlags { + modifierFlags + .intersection(.deviceIndependentFlagsMask) + // We remove `capsLock` as it shouldn't affect the modifiers. + // We remove `numericPad`/`function` as arrow keys trigger it, use `event.specialKeys` instead. + .subtracting([.capsLock, .numericPad, .function]) + } +} + + +extension NSSearchField { + /// Clear the search field. + func clear() { + (cell as? NSSearchFieldCell)?.cancelButtonCell?.performClick(self) + } +} + + +extension NSAlert { + /// Show an alert as a window-modal sheet, or as an app-modal (window-independent) alert if the window is `nil` or not given. + @discardableResult + static func showModal( + for window: NSWindow? = nil, + message: String, + informativeText: String? = nil, + style: Style = .warning, + icon: NSImage? = nil + ) -> NSApplication.ModalResponse { + NSAlert( + message: message, + informativeText: informativeText, + style: style, + icon: icon + ).runModal(for: window) + } + + convenience init( + message: String, + informativeText: String? = nil, + style: Style = .warning, + icon: NSImage? = nil + ) { + self.init() + self.messageText = message + self.alertStyle = style + self.icon = icon + + if let informativeText = informativeText { + self.informativeText = informativeText + } + } + + /// Runs the alert as a window-modal sheet, or as an app-modal (window-independent) alert if the window is `nil` or not given. + @discardableResult + func runModal(for window: NSWindow? = nil) -> NSApplication.ModalResponse { + guard let window = window else { + return runModal() + } + + beginSheetModal(for: window) { returnCode in + NSApp.stopModal(withCode: returnCode) + } + + return NSApp.runModal(for: window) + } +} + + +extension NSEvent.ModifierFlags { + var carbon: Int { + var modifierFlags = 0 + + if contains(.control) { + modifierFlags |= controlKey + } + + if contains(.option) { + modifierFlags |= optionKey + } + + if contains(.shift) { + modifierFlags |= shiftKey + } + + if contains(.command) { + modifierFlags |= cmdKey + } + + return modifierFlags + } + + init(carbon: Int) { + self.init() + + if carbon & controlKey == controlKey { + insert(.control) + } + + if carbon & optionKey == optionKey { + insert(.option) + } + + if carbon & shiftKey == shiftKey { + insert(.shift) + } + + if carbon & cmdKey == cmdKey { + insert(.command) + } + } +} + +/// :nodoc: +extension NSEvent.ModifierFlags: CustomStringConvertible { + /** + The string representation of the modifier flags. + + ``` + print(NSEvent.ModifierFlags([.command, .shift])) + //=> "⇧⌘" + ``` + */ + public var description: String { + var description = "" + + if contains(.control) { + description += "⌃" + } + + if contains(.option) { + description += "⌥" + } + + if contains(.shift) { + description += "⇧" + } + + if contains(.command) { + description += "⌘" + } + + return description + } +} + + +extension NSEvent.SpecialKey { + static let functionKeys: Set = [ + .f1, + .f2, + .f3, + .f4, + .f5, + .f6, + .f7, + .f8, + .f9, + .f10, + .f11, + .f12, + .f13, + .f14, + .f15, + .f16, + .f17, + .f18, + .f19, + .f20, + .f21, + .f22, + .f23, + .f24, + .f25, + .f26, + .f27, + .f28, + .f29, + .f30, + .f31, + .f32, + .f33, + .f34, + .f35 + ] + + var isFunctionKey: Bool { Self.functionKeys.contains(self) } +} diff --git a/license b/license new file mode 100644 index 0000000..fa7ceba --- /dev/null +++ b/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..28198897cca6a232278d78f193c547b1416ef07f GIT binary patch literal 25562 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 literal 0 HcmV?d00001 diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..72a15ac --- /dev/null +++ b/readme.md @@ -0,0 +1,156 @@ +
+ KeyboardShortcuts +
+
+ +This package lets you add support for user-customizable global keyboard shortcuts to your macOS app in minutes. It's fully sandbox and Mac App Store compatible. And it's used in production by [Dato](https://sindresorhus.com/dato), [Jiffy](https://sindresorhus.com/jiffy), and [Lungo](https://sindresorhus.com/lungo). + +This package is still in its early days. I'm happy to accept more configurability and features. PR welcome! What you see here is just what I needed for my own apps. + + + +## Requirements + +macOS 10.11+ + +## Install + +#### Swift Package Manager + +```swift +.package(url: "https://github.com/sindresorhus/KeyboardShortcuts", from: "0.0.0") +``` + +You need to set the build setting “Other Linker Flags” to `-weak_framework Combine` to work around [this Xcode bug](https://github.com/feedback-assistant/reports/issues/44). + +#### Carthage + +``` +github "sindresorhus/KeyboardShortcuts" +``` + +#### CocoaPods + +```ruby +pod 'KeyboardShortcuts' +``` + +## Usage + +First, register a name for the keyboard shortcut. + +`Constants.swift` + +```swift +import KeyboardShortcuts + +extension KeyboardShortcuts.Name { + static let toggleUnicornMode = Name("toggleUnicornMode") +} +``` + +You can then refer to this strongly-typed name in other places. + +You will want to make a view where the user can choose a keyboard shortcut. + +`PreferencesView.swift` + +```swift +import SwiftUI +import KeyboardShortcuts + +struct PreferencesView: View { + var body: some View { + HStack { + Text("Toggle Unicorn Mode:") + KeyboardShortcuts.Recorder(for: .toggleUnicornMode) + } + } +} +``` + +*There's also [support for Cocoa](#cocoa) instead of SwiftUI.* + +`KeyboardShortcuts.Recorder` takes care of storing the keyboard shortcut in `UserDefaults` and also warning the user if the chosen keyboard shortcut is already used by the system or the app's main menu. + +Add a listener for when the user presses their chosen keyboard shortcut. + +`AppDelegate.swift` + +```swift +import Cocoa +import KeyboardShortcuts + +@NSApplicationMain +final class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + KeyboardShortcuts.onKeyUp(for: .toggleUnicornMode) { + // The user pressed the keyboard shortcut for “unicorn mode”! + self.isUnicornMode.toggle() + } + } +} +``` + +*You can also listen to key down with `.onKeyDown()`* + +**That's all! ✨** + +You can find a complete example by opening `KeyboardShortcuts.xcodeproj` and then running the `KeyboardShortcutsExample` target. + +#### Cocoa + +Use [`KeyboardShortcuts.RecorderCocoa`](Sources/KeyboardShortcuts/RecorderCocoa.swift) instead of `KeyboardShortcuts.Recorder`. + +```swift +import Cocoa +import KeyboardShortcuts + +final class PreferencesViewController: NSViewController { + override func loadView() { + view = NSView() + + let recorder = KeyboardShortcuts.RecorderCocoa(for: .toggleUnicornMode) + view.addSubview(recorder) + } +} +``` + +## API + +[See the API docs.](https://sindresorhus.com/KeyboardShortcuts) + +## FAQ + +#### How is it different from [`MASShortcut`](https://github.com/shpakovski/MASShortcut)? + +This package: +- Written in Swift with a swifty API. +- More native-looking UI component. +- SwiftUI component included. +- Support for listening to key down, not just key up. +- Swift Package Manager support. + +`MASShortcut`: +- More mature. +- More features. +- Localized. + +#### How is it different from [`HotKey`](https://github.com/soffes/HotKey)? + +`HotKey` is good for adding hard-coded keyboard shortcuts, but it doesn't provide any UI component for the user to choose their own keyboard shortcuts. + +#### Why is this package importing `Carbon`? Isn't that deprecated? + +Most of the Carbon APIs were deprecated years ago, but there are some left that Apple never shipped modern replacements for. This includes registering global keyboard shortcuts. However, you should not need to worry about this. Apple will for sure ship new APIs before deprecating the Carbon APIs used here. + +#### Does this package cause any permission dialogs? + +No. + +## Related + +- [Preferences](https://github.com/sindresorhus/Preferences) - Add a preferences window to your macOS app in minutes +- [Defaults](https://github.com/sindresorhus/Defaults) - Swifty and modern UserDefaults +- [LaunchAtLogin](https://github.com/sindresorhus/LaunchAtLogin) - Add "Launch at Login" functionality to your macOS app +- [More…](https://github.com/search?q=user%3Asindresorhus+language%3Aswift) diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..ce59dd75d2af1a232f32ec60a40a9ce4467a5563 GIT binary patch literal 80672 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 XyEfa-q#)_<|KGB4`-Z%A`{Vu}kIXcU literal 0 HcmV?d00001