├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── pod-lint.yml │ └── spm.yml ├── .gitignore ├── .spi.yml ├── .swiftlint.yml ├── CHANGELOG.md ├── Example ├── ExampleApp.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ ├── IDETemplateMacros.plist │ │ └── xcschemes │ │ └── ExampleApp.xcscheme ├── Sources │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── AppIcon-1024px.png │ │ │ ├── AppIcon-128px-128pt@1x.png │ │ │ ├── AppIcon-16px-16pt@1x.png │ │ │ ├── AppIcon-256px-128pt@2x.png │ │ │ ├── AppIcon-256px-256pt@1x.png │ │ │ ├── AppIcon-32px-16pt@2x.png │ │ │ ├── AppIcon-32px-32pt@1x.png │ │ │ ├── AppIcon-512px-256pt@2x.png │ │ │ ├── AppIcon-512px.png │ │ │ ├── AppIcon-64px-32pt@2x.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── StatusIcon.imageset │ │ │ ├── Contents.json │ │ │ ├── hb-status-icon.png │ │ │ └── hb-status-icon@2x.png │ ├── Base.lproj │ │ └── Main.storyboard │ ├── ExampleApp.entitlements │ ├── ExampleAppStatusController.swift │ ├── Info.plist │ └── ViewController.swift ├── Tests │ └── ExampleAppTests.swift └── UITests │ └── ExampleAppUITests.swift ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── NSApplication+Extensions.swift ├── NSEvent+Extensions.swift ├── NSMenuItem+Extensions.swift └── StatusItemController.swift ├── StatusItemController.podspec ├── StatusItemController.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ ├── IDETemplateMacros.plist │ └── xcschemes │ └── StatusItemController.xcscheme ├── Tests └── StatusItemControllerTests.swift ├── docs ├── Classes.html ├── Classes │ └── StatusItemController.html ├── Extensions.html ├── Extensions │ ├── NSApplication.html │ ├── NSEvent.html │ └── NSMenuItem.html ├── badge.svg ├── css │ ├── highlight.css │ └── jazzy.css ├── img │ ├── carat.png │ ├── dash.png │ ├── gh.png │ └── spinner.gif ├── index.html ├── js │ ├── jazzy.js │ ├── jazzy.search.js │ ├── jquery.min.js │ ├── lunr.min.js │ └── typeahead.jquery.js ├── search.json └── undocumented.json └── scripts ├── build_docs.zsh └── lint.zsh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Documentation for all configuration options: 2 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "bundler" 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | labels: [] 11 | 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "weekly" 16 | labels: [] 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Virtual Environments 2 | # https://github.com/actions/virtual-environments/ 3 | 4 | name: CI 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | branches: 12 | - main 13 | 14 | env: 15 | PROJECT: StatusItemController.xcodeproj 16 | SCHEME: StatusItemController 17 | 18 | EXAMPLE_PROJECT: Example/ExampleApp.xcodeproj 19 | EXAMPLE_SCHEME: ExampleApp 20 | 21 | DEVELOPER_DIR: /Applications/Xcode_15.1.app/Contents/Developer 22 | MACOS_DEST: "platform=macOS,arch=x86_64" 23 | 24 | jobs: 25 | env-details: 26 | name: Environment details 27 | runs-on: macOS-13 28 | steps: 29 | - name: xcode version 30 | run: xcodebuild -version -sdk 31 | 32 | test-macOS: 33 | name: Tests 34 | runs-on: macOS-13 35 | steps: 36 | - name: git checkout 37 | uses: actions/checkout@v4 38 | 39 | - name: unit tests 40 | run: | 41 | set -o pipefail 42 | xcodebuild clean test \ 43 | -project "$PROJECT" \ 44 | -scheme "$SCHEME" \ 45 | -destination "$MACOS_DEST" \ 46 | CODE_SIGN_IDENTITY="-" | xcpretty -c 47 | 48 | - name: ui tests 49 | run: | 50 | set -o pipefail 51 | xcodebuild clean test \ 52 | -project "$EXAMPLE_PROJECT" \ 53 | -scheme "$EXAMPLE_SCHEME" \ 54 | -destination "$MACOS_DEST" \ 55 | CODE_SIGN_IDENTITY="-" | xcpretty -c 56 | -------------------------------------------------------------------------------- /.github/workflows/pod-lint.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Virtual Environments 2 | # https://github.com/actions/virtual-environments/ 3 | 4 | name: Pod Lint 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | branches: 12 | - main 13 | 14 | env: 15 | DEVELOPER_DIR: /Applications/Xcode_15.1.app/Contents/Developer 16 | 17 | jobs: 18 | main: 19 | name: Pod Lint 20 | runs-on: macOS-13 21 | steps: 22 | - name: git checkout 23 | uses: actions/checkout@v4 24 | 25 | - name: ruby versions 26 | run: | 27 | ruby --version 28 | gem --version 29 | bundler --version 30 | 31 | - name: pod lint 32 | run: pod lib lint --verbose --allow-warnings 33 | -------------------------------------------------------------------------------- /.github/workflows/spm.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Virtual Environments 2 | # https://github.com/actions/virtual-environments/ 3 | 4 | name: SwiftPM Integration 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | branches: 12 | - main 13 | 14 | env: 15 | DEVELOPER_DIR: /Applications/Xcode_15.1.app/Contents/Developer 16 | 17 | jobs: 18 | main: 19 | name: SwiftPM Build 20 | runs-on: macOS-13 21 | steps: 22 | - name: git checkout 23 | uses: actions/checkout@v4 24 | 25 | - name: xcode version 26 | run: xcodebuild -version -sdk 27 | 28 | - name: swift build 29 | run: swift build 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | 4 | # ruby tools 5 | .bundle/ 6 | vendor/ 7 | 8 | # docs 9 | docs/docsets/ 10 | 11 | # Xcode 12 | xcuserdata/ 13 | build/ 14 | 15 | ## Obj-C/Swift specific 16 | *.hmap 17 | 18 | ## App packaging 19 | *.ipa 20 | *.dSYM.zip 21 | *.dSYM 22 | 23 | ## Playgrounds 24 | timeline.xctimeline 25 | playground.xcworkspace 26 | 27 | # Swift Package Manager 28 | .swiftpm 29 | .build/ 30 | 31 | # fastlane 32 | # 33 | # It is recommended to not store the screenshots in the git repo. 34 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 35 | # For more information about the recommended setup visit: 36 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 37 | fastlane/report.xml 38 | fastlane/Preview.html 39 | fastlane/screenshots/**/*.png 40 | fastlane/test_output 41 | -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | # https://swiftpackageindex.com/SwiftPackageIndex/SPIManifest/1.1.1/documentation/spimanifest 2 | version: 1 3 | builder: 4 | configs: 5 | - documentation_targets: [StatusItemController] 6 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | # SwiftLint configuration 2 | # Rule directory: https://realm.github.io/SwiftLint/rule-directory.html 3 | # GitHub: https://github.com/realm/SwiftLint 4 | 5 | excluded: 6 | - Pods 7 | - docs 8 | - build 9 | - scripts 10 | - swiftpm 11 | - .bundle 12 | - vendor 13 | 14 | opt_in_rules: 15 | # performance 16 | - empty_count 17 | - first_where 18 | - sorted_first_last 19 | - contains_over_first_not_nil 20 | - last_where 21 | - reduce_into 22 | - contains_over_filter_count 23 | - contains_over_filter_is_empty 24 | - empty_collection_literal 25 | - type_contents_order 26 | 27 | # idiomatic 28 | - fatal_error_message 29 | - xctfail_message 30 | - discouraged_object_literal 31 | - discouraged_optional_boolean 32 | - discouraged_optional_collection 33 | - for_where 34 | - function_default_parameter_at_end 35 | - legacy_random 36 | - no_extension_access_modifier 37 | - redundant_type_annotation 38 | - static_operator 39 | - toggle_bool 40 | - unavailable_function 41 | - no_space_in_method_call 42 | - discouraged_assert 43 | - legacy_objc_type 44 | - file_name 45 | - file_name_no_space 46 | - discouraged_none_name 47 | - return_value_from_void_function 48 | - prefer_zero_over_explicit_init 49 | - shorthand_optional_binding 50 | - xct_specific_matcher 51 | - unneeded_synthesized_initializer 52 | 53 | # style 54 | - attributes 55 | - number_separator 56 | - operator_usage_whitespace 57 | - sorted_imports 58 | - vertical_parameter_alignment_on_call 59 | - void_return 60 | - closure_spacing 61 | - empty_enum_arguments 62 | - implicit_return 63 | - modifier_order 64 | - multiline_arguments 65 | - multiline_parameters 66 | - trailing_closure 67 | - unneeded_parentheses_in_closure_argument 68 | - vertical_whitespace_between_cases 69 | - prefer_self_in_static_references 70 | - comma_inheritance 71 | - direct_return 72 | - period_spacing 73 | - superfluous_else 74 | - sorted_enum_cases 75 | - non_overridable_class_declaration 76 | 77 | # lint 78 | - overridden_super_call 79 | - override_in_extension 80 | - yoda_condition 81 | - array_init 82 | - empty_xctest_method 83 | - identical_operands 84 | - prohibited_super_call 85 | - duplicate_enum_cases 86 | - legacy_multiple 87 | - accessibility_label_for_image 88 | - lower_acl_than_parent 89 | - unhandled_throwing_task 90 | - private_swiftui_state 91 | 92 | # Rules run by `swiftlint analyze` (experimental) 93 | analyzer_rules: 94 | # - unused_import 95 | - unused_declaration 96 | - explicit_self 97 | - capture_variable 98 | 99 | line_length: 200 100 | file_length: 600 101 | 102 | type_body_length: 500 103 | function_body_length: 250 104 | 105 | cyclomatic_complexity: 10 106 | 107 | nesting: 108 | type_level: 2 109 | function_level: 2 110 | check_nesting_in_closures_and_statements: true 111 | always_allow_one_type_in_functions: false 112 | 113 | identifier_name: 114 | allowed_symbols: ['_'] 115 | 116 | type_contents_order: 117 | order: 118 | - associated_type 119 | - type_alias 120 | - case 121 | - subtype 122 | - type_property 123 | - ib_outlet 124 | - ib_inspectable 125 | - instance_property 126 | - initializer 127 | - deinitializer 128 | - subscript 129 | - type_method 130 | - view_life_cycle_method 131 | - ib_action 132 | - other_method 133 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | The changelog for `StatusItemController`. Also see the [releases](https://github.com/hexedbits/StatusItemController/releases) on GitHub. 4 | 5 | 2.0.1 6 | ----- 7 | 8 | ### Changes 9 | 10 | - `StatusItemController` is now marked as `@MainActor`. 11 | 12 | 13 | 2.0.0 14 | ----- 15 | 16 | ### Changes 17 | 18 | - Now requires minimum macOS 11.0 19 | - By default, `StatusItemController` now sets the status item `tooltip` to the app name (based on the main bundle). 20 | - `NSMenuItem` convenience init now accepts an optional image. It is `nil` by default. 21 | 22 | 23 | 1.2.0 24 | ----- 25 | 26 | This release closes the [1.2.0 milestone](https://github.com/hexedbits/StatusItemController/milestone/2?closed=1). 27 | 28 | ### Changes 29 | - Upgraded to Swift 5.5 and Xcode 13 (#27) 30 | - Various project infra updates (#27) 31 | - Added an example app project (#4) 32 | 33 | ### Fixes 34 | 35 | - Resolved unit testing issue (#15) 36 | 37 | 38 | 1.1.0 39 | ----- 40 | 41 | This release closes the [1.1.0 milestone](https://github.com/hexedbits/StatusItemController/milestone/1?closed=1). 42 | 43 | ### Changes 44 | 45 | - Upgraded to Swift 5.3 and Xcode 12 46 | 47 | - Changed the `target` parameter in the `NSMenuItem` convenience initializer to be optional, with a default value of `nil`. 48 | 49 | ``` 50 | convenience init(title: String, 51 | target: AnyObject? = nil, 52 | action: Selector? = nil, 53 | keyEquivalent: String = "", 54 | isEnabled: Bool = true) 55 | ``` 56 | 57 | 58 | 1.0.0 59 | ----- 60 | 61 | Initial release. [Documentation here](https://hexedbits.github.io/StatusItemController/). 62 | -------------------------------------------------------------------------------- /Example/ExampleApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0B3D9E092745957300358640 /* StatusItemController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B3D9E062745956C00358640 /* StatusItemController.framework */; }; 11 | 0B3D9E0E2745968B00358640 /* ExampleAppStatusController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B3D9E0D2745968B00358640 /* ExampleAppStatusController.swift */; }; 12 | 883CDE9E23FB426300A06CD3 /* ExampleAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 883CDE9D23FB426300A06CD3 /* ExampleAppTests.swift */; }; 13 | 8843D49523DD24B700CC7517 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8843D49423DD24B700CC7517 /* AppDelegate.swift */; }; 14 | 8843D49723DD24B700CC7517 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8843D49623DD24B700CC7517 /* ViewController.swift */; }; 15 | 8843D49923DD24B800CC7517 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8843D49823DD24B800CC7517 /* Assets.xcassets */; }; 16 | 8843D49C23DD24B800CC7517 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8843D49A23DD24B800CC7517 /* Main.storyboard */; }; 17 | 8843D4A823DD24B800CC7517 /* ExampleAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8843D4A723DD24B800CC7517 /* ExampleAppUITests.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 0B3D9E052745956C00358640 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 0B3D9E002745956C00358640 /* StatusItemController.xcodeproj */; 24 | proxyType = 2; 25 | remoteGlobalIDString = 886C101624393069008D8DEA; 26 | remoteInfo = StatusItemController; 27 | }; 28 | 0B3D9E072745956C00358640 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 0B3D9E002745956C00358640 /* StatusItemController.xcodeproj */; 31 | proxyType = 2; 32 | remoteGlobalIDString = 886C101F24393069008D8DEA; 33 | remoteInfo = StatusItemControllerTests; 34 | }; 35 | 0B3D9E0F27459F0800358640 /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 0B3D9E002745956C00358640 /* StatusItemController.xcodeproj */; 38 | proxyType = 1; 39 | remoteGlobalIDString = 886C101524393069008D8DEA; 40 | remoteInfo = StatusItemController; 41 | }; 42 | 883CDEA023FB426300A06CD3 /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = 8843D48923DD24B700CC7517 /* Project object */; 45 | proxyType = 1; 46 | remoteGlobalIDString = 8843D49023DD24B700CC7517; 47 | remoteInfo = ExampleApp; 48 | }; 49 | 8843D4A423DD24B800CC7517 /* PBXContainerItemProxy */ = { 50 | isa = PBXContainerItemProxy; 51 | containerPortal = 8843D48923DD24B700CC7517 /* Project object */; 52 | proxyType = 1; 53 | remoteGlobalIDString = 8843D49023DD24B700CC7517; 54 | remoteInfo = ExampleApp; 55 | }; 56 | /* End PBXContainerItemProxy section */ 57 | 58 | /* Begin PBXFileReference section */ 59 | 0B3D9E002745956C00358640 /* StatusItemController.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = StatusItemController.xcodeproj; path = ../StatusItemController.xcodeproj; sourceTree = ""; }; 60 | 0B3D9E0D2745968B00358640 /* ExampleAppStatusController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleAppStatusController.swift; sourceTree = ""; }; 61 | 883CDE9B23FB426300A06CD3 /* ExampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 883CDE9D23FB426300A06CD3 /* ExampleAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleAppTests.swift; sourceTree = ""; }; 63 | 8843D49123DD24B700CC7517 /* ExampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 8843D49423DD24B700CC7517 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 65 | 8843D49623DD24B700CC7517 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 66 | 8843D49823DD24B800CC7517 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 67 | 8843D49B23DD24B800CC7517 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 68 | 8843D49D23DD24B800CC7517 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | 8843D49E23DD24B800CC7517 /* ExampleApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ExampleApp.entitlements; sourceTree = ""; }; 70 | 8843D4A323DD24B800CC7517 /* ExampleAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | 8843D4A723DD24B800CC7517 /* ExampleAppUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleAppUITests.swift; sourceTree = ""; }; 72 | /* End PBXFileReference section */ 73 | 74 | /* Begin PBXFrameworksBuildPhase section */ 75 | 883CDE9823FB426300A06CD3 /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | 8843D48E23DD24B700CC7517 /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | 0B3D9E092745957300358640 /* StatusItemController.framework in Frameworks */, 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | 8843D4A023DD24B800CC7517 /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | /* End PBXFrameworksBuildPhase section */ 98 | 99 | /* Begin PBXGroup section */ 100 | 0B3D9E012745956C00358640 /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 0B3D9E062745956C00358640 /* StatusItemController.framework */, 104 | 0B3D9E082745956C00358640 /* StatusItemControllerTests.xctest */, 105 | ); 106 | name = Products; 107 | sourceTree = ""; 108 | }; 109 | 883CDE9C23FB426300A06CD3 /* Tests */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 883CDE9D23FB426300A06CD3 /* ExampleAppTests.swift */, 113 | ); 114 | path = Tests; 115 | sourceTree = ""; 116 | }; 117 | 8843D48823DD24B700CC7517 = { 118 | isa = PBXGroup; 119 | children = ( 120 | 0B3D9E002745956C00358640 /* StatusItemController.xcodeproj */, 121 | 8843D49323DD24B700CC7517 /* Sources */, 122 | 883CDE9C23FB426300A06CD3 /* Tests */, 123 | 8843D4A623DD24B800CC7517 /* UITests */, 124 | 8843D49223DD24B700CC7517 /* Products */, 125 | 8843D4BB23DD257C00CC7517 /* Frameworks */, 126 | ); 127 | sourceTree = ""; 128 | }; 129 | 8843D49223DD24B700CC7517 /* Products */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 8843D49123DD24B700CC7517 /* ExampleApp.app */, 133 | 8843D4A323DD24B800CC7517 /* ExampleAppUITests.xctest */, 134 | 883CDE9B23FB426300A06CD3 /* ExampleAppTests.xctest */, 135 | ); 136 | name = Products; 137 | sourceTree = ""; 138 | }; 139 | 8843D49323DD24B700CC7517 /* Sources */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 8843D49423DD24B700CC7517 /* AppDelegate.swift */, 143 | 8843D49823DD24B800CC7517 /* Assets.xcassets */, 144 | 8843D49E23DD24B800CC7517 /* ExampleApp.entitlements */, 145 | 0B3D9E0D2745968B00358640 /* ExampleAppStatusController.swift */, 146 | 8843D49D23DD24B800CC7517 /* Info.plist */, 147 | 8843D49A23DD24B800CC7517 /* Main.storyboard */, 148 | 8843D49623DD24B700CC7517 /* ViewController.swift */, 149 | ); 150 | path = Sources; 151 | sourceTree = ""; 152 | }; 153 | 8843D4A623DD24B800CC7517 /* UITests */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 8843D4A723DD24B800CC7517 /* ExampleAppUITests.swift */, 157 | ); 158 | path = UITests; 159 | sourceTree = ""; 160 | }; 161 | 8843D4BB23DD257C00CC7517 /* Frameworks */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | ); 165 | name = Frameworks; 166 | sourceTree = ""; 167 | }; 168 | /* End PBXGroup section */ 169 | 170 | /* Begin PBXNativeTarget section */ 171 | 883CDE9A23FB426300A06CD3 /* ExampleAppTests */ = { 172 | isa = PBXNativeTarget; 173 | buildConfigurationList = 883CDEA623FB426300A06CD3 /* Build configuration list for PBXNativeTarget "ExampleAppTests" */; 174 | buildPhases = ( 175 | 883CDE9723FB426300A06CD3 /* Sources */, 176 | 883CDE9823FB426300A06CD3 /* Frameworks */, 177 | 883CDE9923FB426300A06CD3 /* Resources */, 178 | ); 179 | buildRules = ( 180 | ); 181 | dependencies = ( 182 | 883CDEA123FB426300A06CD3 /* PBXTargetDependency */, 183 | ); 184 | name = ExampleAppTests; 185 | productName = ExampleAppTests; 186 | productReference = 883CDE9B23FB426300A06CD3 /* ExampleAppTests.xctest */; 187 | productType = "com.apple.product-type.bundle.unit-test"; 188 | }; 189 | 8843D49023DD24B700CC7517 /* ExampleApp */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = 8843D4AC23DD24B800CC7517 /* Build configuration list for PBXNativeTarget "ExampleApp" */; 192 | buildPhases = ( 193 | 8843D48D23DD24B700CC7517 /* Sources */, 194 | 8843D48E23DD24B700CC7517 /* Frameworks */, 195 | 8843D48F23DD24B700CC7517 /* Resources */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | 0B3D9E1027459F0800358640 /* PBXTargetDependency */, 201 | ); 202 | name = ExampleApp; 203 | productName = ExampleApp; 204 | productReference = 8843D49123DD24B700CC7517 /* ExampleApp.app */; 205 | productType = "com.apple.product-type.application"; 206 | }; 207 | 8843D4A223DD24B800CC7517 /* ExampleAppUITests */ = { 208 | isa = PBXNativeTarget; 209 | buildConfigurationList = 8843D4AF23DD24B800CC7517 /* Build configuration list for PBXNativeTarget "ExampleAppUITests" */; 210 | buildPhases = ( 211 | 8843D49F23DD24B800CC7517 /* Sources */, 212 | 8843D4A023DD24B800CC7517 /* Frameworks */, 213 | 8843D4A123DD24B800CC7517 /* Resources */, 214 | ); 215 | buildRules = ( 216 | ); 217 | dependencies = ( 218 | 8843D4A523DD24B800CC7517 /* PBXTargetDependency */, 219 | ); 220 | name = ExampleAppUITests; 221 | productName = ExampleAppUITests; 222 | productReference = 8843D4A323DD24B800CC7517 /* ExampleAppUITests.xctest */; 223 | productType = "com.apple.product-type.bundle.ui-testing"; 224 | }; 225 | /* End PBXNativeTarget section */ 226 | 227 | /* Begin PBXProject section */ 228 | 8843D48923DD24B700CC7517 /* Project object */ = { 229 | isa = PBXProject; 230 | attributes = { 231 | BuildIndependentTargetsInParallel = YES; 232 | LastSwiftUpdateCheck = 1130; 233 | LastUpgradeCheck = 1520; 234 | ORGANIZATIONNAME = "Hexed Bits"; 235 | TargetAttributes = { 236 | 883CDE9A23FB426300A06CD3 = { 237 | CreatedOnToolsVersion = 11.3.1; 238 | TestTargetID = 8843D49023DD24B700CC7517; 239 | }; 240 | 8843D49023DD24B700CC7517 = { 241 | CreatedOnToolsVersion = 11.3.1; 242 | }; 243 | 8843D4A223DD24B800CC7517 = { 244 | CreatedOnToolsVersion = 11.3.1; 245 | TestTargetID = 8843D49023DD24B700CC7517; 246 | }; 247 | }; 248 | }; 249 | buildConfigurationList = 8843D48C23DD24B700CC7517 /* Build configuration list for PBXProject "ExampleApp" */; 250 | compatibilityVersion = "Xcode 9.3"; 251 | developmentRegion = en; 252 | hasScannedForEncodings = 0; 253 | knownRegions = ( 254 | en, 255 | Base, 256 | ); 257 | mainGroup = 8843D48823DD24B700CC7517; 258 | productRefGroup = 8843D49223DD24B700CC7517 /* Products */; 259 | projectDirPath = ""; 260 | projectReferences = ( 261 | { 262 | ProductGroup = 0B3D9E012745956C00358640 /* Products */; 263 | ProjectRef = 0B3D9E002745956C00358640 /* StatusItemController.xcodeproj */; 264 | }, 265 | ); 266 | projectRoot = ""; 267 | targets = ( 268 | 8843D49023DD24B700CC7517 /* ExampleApp */, 269 | 883CDE9A23FB426300A06CD3 /* ExampleAppTests */, 270 | 8843D4A223DD24B800CC7517 /* ExampleAppUITests */, 271 | ); 272 | }; 273 | /* End PBXProject section */ 274 | 275 | /* Begin PBXReferenceProxy section */ 276 | 0B3D9E062745956C00358640 /* StatusItemController.framework */ = { 277 | isa = PBXReferenceProxy; 278 | fileType = wrapper.framework; 279 | path = StatusItemController.framework; 280 | remoteRef = 0B3D9E052745956C00358640 /* PBXContainerItemProxy */; 281 | sourceTree = BUILT_PRODUCTS_DIR; 282 | }; 283 | 0B3D9E082745956C00358640 /* StatusItemControllerTests.xctest */ = { 284 | isa = PBXReferenceProxy; 285 | fileType = wrapper.cfbundle; 286 | path = StatusItemControllerTests.xctest; 287 | remoteRef = 0B3D9E072745956C00358640 /* PBXContainerItemProxy */; 288 | sourceTree = BUILT_PRODUCTS_DIR; 289 | }; 290 | /* End PBXReferenceProxy section */ 291 | 292 | /* Begin PBXResourcesBuildPhase section */ 293 | 883CDE9923FB426300A06CD3 /* Resources */ = { 294 | isa = PBXResourcesBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | 8843D48F23DD24B700CC7517 /* Resources */ = { 301 | isa = PBXResourcesBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | 8843D49923DD24B800CC7517 /* Assets.xcassets in Resources */, 305 | 8843D49C23DD24B800CC7517 /* Main.storyboard in Resources */, 306 | ); 307 | runOnlyForDeploymentPostprocessing = 0; 308 | }; 309 | 8843D4A123DD24B800CC7517 /* Resources */ = { 310 | isa = PBXResourcesBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | }; 316 | /* End PBXResourcesBuildPhase section */ 317 | 318 | /* Begin PBXSourcesBuildPhase section */ 319 | 883CDE9723FB426300A06CD3 /* Sources */ = { 320 | isa = PBXSourcesBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | 883CDE9E23FB426300A06CD3 /* ExampleAppTests.swift in Sources */, 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | }; 327 | 8843D48D23DD24B700CC7517 /* Sources */ = { 328 | isa = PBXSourcesBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | 8843D49723DD24B700CC7517 /* ViewController.swift in Sources */, 332 | 8843D49523DD24B700CC7517 /* AppDelegate.swift in Sources */, 333 | 0B3D9E0E2745968B00358640 /* ExampleAppStatusController.swift in Sources */, 334 | ); 335 | runOnlyForDeploymentPostprocessing = 0; 336 | }; 337 | 8843D49F23DD24B800CC7517 /* Sources */ = { 338 | isa = PBXSourcesBuildPhase; 339 | buildActionMask = 2147483647; 340 | files = ( 341 | 8843D4A823DD24B800CC7517 /* ExampleAppUITests.swift in Sources */, 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | /* End PBXSourcesBuildPhase section */ 346 | 347 | /* Begin PBXTargetDependency section */ 348 | 0B3D9E1027459F0800358640 /* PBXTargetDependency */ = { 349 | isa = PBXTargetDependency; 350 | name = StatusItemController; 351 | targetProxy = 0B3D9E0F27459F0800358640 /* PBXContainerItemProxy */; 352 | }; 353 | 883CDEA123FB426300A06CD3 /* PBXTargetDependency */ = { 354 | isa = PBXTargetDependency; 355 | target = 8843D49023DD24B700CC7517 /* ExampleApp */; 356 | targetProxy = 883CDEA023FB426300A06CD3 /* PBXContainerItemProxy */; 357 | }; 358 | 8843D4A523DD24B800CC7517 /* PBXTargetDependency */ = { 359 | isa = PBXTargetDependency; 360 | target = 8843D49023DD24B700CC7517 /* ExampleApp */; 361 | targetProxy = 8843D4A423DD24B800CC7517 /* PBXContainerItemProxy */; 362 | }; 363 | /* End PBXTargetDependency section */ 364 | 365 | /* Begin PBXVariantGroup section */ 366 | 8843D49A23DD24B800CC7517 /* Main.storyboard */ = { 367 | isa = PBXVariantGroup; 368 | children = ( 369 | 8843D49B23DD24B800CC7517 /* Base */, 370 | ); 371 | name = Main.storyboard; 372 | sourceTree = ""; 373 | }; 374 | /* End PBXVariantGroup section */ 375 | 376 | /* Begin XCBuildConfiguration section */ 377 | 883CDEA223FB426300A06CD3 /* Debug */ = { 378 | isa = XCBuildConfiguration; 379 | buildSettings = { 380 | BUNDLE_LOADER = "$(TEST_HOST)"; 381 | COMBINE_HIDPI_IMAGES = YES; 382 | DEAD_CODE_STRIPPING = YES; 383 | GENERATE_INFOPLIST_FILE = YES; 384 | INFOPLIST_FILE = ""; 385 | LD_RUNPATH_SEARCH_PATHS = ( 386 | "$(inherited)", 387 | "@executable_path/../Frameworks", 388 | "@loader_path/../Frameworks", 389 | ); 390 | PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.ExampleAppTests; 391 | PRODUCT_NAME = "$(TARGET_NAME)"; 392 | SWIFT_VERSION = 5.0; 393 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/Contents/MacOS/ExampleApp"; 394 | }; 395 | name = Debug; 396 | }; 397 | 883CDEA323FB426300A06CD3 /* Release */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | BUNDLE_LOADER = "$(TEST_HOST)"; 401 | COMBINE_HIDPI_IMAGES = YES; 402 | DEAD_CODE_STRIPPING = YES; 403 | GENERATE_INFOPLIST_FILE = YES; 404 | INFOPLIST_FILE = ""; 405 | LD_RUNPATH_SEARCH_PATHS = ( 406 | "$(inherited)", 407 | "@executable_path/../Frameworks", 408 | "@loader_path/../Frameworks", 409 | ); 410 | PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.ExampleAppTests; 411 | PRODUCT_NAME = "$(TARGET_NAME)"; 412 | SWIFT_VERSION = 5.0; 413 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/Contents/MacOS/ExampleApp"; 414 | }; 415 | name = Release; 416 | }; 417 | 8843D4AA23DD24B800CC7517 /* Debug */ = { 418 | isa = XCBuildConfiguration; 419 | buildSettings = { 420 | ALWAYS_SEARCH_USER_PATHS = NO; 421 | CLANG_ANALYZER_NONNULL = YES; 422 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 423 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 424 | CLANG_CXX_LIBRARY = "libc++"; 425 | CLANG_ENABLE_MODULES = YES; 426 | CLANG_ENABLE_OBJC_ARC = YES; 427 | CLANG_ENABLE_OBJC_WEAK = YES; 428 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 429 | CLANG_WARN_BOOL_CONVERSION = YES; 430 | CLANG_WARN_COMMA = YES; 431 | CLANG_WARN_CONSTANT_CONVERSION = YES; 432 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 433 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 434 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 435 | CLANG_WARN_EMPTY_BODY = YES; 436 | CLANG_WARN_ENUM_CONVERSION = YES; 437 | CLANG_WARN_INFINITE_RECURSION = YES; 438 | CLANG_WARN_INT_CONVERSION = YES; 439 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 440 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 441 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 442 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 443 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 444 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 445 | CLANG_WARN_STRICT_PROTOTYPES = YES; 446 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 447 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 448 | CLANG_WARN_UNREACHABLE_CODE = YES; 449 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 450 | COPY_PHASE_STRIP = NO; 451 | DEAD_CODE_STRIPPING = YES; 452 | DEBUG_INFORMATION_FORMAT = dwarf; 453 | ENABLE_STRICT_OBJC_MSGSEND = YES; 454 | ENABLE_TESTABILITY = YES; 455 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 456 | GCC_C_LANGUAGE_STANDARD = gnu11; 457 | GCC_DYNAMIC_NO_PIC = NO; 458 | GCC_NO_COMMON_BLOCKS = YES; 459 | GCC_OPTIMIZATION_LEVEL = 0; 460 | GCC_PREPROCESSOR_DEFINITIONS = ( 461 | "DEBUG=1", 462 | "$(inherited)", 463 | ); 464 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 465 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 466 | GCC_WARN_UNDECLARED_SELECTOR = YES; 467 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 468 | GCC_WARN_UNUSED_FUNCTION = YES; 469 | GCC_WARN_UNUSED_VARIABLE = YES; 470 | MACOSX_DEPLOYMENT_TARGET = 11.0; 471 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 472 | MTL_FAST_MATH = YES; 473 | ONLY_ACTIVE_ARCH = YES; 474 | SDKROOT = macosx; 475 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 476 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 477 | SWIFT_STRICT_CONCURRENCY = complete; 478 | SWIFT_VERSION = 5.0; 479 | }; 480 | name = Debug; 481 | }; 482 | 8843D4AB23DD24B800CC7517 /* Release */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | ALWAYS_SEARCH_USER_PATHS = NO; 486 | CLANG_ANALYZER_NONNULL = YES; 487 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 488 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 489 | CLANG_CXX_LIBRARY = "libc++"; 490 | CLANG_ENABLE_MODULES = YES; 491 | CLANG_ENABLE_OBJC_ARC = YES; 492 | CLANG_ENABLE_OBJC_WEAK = YES; 493 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 494 | CLANG_WARN_BOOL_CONVERSION = YES; 495 | CLANG_WARN_COMMA = YES; 496 | CLANG_WARN_CONSTANT_CONVERSION = YES; 497 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 498 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 499 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 500 | CLANG_WARN_EMPTY_BODY = YES; 501 | CLANG_WARN_ENUM_CONVERSION = YES; 502 | CLANG_WARN_INFINITE_RECURSION = YES; 503 | CLANG_WARN_INT_CONVERSION = YES; 504 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 505 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 506 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 507 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 508 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 509 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 510 | CLANG_WARN_STRICT_PROTOTYPES = YES; 511 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 512 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 513 | CLANG_WARN_UNREACHABLE_CODE = YES; 514 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 515 | COPY_PHASE_STRIP = NO; 516 | DEAD_CODE_STRIPPING = YES; 517 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 518 | ENABLE_NS_ASSERTIONS = NO; 519 | ENABLE_STRICT_OBJC_MSGSEND = YES; 520 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 521 | GCC_C_LANGUAGE_STANDARD = gnu11; 522 | GCC_NO_COMMON_BLOCKS = YES; 523 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 524 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 525 | GCC_WARN_UNDECLARED_SELECTOR = YES; 526 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 527 | GCC_WARN_UNUSED_FUNCTION = YES; 528 | GCC_WARN_UNUSED_VARIABLE = YES; 529 | MACOSX_DEPLOYMENT_TARGET = 11.0; 530 | MTL_ENABLE_DEBUG_INFO = NO; 531 | MTL_FAST_MATH = YES; 532 | SDKROOT = macosx; 533 | SWIFT_COMPILATION_MODE = wholemodule; 534 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 535 | SWIFT_STRICT_CONCURRENCY = complete; 536 | SWIFT_VERSION = 5.0; 537 | }; 538 | name = Release; 539 | }; 540 | 8843D4AD23DD24B800CC7517 /* Debug */ = { 541 | isa = XCBuildConfiguration; 542 | buildSettings = { 543 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 544 | CODE_SIGN_ENTITLEMENTS = Sources/ExampleApp.entitlements; 545 | CODE_SIGN_IDENTITY = "-"; 546 | COMBINE_HIDPI_IMAGES = YES; 547 | CURRENT_PROJECT_VERSION = 666; 548 | DEAD_CODE_STRIPPING = YES; 549 | INFOPLIST_FILE = Sources/Info.plist; 550 | LD_RUNPATH_SEARCH_PATHS = ( 551 | "$(inherited)", 552 | "@executable_path/../Frameworks", 553 | ); 554 | MARKETING_VERSION = 1.2.3; 555 | PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.ExampleApp; 556 | PRODUCT_NAME = "$(TARGET_NAME)"; 557 | }; 558 | name = Debug; 559 | }; 560 | 8843D4AE23DD24B800CC7517 /* Release */ = { 561 | isa = XCBuildConfiguration; 562 | buildSettings = { 563 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 564 | CODE_SIGN_ENTITLEMENTS = Sources/ExampleApp.entitlements; 565 | CODE_SIGN_IDENTITY = "-"; 566 | COMBINE_HIDPI_IMAGES = YES; 567 | CURRENT_PROJECT_VERSION = 666; 568 | DEAD_CODE_STRIPPING = YES; 569 | INFOPLIST_FILE = Sources/Info.plist; 570 | LD_RUNPATH_SEARCH_PATHS = ( 571 | "$(inherited)", 572 | "@executable_path/../Frameworks", 573 | ); 574 | MARKETING_VERSION = 1.2.3; 575 | PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.ExampleApp; 576 | PRODUCT_NAME = "$(TARGET_NAME)"; 577 | }; 578 | name = Release; 579 | }; 580 | 8843D4B023DD24B800CC7517 /* Debug */ = { 581 | isa = XCBuildConfiguration; 582 | buildSettings = { 583 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 584 | COMBINE_HIDPI_IMAGES = YES; 585 | DEAD_CODE_STRIPPING = YES; 586 | GENERATE_INFOPLIST_FILE = YES; 587 | LD_RUNPATH_SEARCH_PATHS = ( 588 | "$(inherited)", 589 | "@executable_path/../Frameworks", 590 | "@loader_path/../Frameworks", 591 | ); 592 | PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.ExampleAppUITests; 593 | PRODUCT_NAME = "$(TARGET_NAME)"; 594 | TEST_TARGET_NAME = ExampleApp; 595 | }; 596 | name = Debug; 597 | }; 598 | 8843D4B123DD24B800CC7517 /* Release */ = { 599 | isa = XCBuildConfiguration; 600 | buildSettings = { 601 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 602 | COMBINE_HIDPI_IMAGES = YES; 603 | DEAD_CODE_STRIPPING = YES; 604 | GENERATE_INFOPLIST_FILE = YES; 605 | LD_RUNPATH_SEARCH_PATHS = ( 606 | "$(inherited)", 607 | "@executable_path/../Frameworks", 608 | "@loader_path/../Frameworks", 609 | ); 610 | PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.ExampleAppUITests; 611 | PRODUCT_NAME = "$(TARGET_NAME)"; 612 | TEST_TARGET_NAME = ExampleApp; 613 | }; 614 | name = Release; 615 | }; 616 | /* End XCBuildConfiguration section */ 617 | 618 | /* Begin XCConfigurationList section */ 619 | 883CDEA623FB426300A06CD3 /* Build configuration list for PBXNativeTarget "ExampleAppTests" */ = { 620 | isa = XCConfigurationList; 621 | buildConfigurations = ( 622 | 883CDEA223FB426300A06CD3 /* Debug */, 623 | 883CDEA323FB426300A06CD3 /* Release */, 624 | ); 625 | defaultConfigurationIsVisible = 0; 626 | defaultConfigurationName = Release; 627 | }; 628 | 8843D48C23DD24B700CC7517 /* Build configuration list for PBXProject "ExampleApp" */ = { 629 | isa = XCConfigurationList; 630 | buildConfigurations = ( 631 | 8843D4AA23DD24B800CC7517 /* Debug */, 632 | 8843D4AB23DD24B800CC7517 /* Release */, 633 | ); 634 | defaultConfigurationIsVisible = 0; 635 | defaultConfigurationName = Release; 636 | }; 637 | 8843D4AC23DD24B800CC7517 /* Build configuration list for PBXNativeTarget "ExampleApp" */ = { 638 | isa = XCConfigurationList; 639 | buildConfigurations = ( 640 | 8843D4AD23DD24B800CC7517 /* Debug */, 641 | 8843D4AE23DD24B800CC7517 /* Release */, 642 | ); 643 | defaultConfigurationIsVisible = 0; 644 | defaultConfigurationName = Release; 645 | }; 646 | 8843D4AF23DD24B800CC7517 /* Build configuration list for PBXNativeTarget "ExampleAppUITests" */ = { 647 | isa = XCConfigurationList; 648 | buildConfigurations = ( 649 | 8843D4B023DD24B800CC7517 /* Debug */, 650 | 8843D4B123DD24B800CC7517 /* Release */, 651 | ); 652 | defaultConfigurationIsVisible = 0; 653 | defaultConfigurationName = Release; 654 | }; 655 | /* End XCConfigurationList section */ 656 | }; 657 | rootObject = 8843D48923DD24B700CC7517 /* Project object */; 658 | } 659 | -------------------------------------------------------------------------------- /Example/ExampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/ExampleApp.xcodeproj/xcshareddata/IDETemplateMacros.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | FILEHEADER 7 | 8 | // Created by Jesse Squires 9 | // https://www.jessesquires.com 10 | // 11 | // Documentation 12 | // https://hexedbits.github.io/StatusItemController 13 | // 14 | // GitHub 15 | // https://github.com/hexedbits/StatusItemController 16 | // 17 | // Copyright © 2020-present Jesse Squires, Hexed Bits 18 | // https://www.hexedbits.com 19 | // 20 | 21 | 22 | -------------------------------------------------------------------------------- /Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/ExampleApp.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 39 | 40 | 41 | 42 | 45 | 51 | 52 | 53 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/Sources/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Squires 3 | // https://www.jessesquires.com 4 | // 5 | // Documentation 6 | // https://hexedbits.github.io/StatusItemController 7 | // 8 | // GitHub 9 | // https://github.com/hexedbits/StatusItemController 10 | // 11 | // Copyright © 2020-present Jesse Squires, Hexed Bits 12 | // https://www.hexedbits.com 13 | // 14 | 15 | import Cocoa 16 | import StatusItemController 17 | 18 | @main 19 | @MainActor 20 | final class AppDelegate: NSObject, NSApplicationDelegate { 21 | 22 | let statusItemController = ExampleAppStatusController() 23 | } 24 | -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-1024px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-1024px.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-128px-128pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-128px-128pt@1x.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-16px-16pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-16px-16pt@1x.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-256px-128pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-256px-128pt@2x.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-256px-256pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-256px-256pt@1x.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-32px-16pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-32px-16pt@2x.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-32px-32pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-32px-32pt@1x.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-512px-256pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-512px-256pt@2x.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-512px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-512px.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-64px-32pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon-64px-32pt@2x.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "AppIcon-16px-16pt@1x.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "AppIcon-32px-16pt@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "AppIcon-32px-32pt@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "AppIcon-64px-32pt@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "AppIcon-128px-128pt@1x.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "AppIcon-256px-128pt@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "AppIcon-256px-256pt@1x.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "AppIcon-512px-256pt@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "AppIcon-512px.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "AppIcon-1024px.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/StatusIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "hb-status-icon.png", 5 | "idiom" : "mac", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "hb-status-icon@2x.png", 10 | "idiom" : "mac", 11 | "scale" : "2x" 12 | } 13 | ], 14 | "info" : { 15 | "author" : "xcode", 16 | "version" : 1 17 | }, 18 | "properties" : { 19 | "template-rendering-intent" : "template" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/StatusIcon.imageset/hb-status-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/StatusIcon.imageset/hb-status-icon.png -------------------------------------------------------------------------------- /Example/Sources/Assets.xcassets/StatusIcon.imageset/hb-status-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/Example/Sources/Assets.xcassets/StatusIcon.imageset/hb-status-icon@2x.png -------------------------------------------------------------------------------- /Example/Sources/ExampleApp.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/Sources/ExampleAppStatusController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Squires 3 | // https://www.jessesquires.com 4 | // 5 | // Documentation 6 | // https://hexedbits.github.io/StatusItemController 7 | // 8 | // GitHub 9 | // https://github.com/hexedbits/StatusItemController 10 | // 11 | // Copyright © 2020-present Jesse Squires, Hexed Bits 12 | // https://www.hexedbits.com 13 | // 14 | 15 | import AppKit 16 | import Foundation 17 | import StatusItemController 18 | 19 | class ExampleAppStatusController: StatusItemController { 20 | 21 | init() { 22 | super.init(image: NSImage(named: "StatusIcon")!) 23 | } 24 | 25 | // MARK: Overrides 26 | 27 | override func buildMenu() -> NSMenu { 28 | let menu = NSMenu() 29 | menu.addItem(NSMenuItem(title: "Option 1")) 30 | menu.addItem(NSMenuItem(title: "Option 2")) 31 | menu.addItem(NSMenuItem(title: "Option 3")) 32 | menu.addItem(NSMenuItem.separator()) 33 | menu.addItem(NSMenuItem(title: "Version 1.0", isEnabled: false)) 34 | menu.addItem(NSMenuItem(title: "Quit", 35 | target: self, 36 | action: #selector(quit))) 37 | return menu 38 | } 39 | 40 | override func leftClickAction() { 41 | self.openMenu() 42 | } 43 | 44 | override func rightClickAction() { 45 | self.openMenu() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Example/Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2020 Hexed Bits. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | NSSupportsAutomaticTermination 32 | 33 | NSSupportsSuddenTermination 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Example/Sources/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Squires 3 | // https://www.jessesquires.com 4 | // 5 | // Documentation 6 | // https://hexedbits.github.io/StatusItemController 7 | // 8 | // GitHub 9 | // https://github.com/hexedbits/StatusItemController 10 | // 11 | // Copyright © 2020-present Jesse Squires, Hexed Bits 12 | // https://www.hexedbits.com 13 | // 14 | 15 | import Cocoa 16 | import StatusItemController 17 | 18 | final class ViewController: NSViewController { } 19 | -------------------------------------------------------------------------------- /Example/Tests/ExampleAppTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Squires 3 | // https://www.jessesquires.com 4 | // 5 | // Documentation 6 | // https://hexedbits.github.io/StatusItemController 7 | // 8 | // GitHub 9 | // https://github.com/hexedbits/StatusItemController 10 | // 11 | // Copyright © 2020-present Jesse Squires, Hexed Bits 12 | // https://www.hexedbits.com 13 | // 14 | 15 | @testable import ExampleApp 16 | import StatusItemController 17 | import XCTest 18 | 19 | final class ExampleAppTests: XCTestCase { 20 | 21 | @MainActor 22 | func test_DefaultInitValues() throws { 23 | let controller = TestController() 24 | let img = NSImage(named: "StatusIcon")! 25 | 26 | XCTAssertEqual(controller.statusItem.button?.image, img) 27 | XCTAssertEqual(controller.statusItem.length, NSStatusItem.squareLength) 28 | } 29 | 30 | @MainActor 31 | func test_BuildMenuIsCalled_ForOpenMenu() { 32 | let controller = TestController() 33 | 34 | let expectation = self.expectation() 35 | controller.buildMenuExpectation = expectation 36 | 37 | controller.openMenu() 38 | self.wait(for: [expectation]) 39 | } 40 | 41 | @MainActor 42 | func test_BuildMenuIsNotCalled_ForHideMenu() { 43 | let controller = TestController() 44 | 45 | let expectation = self.expectation() 46 | expectation.isInverted = true 47 | controller.buildMenuExpectation = expectation 48 | 49 | controller.hideMenu() 50 | self.wait(for: [expectation]) 51 | } 52 | 53 | @MainActor 54 | func test_LeftClickAction_isCalled() { 55 | let controller = TestController() 56 | 57 | let leftExpectation = self.expectation() 58 | let rightExpectation = self.expectation() 59 | rightExpectation.isInverted = true 60 | controller.leftClickExpectation = leftExpectation 61 | controller.rightClickExpectation = rightExpectation 62 | 63 | controller.statusItem.button?.performClick(nil) 64 | self.wait(for: [leftExpectation, rightExpectation]) 65 | } 66 | } 67 | 68 | final class TestController: ExampleAppStatusController { 69 | 70 | var buildMenuExpectation: XCTestExpectation? 71 | var leftClickExpectation: XCTestExpectation? 72 | var rightClickExpectation: XCTestExpectation? 73 | 74 | override func buildMenu() -> NSMenu { 75 | self.buildMenuExpectation?.fulfill() 76 | return super.buildMenu() 77 | } 78 | 79 | override func leftClickAction() { 80 | super.leftClickAction() 81 | 82 | self.leftClickExpectation!.fulfill() 83 | } 84 | 85 | override func rightClickAction() { 86 | super.rightClickAction() 87 | 88 | self.rightClickExpectation!.fulfill() 89 | } 90 | } 91 | 92 | extension XCTestCase { 93 | static let timeout = TimeInterval(3) 94 | 95 | func wait(for expectations: [XCTestExpectation]) { 96 | self.wait(for: expectations, timeout: Self.timeout) 97 | } 98 | 99 | func expectation(name: String = #function) -> XCTestExpectation { 100 | self.expectation(description: name) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Example/UITests/ExampleAppUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Squires 3 | // https://www.jessesquires.com 4 | // 5 | // Documentation 6 | // https://hexedbits.github.io/StatusItemController 7 | // 8 | // GitHub 9 | // https://github.com/hexedbits/StatusItemController 10 | // 11 | // Copyright © 2020-present Jesse Squires, Hexed Bits 12 | // https://www.hexedbits.com 13 | // 14 | 15 | import StatusItemController 16 | import XCTest 17 | 18 | final class ExampleAppUITests: XCTestCase { 19 | 20 | override func setUpWithError() throws { 21 | try super.setUpWithError() 22 | 23 | self.continueAfterFailure = false 24 | XCUIApplication().launch() 25 | } 26 | 27 | func test_StatusItemController() { 28 | let app = XCUIApplication() 29 | let statusItem = app.children(matching: .menuBar).element(boundBy: 1).children(matching: .statusItem).element 30 | let menuBarsQuery = app.menuBars 31 | 32 | statusItem.click() 33 | XCTAssertTrue(menuBarsQuery.menuItems["Option 1"].waitForExistence(timeout: 3)) 34 | XCTAssertTrue(menuBarsQuery.menuItems["Option 2"].exists) 35 | XCTAssertTrue(menuBarsQuery.menuItems["Option 3"].exists) 36 | XCTAssertTrue(menuBarsQuery.menuItems["Version 1.0"].exists) 37 | XCTAssertTrue(menuBarsQuery.menuItems["Quit"].exists) 38 | 39 | statusItem.click() 40 | statusItem.rightClick() 41 | XCTAssertTrue(menuBarsQuery.menuItems["Option 1"].waitForExistence(timeout: 3)) 42 | XCTAssertTrue(menuBarsQuery.menuItems["Option 2"].exists) 43 | XCTAssertTrue(menuBarsQuery.menuItems["Option 3"].exists) 44 | XCTAssertTrue(menuBarsQuery.menuItems["Version 1.0"].exists) 45 | XCTAssertTrue(menuBarsQuery.menuItems["Quit"].exists) 46 | 47 | app.buttons[XCUIIdentifierCloseWindow].click() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Hexed Bits 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.9 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | // 4 | // Created by Jesse Squires 5 | // https://www.jessesquires.com 6 | // 7 | // Documentation 8 | // https://hexedbits.github.io/StatusItemController 9 | // 10 | // GitHub 11 | // https://github.com/hexedbits/StatusItemController 12 | // 13 | // Copyright © 2020-present Jesse Squires, Hexed Bits 14 | // https://www.hexedbits.com 15 | // 16 | 17 | import PackageDescription 18 | 19 | let package = Package( 20 | name: "StatusItemController", 21 | platforms: [ 22 | .macOS(.v11) 23 | ], 24 | products: [ 25 | .library( 26 | name: "StatusItemController", 27 | targets: ["StatusItemController"]) 28 | ], 29 | targets: [ 30 | .target( 31 | name: "StatusItemController", 32 | path: "Sources"), 33 | .testTarget(name: "StatusItemControllerTests", 34 | dependencies: ["StatusItemController"], 35 | path: "Tests") 36 | ], 37 | swiftLanguageVersions: [.v5] 38 | ) 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StatusItemController [![CI](https://github.com/hexedbits/StatusItemController/workflows/CI/badge.svg)](https://github.com/hexedbits/StatusItemController/actions) 2 | 3 | *A "view controller" for menu bar Mac apps* 4 | 5 | [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fhexedbits%2FStatusItemController%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/hexedbits/StatusItemController)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fhexedbits%2FStatusItemController%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/hexedbits/StatusItemController) 6 | 7 | ## About 8 | 9 | This library provides a `StatusItemController` component that you can use to create menu bar apps, or apps with menu bar items in macOS. 10 | 11 | This component is used in [Red Eye](https://www.hexedbits.com/redeye/) and [Lucifer](https://www.hexedbits.com/lucifer/). 12 | 13 | ## Usage 14 | 15 | 1. Subclass `StatusItemController` 16 | 1. Implement the following methods: 17 | 1. `buildMenu() -> NSMenu` 18 | 1. `leftClickAction()` 19 | 1. `rightClickAction()` 20 | 1. Create an instance of your `StatusItemController` subclass in your `NSApplicationDelegate`. 21 | 22 | ## Requirements 23 | 24 | - macOS 11.0+ 25 | - Swift 5.9+ 26 | - Xcode 15.0+ 27 | - [SwiftLint](https://github.com/realm/SwiftLint) 28 | 29 | ## Installation 30 | 31 | ### [CocoaPods](http://cocoapods.org) 32 | 33 | ````ruby 34 | pod 'StatusItemController', '~> 2.0.0' 35 | ```` 36 | 37 | ### [Swift Package Manager](https://swift.org/package-manager/) 38 | 39 | Add `StatusItemController` to the `dependencies` value of your `Package.swift`. 40 | 41 | ```swift 42 | dependencies: [ 43 | .package(url: "https://github.com/hexedbits/StatusItemController", from: "2.0.0") 44 | ] 45 | ``` 46 | 47 | Alternatively, you can add the package [directly via Xcode](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app). 48 | 49 | ## Documentation 50 | 51 | You can read the [documentation here](https://hexedbits.github.io/StatusItemController). Generated with [jazzy](https://github.com/realm/jazzy). Hosted by [GitHub Pages](https://pages.github.com). 52 | 53 | ## Notes on Testing 54 | 55 | Unfortunately, `StatusItemController` cannot be tested directly. Attempting to create an `NSStatusItem` _outside_ of an app context throws an assert, which makes sense. Thus, in order to test `StatusItemController` it must be embedded in an app. Tests can be found in the Example App test suite. See [#15](https://github.com/hexedbits/StatusItemController/issues/15) for more details. 56 | 57 | ## Contributing 58 | 59 | Interested in making contributions to this project? Please review the guides below. 60 | 61 | - [Contributing Guidelines](https://github.com/hexedbits/.github/blob/main/CONTRIBUTING.md) 62 | - [Code of Conduct](https://github.com/hexedbits/.github/blob/main/CODE_OF_CONDUCT.md) 63 | - [Support and Help](https://github.com/hexedbits/.github/blob/main/SUPPORT.md) 64 | - [Security Policy](https://github.com/hexedbits/.github/blob/main/SECURITY.md) 65 | 66 | Also consider [sponsoring this project](https://github.com/sponsors/jessesquires) or [buying my apps](https://www.hexedbits.com)! ✌️ 67 | 68 | ## Credits 69 | 70 | Created and maintained by [**Jesse Squires**](https://www.jessesquires.com). 71 | 72 | ## License 73 | 74 | Released under the MIT License. See `LICENSE` for details. 75 | 76 | > **Copyright © 2020-present Jesse Squires.** 77 | -------------------------------------------------------------------------------- /Sources/NSApplication+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Squires 3 | // https://www.jessesquires.com 4 | // 5 | // Documentation 6 | // https://hexedbits.github.io/StatusItemController 7 | // 8 | // GitHub 9 | // https://github.com/hexedbits/StatusItemController 10 | // 11 | // Copyright © 2020-present Jesse Squires, Hexed Bits 12 | // https://www.hexedbits.com 13 | // 14 | 15 | import AppKit 16 | 17 | extension NSApplication { 18 | /// Returns `true` if the application's current event is `.rightMouseUp` or equivalent. 19 | /// Returns `false` otherwise. 20 | public var isCurrentEventRightClickUp: Bool { 21 | if let current = self.currentEvent { 22 | return current.isRightClickUp 23 | } 24 | return false 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/NSEvent+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Squires 3 | // https://www.jessesquires.com 4 | // 5 | // Documentation 6 | // https://hexedbits.github.io/StatusItemController 7 | // 8 | // GitHub 9 | // https://github.com/hexedbits/StatusItemController 10 | // 11 | // Copyright © 2020-present Jesse Squires, Hexed Bits 12 | // https://www.hexedbits.com 13 | // 14 | 15 | import AppKit 16 | import Cocoa 17 | 18 | extension NSEvent { 19 | /// Returns `true` if the event is `.rightMouseUp` or equivalent. 20 | /// Returns `false` otherwise. 21 | public var isRightClickUp: Bool { 22 | let rightClick = (self.type == .rightMouseUp) 23 | let controlClick = self.modifierFlags.contains(.control) 24 | return rightClick || controlClick 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/NSMenuItem+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Squires 3 | // https://www.jessesquires.com 4 | // 5 | // Documentation 6 | // https://hexedbits.github.io/StatusItemController 7 | // 8 | // GitHub 9 | // https://github.com/hexedbits/StatusItemController 10 | // 11 | // Copyright © 2020-present Jesse Squires, Hexed Bits 12 | // https://www.hexedbits.com 13 | // 14 | 15 | import AppKit 16 | 17 | extension NSMenuItem { 18 | /// A convenience init for `NSMenuItem`. 19 | /// - Parameters: 20 | /// - title: The title of the menu item. 21 | /// - image: The image for the menu item. 22 | /// - target: The target to be associated with the menu item. 23 | /// - action: The action selector to be associated with the menu item. 24 | /// - keyEquivalent: A string representing a keyboard key to be used as the key equivalent. 25 | /// - isEnabled: A Boolean value that indicates whether the menu item is enabled. 26 | public convenience init(title: String, 27 | image: NSImage? = nil, 28 | target: AnyObject? = nil, 29 | action: Selector? = nil, 30 | keyEquivalent: String = "", 31 | isEnabled: Bool = true) { 32 | self.init(title: title, action: action, keyEquivalent: keyEquivalent) 33 | self.image = image 34 | self.target = target 35 | self.isEnabled = isEnabled 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/StatusItemController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Squires 3 | // https://www.jessesquires.com 4 | // 5 | // Documentation 6 | // https://hexedbits.github.io/StatusItemController 7 | // 8 | // GitHub 9 | // https://github.com/hexedbits/StatusItemController 10 | // 11 | // Copyright © 2020-present Jesse Squires, Hexed Bits 12 | // https://www.hexedbits.com 13 | // 14 | 15 | import AppKit 16 | 17 | /// Controller for an `NSStatusItem`. Designed to be subclassed. 18 | /// - Warning: You must subclass this controller. 19 | @MainActor 20 | open class StatusItemController: NSObject, NSMenuDelegate { 21 | 22 | // MARK: Properties 23 | 24 | /// The status item. 25 | public let statusItem: NSStatusItem 26 | 27 | // MARK: Init 28 | 29 | /// Creates a new `StatusItemController`. 30 | /// - Parameters: 31 | /// - image: An image for the status item button. 32 | /// - length: The length of the status item. 33 | public init(image: NSImage, length: CGFloat = NSStatusItem.squareLength) { 34 | self.statusItem = NSStatusBar.system.statusItem(withLength: length) 35 | super.init() 36 | self.statusItem.button?.toolTip = Bundle.main.infoDictionary?["CFBundleName"] as? String 37 | self.statusItem.button?.image = image 38 | (self.statusItem.button?.cell as? NSButtonCell)?.highlightsBy = [NSCell.StyleMask.init(rawValue: 0)] 39 | self.statusItem.button?.target = self 40 | self.statusItem.button?.action = #selector(_didClickStatusItem(_:)) 41 | self.statusItem.button?.sendAction(on: [.leftMouseDown, .rightMouseUp]) 42 | } 43 | 44 | /// :nodoc: 45 | deinit { 46 | NSStatusBar.system.removeStatusItem(self.statusItem) 47 | } 48 | 49 | // MARK: Open methods 50 | 51 | /// Constructs an `NSMenu` to display for the status item. 52 | /// - Returns: A menu object. 53 | /// - Warning: You must override this method. 54 | open func buildMenu() -> NSMenu { NSMenu() } 55 | 56 | /// The action to be executed on the `.leftMouseDown` event. 57 | /// - Warning: You must override this method. 58 | open func leftClickAction() { } 59 | 60 | /// The action to be executed on `.rightMouseUp` event. 61 | /// - Warning: You must override this method. 62 | open func rightClickAction() { } 63 | 64 | // MARK: Actions 65 | 66 | /// Opens the status item menu. 67 | /// You may wish to call this from `leftClickAction()` or `rightClickAction()`. 68 | public func openMenu() { 69 | // Yes, this is deprecated, however there seems to be a bug in AppKit. 70 | // Without using this method, the menu does not popup immediately, 71 | // and you have to right click a second time. 72 | self.statusItem.popUpMenu(self._configureMenu(hidden: false)!) 73 | } 74 | 75 | /// Hides the status item menu. 76 | public func hideMenu() { 77 | self._configureMenu(hidden: true) 78 | } 79 | 80 | /// Quits the application. 81 | /// You may wish to create an `NSMenuItem` that calls this method. 82 | @objc 83 | public func quit() { 84 | NSApp.terminate(self) 85 | } 86 | 87 | // MARK: Private 88 | 89 | @discardableResult 90 | private func _configureMenu(hidden: Bool) -> NSMenu? { 91 | if hidden { 92 | self.statusItem.menu = nil 93 | return nil 94 | } 95 | let menu = self.buildMenu() 96 | menu.delegate = self 97 | self.statusItem.menu = menu 98 | return menu 99 | } 100 | 101 | @objc 102 | private func _didClickStatusItem(_ sender: NSStatusItem) { 103 | if NSApp.isCurrentEventRightClickUp { 104 | self.rightClickAction() 105 | } else { 106 | self.leftClickAction() 107 | } 108 | } 109 | 110 | // MARK: NSMenuDelegate 111 | 112 | /// :nodoc: 113 | public func menuDidClose(_ menu: NSMenu) { 114 | self._configureMenu(hidden: true) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /StatusItemController.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'StatusItemController' 3 | s.version = '2.0.1' 4 | s.license = 'MIT' 5 | 6 | s.summary = 'A "view controller" for menu bar Mac apps' 7 | s.homepage = 'https://github.com/hexedbits/StatusItemController' 8 | s.documentation_url = 'https://hexedbits.github.io/StatusItemController' 9 | s.social_media_url = 'https://www.jessesquires.com' 10 | s.author = 'Jesse Squires' 11 | 12 | s.source = { :git => 'https://github.com/hexedbits/StatusItemController.git', :tag => s.version } 13 | s.source_files = 'Sources/*.swift' 14 | 15 | s.swift_version = '5.9' 16 | s.osx.deployment_target = '11.0' 17 | s.requires_arc = true 18 | end 19 | -------------------------------------------------------------------------------- /StatusItemController.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0BDFEA63260C0A8E004FFF4E /* NSApplication+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BDFEA62260C0A8E004FFF4E /* NSApplication+Extensions.swift */; }; 11 | 0BDFEA67260C0A9A004FFF4E /* NSMenuItem+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BDFEA66260C0A9A004FFF4E /* NSMenuItem+Extensions.swift */; }; 12 | 886C102024393069008D8DEA /* StatusItemController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 886C101624393069008D8DEA /* StatusItemController.framework */; }; 13 | 886C102524393069008D8DEA /* StatusItemControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 886C102424393069008D8DEA /* StatusItemControllerTests.swift */; }; 14 | 886C1031243930C4008D8DEA /* StatusItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 886C1030243930C4008D8DEA /* StatusItemController.swift */; }; 15 | 886C103E24394746008D8DEA /* NSEvent+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 886C103D24394746008D8DEA /* NSEvent+Extensions.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXContainerItemProxy section */ 19 | 886C102124393069008D8DEA /* PBXContainerItemProxy */ = { 20 | isa = PBXContainerItemProxy; 21 | containerPortal = 886C100D24393069008D8DEA /* Project object */; 22 | proxyType = 1; 23 | remoteGlobalIDString = 886C101524393069008D8DEA; 24 | remoteInfo = StatusItemController; 25 | }; 26 | /* End PBXContainerItemProxy section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 0BDFEA62260C0A8E004FFF4E /* NSApplication+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSApplication+Extensions.swift"; sourceTree = ""; }; 30 | 0BDFEA66260C0A9A004FFF4E /* NSMenuItem+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMenuItem+Extensions.swift"; sourceTree = ""; }; 31 | 886C101624393069008D8DEA /* StatusItemController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = StatusItemController.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 886C101F24393069008D8DEA /* StatusItemControllerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StatusItemControllerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 886C102424393069008D8DEA /* StatusItemControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemControllerTests.swift; sourceTree = ""; }; 34 | 886C1030243930C4008D8DEA /* StatusItemController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemController.swift; sourceTree = ""; }; 35 | 886C103D24394746008D8DEA /* NSEvent+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSEvent+Extensions.swift"; sourceTree = ""; }; 36 | /* End PBXFileReference section */ 37 | 38 | /* Begin PBXFrameworksBuildPhase section */ 39 | 886C101324393069008D8DEA /* Frameworks */ = { 40 | isa = PBXFrameworksBuildPhase; 41 | buildActionMask = 2147483647; 42 | files = ( 43 | ); 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | 886C101C24393069008D8DEA /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | 886C102024393069008D8DEA /* StatusItemController.framework in Frameworks */, 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 886C100C24393069008D8DEA = { 58 | isa = PBXGroup; 59 | children = ( 60 | 886C101824393069008D8DEA /* Sources */, 61 | 886C102324393069008D8DEA /* Tests */, 62 | 886C101724393069008D8DEA /* Products */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | 886C101724393069008D8DEA /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 886C101624393069008D8DEA /* StatusItemController.framework */, 70 | 886C101F24393069008D8DEA /* StatusItemControllerTests.xctest */, 71 | ); 72 | name = Products; 73 | sourceTree = ""; 74 | }; 75 | 886C101824393069008D8DEA /* Sources */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 0BDFEA62260C0A8E004FFF4E /* NSApplication+Extensions.swift */, 79 | 886C103D24394746008D8DEA /* NSEvent+Extensions.swift */, 80 | 0BDFEA66260C0A9A004FFF4E /* NSMenuItem+Extensions.swift */, 81 | 886C1030243930C4008D8DEA /* StatusItemController.swift */, 82 | ); 83 | path = Sources; 84 | sourceTree = ""; 85 | }; 86 | 886C102324393069008D8DEA /* Tests */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 886C102424393069008D8DEA /* StatusItemControllerTests.swift */, 90 | ); 91 | path = Tests; 92 | sourceTree = ""; 93 | }; 94 | /* End PBXGroup section */ 95 | 96 | /* Begin PBXHeadersBuildPhase section */ 97 | 886C101124393069008D8DEA /* Headers */ = { 98 | isa = PBXHeadersBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | ); 102 | runOnlyForDeploymentPostprocessing = 0; 103 | }; 104 | /* End PBXHeadersBuildPhase section */ 105 | 106 | /* Begin PBXNativeTarget section */ 107 | 886C101524393069008D8DEA /* StatusItemController */ = { 108 | isa = PBXNativeTarget; 109 | buildConfigurationList = 886C102A24393069008D8DEA /* Build configuration list for PBXNativeTarget "StatusItemController" */; 110 | buildPhases = ( 111 | 886C101124393069008D8DEA /* Headers */, 112 | 886C101224393069008D8DEA /* Sources */, 113 | 886C101324393069008D8DEA /* Frameworks */, 114 | 886C101424393069008D8DEA /* Resources */, 115 | 886C10382439348B008D8DEA /* SwiftLint */, 116 | ); 117 | buildRules = ( 118 | ); 119 | dependencies = ( 120 | ); 121 | name = StatusItemController; 122 | productName = StatusItemController; 123 | productReference = 886C101624393069008D8DEA /* StatusItemController.framework */; 124 | productType = "com.apple.product-type.framework"; 125 | }; 126 | 886C101E24393069008D8DEA /* StatusItemControllerTests */ = { 127 | isa = PBXNativeTarget; 128 | buildConfigurationList = 886C102D24393069008D8DEA /* Build configuration list for PBXNativeTarget "StatusItemControllerTests" */; 129 | buildPhases = ( 130 | 886C101B24393069008D8DEA /* Sources */, 131 | 886C101C24393069008D8DEA /* Frameworks */, 132 | 886C101D24393069008D8DEA /* Resources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | 886C102224393069008D8DEA /* PBXTargetDependency */, 138 | ); 139 | name = StatusItemControllerTests; 140 | productName = StatusItemControllerTests; 141 | productReference = 886C101F24393069008D8DEA /* StatusItemControllerTests.xctest */; 142 | productType = "com.apple.product-type.bundle.unit-test"; 143 | }; 144 | /* End PBXNativeTarget section */ 145 | 146 | /* Begin PBXProject section */ 147 | 886C100D24393069008D8DEA /* Project object */ = { 148 | isa = PBXProject; 149 | attributes = { 150 | BuildIndependentTargetsInParallel = YES; 151 | LastSwiftUpdateCheck = 1140; 152 | LastUpgradeCheck = 1520; 153 | ORGANIZATIONNAME = "Hexed Bits"; 154 | TargetAttributes = { 155 | 886C101524393069008D8DEA = { 156 | CreatedOnToolsVersion = 11.4; 157 | LastSwiftMigration = 1140; 158 | }; 159 | 886C101E24393069008D8DEA = { 160 | CreatedOnToolsVersion = 11.4; 161 | }; 162 | }; 163 | }; 164 | buildConfigurationList = 886C101024393069008D8DEA /* Build configuration list for PBXProject "StatusItemController" */; 165 | compatibilityVersion = "Xcode 9.3"; 166 | developmentRegion = en; 167 | hasScannedForEncodings = 0; 168 | knownRegions = ( 169 | en, 170 | Base, 171 | ); 172 | mainGroup = 886C100C24393069008D8DEA; 173 | productRefGroup = 886C101724393069008D8DEA /* Products */; 174 | projectDirPath = ""; 175 | projectRoot = ""; 176 | targets = ( 177 | 886C101524393069008D8DEA /* StatusItemController */, 178 | 886C101E24393069008D8DEA /* StatusItemControllerTests */, 179 | ); 180 | }; 181 | /* End PBXProject section */ 182 | 183 | /* Begin PBXResourcesBuildPhase section */ 184 | 886C101424393069008D8DEA /* Resources */ = { 185 | isa = PBXResourcesBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | 886C101D24393069008D8DEA /* Resources */ = { 192 | isa = PBXResourcesBuildPhase; 193 | buildActionMask = 2147483647; 194 | files = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 886C10382439348B008D8DEA /* SwiftLint */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | alwaysOutOfDate = 1; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | ); 207 | inputFileListPaths = ( 208 | ); 209 | inputPaths = ( 210 | ); 211 | name = SwiftLint; 212 | outputFileListPaths = ( 213 | ); 214 | outputPaths = ( 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | shellPath = /bin/zsh; 218 | shellScript = "\"${SRCROOT}/scripts/lint.zsh\"\n"; 219 | }; 220 | /* End PBXShellScriptBuildPhase section */ 221 | 222 | /* Begin PBXSourcesBuildPhase section */ 223 | 886C101224393069008D8DEA /* Sources */ = { 224 | isa = PBXSourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 886C1031243930C4008D8DEA /* StatusItemController.swift in Sources */, 228 | 886C103E24394746008D8DEA /* NSEvent+Extensions.swift in Sources */, 229 | 0BDFEA67260C0A9A004FFF4E /* NSMenuItem+Extensions.swift in Sources */, 230 | 0BDFEA63260C0A8E004FFF4E /* NSApplication+Extensions.swift in Sources */, 231 | ); 232 | runOnlyForDeploymentPostprocessing = 0; 233 | }; 234 | 886C101B24393069008D8DEA /* Sources */ = { 235 | isa = PBXSourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | 886C102524393069008D8DEA /* StatusItemControllerTests.swift in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | /* End PBXSourcesBuildPhase section */ 243 | 244 | /* Begin PBXTargetDependency section */ 245 | 886C102224393069008D8DEA /* PBXTargetDependency */ = { 246 | isa = PBXTargetDependency; 247 | target = 886C101524393069008D8DEA /* StatusItemController */; 248 | targetProxy = 886C102124393069008D8DEA /* PBXContainerItemProxy */; 249 | }; 250 | /* End PBXTargetDependency section */ 251 | 252 | /* Begin XCBuildConfiguration section */ 253 | 886C102824393069008D8DEA /* Debug */ = { 254 | isa = XCBuildConfiguration; 255 | buildSettings = { 256 | ALWAYS_SEARCH_USER_PATHS = NO; 257 | CLANG_ANALYZER_NONNULL = YES; 258 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 260 | CLANG_CXX_LIBRARY = "libc++"; 261 | CLANG_ENABLE_MODULES = YES; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_ENABLE_OBJC_WEAK = YES; 264 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 265 | CLANG_WARN_BOOL_CONVERSION = YES; 266 | CLANG_WARN_COMMA = YES; 267 | CLANG_WARN_CONSTANT_CONVERSION = YES; 268 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 269 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 270 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 271 | CLANG_WARN_EMPTY_BODY = YES; 272 | CLANG_WARN_ENUM_CONVERSION = YES; 273 | CLANG_WARN_INFINITE_RECURSION = YES; 274 | CLANG_WARN_INT_CONVERSION = YES; 275 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 276 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 277 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 278 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 279 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 280 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 281 | CLANG_WARN_STRICT_PROTOTYPES = YES; 282 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 283 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 284 | CLANG_WARN_UNREACHABLE_CODE = YES; 285 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 286 | COPY_PHASE_STRIP = NO; 287 | CURRENT_PROJECT_VERSION = 1; 288 | DEAD_CODE_STRIPPING = YES; 289 | DEBUG_INFORMATION_FORMAT = dwarf; 290 | ENABLE_STRICT_OBJC_MSGSEND = YES; 291 | ENABLE_TESTABILITY = YES; 292 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 293 | GCC_C_LANGUAGE_STANDARD = gnu11; 294 | GCC_DYNAMIC_NO_PIC = NO; 295 | GCC_NO_COMMON_BLOCKS = YES; 296 | GCC_OPTIMIZATION_LEVEL = 0; 297 | GCC_PREPROCESSOR_DEFINITIONS = ( 298 | "DEBUG=1", 299 | "$(inherited)", 300 | ); 301 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 302 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 303 | GCC_WARN_UNDECLARED_SELECTOR = YES; 304 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 305 | GCC_WARN_UNUSED_FUNCTION = YES; 306 | GCC_WARN_UNUSED_VARIABLE = YES; 307 | MACOSX_DEPLOYMENT_TARGET = 11.0; 308 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 309 | MTL_FAST_MATH = YES; 310 | ONLY_ACTIVE_ARCH = YES; 311 | SDKROOT = macosx; 312 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 313 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 314 | SWIFT_STRICT_CONCURRENCY = complete; 315 | SWIFT_VERSION = 5.0; 316 | VERSIONING_SYSTEM = "apple-generic"; 317 | VERSION_INFO_PREFIX = ""; 318 | }; 319 | name = Debug; 320 | }; 321 | 886C102924393069008D8DEA /* Release */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | ALWAYS_SEARCH_USER_PATHS = NO; 325 | CLANG_ANALYZER_NONNULL = YES; 326 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 327 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 328 | CLANG_CXX_LIBRARY = "libc++"; 329 | CLANG_ENABLE_MODULES = YES; 330 | CLANG_ENABLE_OBJC_ARC = YES; 331 | CLANG_ENABLE_OBJC_WEAK = YES; 332 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 333 | CLANG_WARN_BOOL_CONVERSION = YES; 334 | CLANG_WARN_COMMA = YES; 335 | CLANG_WARN_CONSTANT_CONVERSION = YES; 336 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 337 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 338 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 339 | CLANG_WARN_EMPTY_BODY = YES; 340 | CLANG_WARN_ENUM_CONVERSION = YES; 341 | CLANG_WARN_INFINITE_RECURSION = YES; 342 | CLANG_WARN_INT_CONVERSION = YES; 343 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 344 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 345 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 346 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 347 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 348 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 349 | CLANG_WARN_STRICT_PROTOTYPES = YES; 350 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 351 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 352 | CLANG_WARN_UNREACHABLE_CODE = YES; 353 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 354 | COPY_PHASE_STRIP = NO; 355 | CURRENT_PROJECT_VERSION = 1; 356 | DEAD_CODE_STRIPPING = YES; 357 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 358 | ENABLE_NS_ASSERTIONS = NO; 359 | ENABLE_STRICT_OBJC_MSGSEND = YES; 360 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 361 | GCC_C_LANGUAGE_STANDARD = gnu11; 362 | GCC_NO_COMMON_BLOCKS = YES; 363 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 364 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 365 | GCC_WARN_UNDECLARED_SELECTOR = YES; 366 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 367 | GCC_WARN_UNUSED_FUNCTION = YES; 368 | GCC_WARN_UNUSED_VARIABLE = YES; 369 | MACOSX_DEPLOYMENT_TARGET = 11.0; 370 | MTL_ENABLE_DEBUG_INFO = NO; 371 | MTL_FAST_MATH = YES; 372 | SDKROOT = macosx; 373 | SWIFT_COMPILATION_MODE = wholemodule; 374 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 375 | SWIFT_STRICT_CONCURRENCY = complete; 376 | SWIFT_VERSION = 5.0; 377 | VERSIONING_SYSTEM = "apple-generic"; 378 | VERSION_INFO_PREFIX = ""; 379 | }; 380 | name = Release; 381 | }; 382 | 886C102B24393069008D8DEA /* Debug */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | CLANG_ENABLE_MODULES = YES; 386 | CODE_SIGN_STYLE = Manual; 387 | COMBINE_HIDPI_IMAGES = YES; 388 | DEAD_CODE_STRIPPING = YES; 389 | DEFINES_MODULE = YES; 390 | DEVELOPMENT_TEAM = ""; 391 | DYLIB_COMPATIBILITY_VERSION = 1; 392 | DYLIB_CURRENT_VERSION = 1; 393 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 394 | ENABLE_MODULE_VERIFIER = YES; 395 | GENERATE_INFOPLIST_FILE = YES; 396 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 397 | LD_RUNPATH_SEARCH_PATHS = ( 398 | "$(inherited)", 399 | "@executable_path/../Frameworks", 400 | "@loader_path/Frameworks", 401 | ); 402 | MARKETING_VERSION = 1.2.0; 403 | MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; 404 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; 405 | PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.StatusItemController; 406 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 407 | PROVISIONING_PROFILE_SPECIFIER = ""; 408 | SKIP_INSTALL = YES; 409 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 410 | }; 411 | name = Debug; 412 | }; 413 | 886C102C24393069008D8DEA /* Release */ = { 414 | isa = XCBuildConfiguration; 415 | buildSettings = { 416 | CLANG_ENABLE_MODULES = YES; 417 | CODE_SIGN_STYLE = Manual; 418 | COMBINE_HIDPI_IMAGES = YES; 419 | DEAD_CODE_STRIPPING = YES; 420 | DEFINES_MODULE = YES; 421 | DEVELOPMENT_TEAM = ""; 422 | DYLIB_COMPATIBILITY_VERSION = 1; 423 | DYLIB_CURRENT_VERSION = 1; 424 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 425 | ENABLE_MODULE_VERIFIER = YES; 426 | GENERATE_INFOPLIST_FILE = YES; 427 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 428 | LD_RUNPATH_SEARCH_PATHS = ( 429 | "$(inherited)", 430 | "@executable_path/../Frameworks", 431 | "@loader_path/Frameworks", 432 | ); 433 | MARKETING_VERSION = 1.2.0; 434 | MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; 435 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; 436 | PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.StatusItemController; 437 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 438 | PROVISIONING_PROFILE_SPECIFIER = ""; 439 | SKIP_INSTALL = YES; 440 | }; 441 | name = Release; 442 | }; 443 | 886C102E24393069008D8DEA /* Debug */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 447 | CODE_SIGN_STYLE = Manual; 448 | COMBINE_HIDPI_IMAGES = YES; 449 | DEAD_CODE_STRIPPING = YES; 450 | DEVELOPMENT_TEAM = ""; 451 | GENERATE_INFOPLIST_FILE = YES; 452 | LD_RUNPATH_SEARCH_PATHS = ( 453 | "$(inherited)", 454 | "@executable_path/../Frameworks", 455 | "@loader_path/../Frameworks", 456 | ); 457 | PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.StatusItemControllerTests; 458 | PRODUCT_NAME = "$(TARGET_NAME)"; 459 | PROVISIONING_PROFILE_SPECIFIER = ""; 460 | }; 461 | name = Debug; 462 | }; 463 | 886C102F24393069008D8DEA /* Release */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 467 | CODE_SIGN_STYLE = Manual; 468 | COMBINE_HIDPI_IMAGES = YES; 469 | DEAD_CODE_STRIPPING = YES; 470 | DEVELOPMENT_TEAM = ""; 471 | GENERATE_INFOPLIST_FILE = YES; 472 | LD_RUNPATH_SEARCH_PATHS = ( 473 | "$(inherited)", 474 | "@executable_path/../Frameworks", 475 | "@loader_path/../Frameworks", 476 | ); 477 | PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.StatusItemControllerTests; 478 | PRODUCT_NAME = "$(TARGET_NAME)"; 479 | PROVISIONING_PROFILE_SPECIFIER = ""; 480 | }; 481 | name = Release; 482 | }; 483 | /* End XCBuildConfiguration section */ 484 | 485 | /* Begin XCConfigurationList section */ 486 | 886C101024393069008D8DEA /* Build configuration list for PBXProject "StatusItemController" */ = { 487 | isa = XCConfigurationList; 488 | buildConfigurations = ( 489 | 886C102824393069008D8DEA /* Debug */, 490 | 886C102924393069008D8DEA /* Release */, 491 | ); 492 | defaultConfigurationIsVisible = 0; 493 | defaultConfigurationName = Release; 494 | }; 495 | 886C102A24393069008D8DEA /* Build configuration list for PBXNativeTarget "StatusItemController" */ = { 496 | isa = XCConfigurationList; 497 | buildConfigurations = ( 498 | 886C102B24393069008D8DEA /* Debug */, 499 | 886C102C24393069008D8DEA /* Release */, 500 | ); 501 | defaultConfigurationIsVisible = 0; 502 | defaultConfigurationName = Release; 503 | }; 504 | 886C102D24393069008D8DEA /* Build configuration list for PBXNativeTarget "StatusItemControllerTests" */ = { 505 | isa = XCConfigurationList; 506 | buildConfigurations = ( 507 | 886C102E24393069008D8DEA /* Debug */, 508 | 886C102F24393069008D8DEA /* Release */, 509 | ); 510 | defaultConfigurationIsVisible = 0; 511 | defaultConfigurationName = Release; 512 | }; 513 | /* End XCConfigurationList section */ 514 | }; 515 | rootObject = 886C100D24393069008D8DEA /* Project object */; 516 | } 517 | -------------------------------------------------------------------------------- /StatusItemController.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /StatusItemController.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /StatusItemController.xcodeproj/xcshareddata/IDETemplateMacros.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | FILEHEADER 7 | 8 | // Created by Jesse Squires 9 | // https://www.jessesquires.com 10 | // 11 | // Documentation 12 | // https://hexedbits.github.io/StatusItemController 13 | // 14 | // GitHub 15 | // https://github.com/hexedbits/StatusItemController 16 | // 17 | // Copyright © 2020-present Jesse Squires, Hexed Bits 18 | // https://www.hexedbits.com 19 | // 20 | 21 | 22 | -------------------------------------------------------------------------------- /StatusItemController.xcodeproj/xcshareddata/xcschemes/StatusItemController.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 39 | 40 | 41 | 42 | 46 | 52 | 53 | 54 | 55 | 56 | 66 | 67 | 73 | 74 | 80 | 81 | 82 | 83 | 85 | 86 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Tests/StatusItemControllerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jesse Squires 3 | // https://www.jessesquires.com 4 | // 5 | // Documentation 6 | // https://hexedbits.github.io/StatusItemController 7 | // 8 | // GitHub 9 | // https://github.com/hexedbits/StatusItemController 10 | // 11 | // Copyright © 2020-present Jesse Squires, Hexed Bits 12 | // https://www.hexedbits.com 13 | // 14 | 15 | import AppKit 16 | @testable import StatusItemController 17 | import XCTest 18 | 19 | /// - Note: see ExampleAppTests instead. 20 | /// Unfortunately, `StatusItemController` cannot be tested directly. 21 | /// It requires being embedded in an app. 22 | final class StatusItemControllerTests: XCTestCase { } 23 | -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

StatusItemController 2.0.1 Docs (100% documented)

21 |

GitHubView on GitHub

22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 | 35 |
36 |
37 | 63 |
64 |
65 |
66 |

Classes

67 |

The following classes are available globally.

68 | 69 |
70 |
71 |
72 |
    73 |
  • 74 |
    75 | 76 | 77 | 78 | StatusItemController 79 | 80 |
    81 |
    82 |
    83 |
    84 |
    85 |
    86 |

    Controller for an NSStatusItem. Designed to be subclassed.

    87 |
    88 |

    Warning

    89 | You must subclass this controller. 90 | 91 |
    92 | 93 | See more 94 |
    95 |
    96 |

    Declaration

    97 |
    98 |

    Swift

    99 |
    @MainActor
    100 | open class StatusItemController : NSObject, NSMenuDelegate
    101 | 102 |
    103 |
    104 |
    105 |
    106 |
  • 107 |
108 |
109 |
110 |
111 | 115 |
116 |
117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/Classes/StatusItemController.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | StatusItemController Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

StatusItemController 2.0.1 Docs (100% documented)

21 |

GitHubView on GitHub

22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 | 35 |
36 |
37 | 63 |
64 |
65 |
66 |

StatusItemController

67 |
68 |
69 | 70 |
@MainActor
 71 | open class StatusItemController : NSObject, NSMenuDelegate
72 | 73 |
74 |
75 |

Controller for an NSStatusItem. Designed to be subclassed.

76 |
77 |

Warning

78 | You must subclass this controller. 79 | 80 |
81 | 82 |
83 |
84 |
85 |
86 | 87 | 88 |
89 | 90 |

Properties 91 |

92 |
93 |
94 |
    95 |
  • 96 |
    97 | 98 | 99 | 100 | statusItem 101 | 102 |
    103 |
    104 |
    105 |
    106 |
    107 |
    108 |

    The status item.

    109 | 110 |
    111 |
    112 |

    Declaration

    113 |
    114 |

    Swift

    115 |
    public let statusItem: NSStatusItem
    116 | 117 |
    118 |
    119 |
    120 |
    121 |
  • 122 |
123 |
124 |
125 |
126 | 127 | 128 |
129 | 130 |

Init 131 |

132 |
133 |
134 |
    135 |
  • 136 |
    137 | 138 | 139 | 140 | init(image:length:) 141 | 142 |
    143 |
    144 |
    145 |
    146 |
    147 |
    148 |

    Creates a new StatusItemController.

    149 | 150 |
    151 |
    152 |

    Declaration

    153 |
    154 |

    Swift

    155 |
    public init(image: NSImage, length: CGFloat = NSStatusItem.squareLength)
    156 | 157 |
    158 |
    159 |
    160 |

    Parameters

    161 | 162 | 163 | 164 | 169 | 174 | 175 | 176 | 181 | 186 | 187 | 188 |
    165 | 166 | image 167 | 168 | 170 |
    171 |

    An image for the status item button.

    172 |
    173 |
    177 | 178 | length 179 | 180 | 182 |
    183 |

    The length of the status item.

    184 |
    185 |
    189 |
    190 |
    191 |
    192 |
  • 193 |
194 |
195 |
196 |
197 | 198 | 199 |
200 | 201 |

Open methods 202 |

203 |
204 |
205 |
    206 |
  • 207 |
    208 | 209 | 210 | 211 | buildMenu() 212 | 213 |
    214 |
    215 |
    216 |
    217 |
    218 |
    219 |

    Constructs an NSMenu to display for the status item.

    220 |
    221 |

    Warning

    222 | You must override this method. 223 | 224 |
    225 | 226 |
    227 |
    228 |

    Declaration

    229 |
    230 |

    Swift

    231 |
    open func buildMenu() -> NSMenu
    232 | 233 |
    234 |
    235 |
    236 |

    Return Value

    237 |

    A menu object.

    238 |
    239 |
    240 |
    241 |
  • 242 |
  • 243 |
    244 | 245 | 246 | 247 | leftClickAction() 248 | 249 |
    250 |
    251 |
    252 |
    253 |
    254 |
    255 |

    The action to be executed on the .leftMouseDown event.

    256 |
    257 |

    Warning

    258 | You must override this method. 259 | 260 |
    261 | 262 |
    263 |
    264 |

    Declaration

    265 |
    266 |

    Swift

    267 |
    open func leftClickAction()
    268 | 269 |
    270 |
    271 |
    272 |
    273 |
  • 274 |
  • 275 |
    276 | 277 | 278 | 279 | rightClickAction() 280 | 281 |
    282 |
    283 |
    284 |
    285 |
    286 |
    287 |

    The action to be executed on .rightMouseUp event.

    288 |
    289 |

    Warning

    290 | You must override this method. 291 | 292 |
    293 | 294 |
    295 |
    296 |

    Declaration

    297 |
    298 |

    Swift

    299 |
    open func rightClickAction()
    300 | 301 |
    302 |
    303 |
    304 |
    305 |
  • 306 |
307 |
308 |
309 |
310 | 311 | 312 |
313 | 314 |

Actions 315 |

316 |
317 |
318 |
    319 |
  • 320 |
    321 | 322 | 323 | 324 | openMenu() 325 | 326 |
    327 |
    328 |
    329 |
    330 |
    331 |
    332 |

    Opens the status item menu. 333 | You may wish to call this from leftClickAction() or rightClickAction().

    334 | 335 |
    336 |
    337 |

    Declaration

    338 |
    339 |

    Swift

    340 |
    public func openMenu()
    341 | 342 |
    343 |
    344 |
    345 |
    346 |
  • 347 |
  • 348 |
    349 | 350 | 351 | 352 | hideMenu() 353 | 354 |
    355 |
    356 |
    357 |
    358 |
    359 |
    360 |

    Hides the status item menu.

    361 | 362 |
    363 |
    364 |

    Declaration

    365 |
    366 |

    Swift

    367 |
    public func hideMenu()
    368 | 369 |
    370 |
    371 |
    372 |
    373 |
  • 374 |
  • 375 |
    376 | 377 | 378 | 379 | quit() 380 | 381 |
    382 |
    383 |
    384 |
    385 |
    386 |
    387 |

    Quits the application. 388 | You may wish to create an NSMenuItem that calls this method.

    389 | 390 |
    391 |
    392 |

    Declaration

    393 |
    394 |

    Swift

    395 |
    @objc
    396 | public func quit()
    397 | 398 |
    399 |
    400 |
    401 |
    402 |
  • 403 |
404 |
405 |
406 |
407 | 411 |
412 |
413 | 414 | 415 | -------------------------------------------------------------------------------- /docs/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

StatusItemController 2.0.1 Docs (100% documented)

21 |

GitHubView on GitHub

22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 | 35 |
36 |
37 | 63 |
64 |
65 |
66 |

Extensions

67 |

The following extensions are available globally.

68 | 69 |
70 |
71 |
72 |
    73 |
  • 74 |
    75 | 76 | 77 | 78 | NSApplication 79 | 80 |
    81 |
    82 |
    83 |
    84 |
    85 |
    86 | 87 | See more 88 |
    89 |
    90 |

    Declaration

    91 |
    92 |

    Swift

    93 |
    extension NSApplication
    94 | 95 |
    96 |
    97 |
    98 |
    99 |
  • 100 |
  • 101 |
    102 | 103 | 104 | 105 | NSEvent 106 | 107 |
    108 |
    109 |
    110 |
    111 |
    112 |
    113 | 114 | See more 115 |
    116 |
    117 |

    Declaration

    118 |
    119 |

    Swift

    120 |
    extension NSEvent
    121 | 122 |
    123 |
    124 |
    125 |
    126 |
  • 127 |
  • 128 |
    129 | 130 | 131 | 132 | NSMenuItem 133 | 134 |
    135 |
    136 |
    137 |
    138 |
    139 |
    140 | 141 | See more 142 |
    143 |
    144 |

    Declaration

    145 |
    146 |

    Swift

    147 |
    extension NSMenuItem
    148 | 149 |
    150 |
    151 |
    152 |
    153 |
  • 154 |
155 |
156 |
157 |
158 | 162 |
163 |
164 | 165 | 166 | -------------------------------------------------------------------------------- /docs/Extensions/NSApplication.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NSApplication Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

StatusItemController 2.0.1 Docs (100% documented)

21 |

GitHubView on GitHub

22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 | 35 |
36 |
37 | 63 |
64 |
65 |
66 |

NSApplication

67 |
68 |
69 | 70 |
extension NSApplication
71 | 72 |
73 |
74 | 75 |
76 |
77 |
78 |
    79 |
  • 80 |
    81 | 82 | 83 | 84 | isCurrentEventRightClickUp 85 | 86 |
    87 |
    88 |
    89 |
    90 |
    91 |
    92 |

    Returns true if the application’s current event is .rightMouseUp or equivalent. 93 | Returns false otherwise.

    94 | 95 |
    96 |
    97 |

    Declaration

    98 |
    99 |

    Swift

    100 |
    public var isCurrentEventRightClickUp: Bool { get }
    101 | 102 |
    103 |
    104 |
    105 |
    106 |
  • 107 |
108 |
109 |
110 |
111 | 115 |
116 |
117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/Extensions/NSEvent.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NSEvent Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

StatusItemController 2.0.1 Docs (100% documented)

21 |

GitHubView on GitHub

22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 | 35 |
36 |
37 | 63 |
64 |
65 |
66 |

NSEvent

67 |
68 |
69 | 70 |
extension NSEvent
71 | 72 |
73 |
74 | 75 |
76 |
77 |
78 |
    79 |
  • 80 |
    81 | 82 | 83 | 84 | isRightClickUp 85 | 86 |
    87 |
    88 |
    89 |
    90 |
    91 |
    92 |

    Returns true if the event is .rightMouseUp or equivalent. 93 | Returns false otherwise.

    94 | 95 |
    96 |
    97 |

    Declaration

    98 |
    99 |

    Swift

    100 |
    public var isRightClickUp: Bool { get }
    101 | 102 |
    103 |
    104 |
    105 |
    106 |
  • 107 |
108 |
109 |
110 |
111 | 115 |
116 |
117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/Extensions/NSMenuItem.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NSMenuItem Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

StatusItemController 2.0.1 Docs (100% documented)

21 |

GitHubView on GitHub

22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 | 35 |
36 |
37 | 63 |
64 |
65 |
66 |

NSMenuItem

67 |
68 |
69 | 70 |
extension NSMenuItem
71 | 72 |
73 |
74 | 75 |
76 |
77 |
78 |
    79 |
  • 80 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |

    A convenience init for NSMenuItem.

    93 | 94 |
    95 |
    96 |

    Declaration

    97 |
    98 |

    Swift

    99 |
    public convenience init(title: String,
    100 |                         image: NSImage? = nil,
    101 |                         target: AnyObject? = nil,
    102 |                         action: Selector? = nil,
    103 |                         keyEquivalent: String = "",
    104 |                         isEnabled: Bool = true)
    105 | 106 |
    107 |
    108 |
    109 |

    Parameters

    110 | 111 | 112 | 113 | 118 | 123 | 124 | 125 | 130 | 135 | 136 | 137 | 142 | 147 | 148 | 149 | 154 | 159 | 160 | 161 | 166 | 171 | 172 | 173 | 178 | 183 | 184 | 185 |
    114 | 115 | title 116 | 117 | 119 |
    120 |

    The title of the menu item.

    121 |
    122 |
    126 | 127 | image 128 | 129 | 131 |
    132 |

    The image for the menu item.

    133 |
    134 |
    138 | 139 | target 140 | 141 | 143 |
    144 |

    The target to be associated with the menu item.

    145 |
    146 |
    150 | 151 | action 152 | 153 | 155 |
    156 |

    The action selector to be associated with the menu item.

    157 |
    158 |
    162 | 163 | keyEquivalent 164 | 165 | 167 |
    168 |

    A string representing a keyboard key to be used as the key equivalent.

    169 |
    170 |
    174 | 175 | isEnabled 176 | 177 | 179 |
    180 |

    A Boolean value that indicates whether the menu item is enabled.

    181 |
    182 |
    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 198 |
199 |
200 | 201 | 202 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 100% 23 | 24 | 25 | 100% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /*! Jazzy - https://github.com/realm/jazzy 2 | * Copyright Realm Inc. 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | /* Credit to https://gist.github.com/wataru420/2048287 */ 6 | .highlight .c { 7 | color: #999988; 8 | font-style: italic; } 9 | 10 | .highlight .err { 11 | color: #a61717; 12 | background-color: #e3d2d2; } 13 | 14 | .highlight .k { 15 | color: #000000; 16 | font-weight: bold; } 17 | 18 | .highlight .o { 19 | color: #000000; 20 | font-weight: bold; } 21 | 22 | .highlight .cm { 23 | color: #999988; 24 | font-style: italic; } 25 | 26 | .highlight .cp { 27 | color: #999999; 28 | font-weight: bold; } 29 | 30 | .highlight .c1 { 31 | color: #999988; 32 | font-style: italic; } 33 | 34 | .highlight .cs { 35 | color: #999999; 36 | font-weight: bold; 37 | font-style: italic; } 38 | 39 | .highlight .gd { 40 | color: #000000; 41 | background-color: #ffdddd; } 42 | 43 | .highlight .gd .x { 44 | color: #000000; 45 | background-color: #ffaaaa; } 46 | 47 | .highlight .ge { 48 | color: #000000; 49 | font-style: italic; } 50 | 51 | .highlight .gr { 52 | color: #aa0000; } 53 | 54 | .highlight .gh { 55 | color: #999999; } 56 | 57 | .highlight .gi { 58 | color: #000000; 59 | background-color: #ddffdd; } 60 | 61 | .highlight .gi .x { 62 | color: #000000; 63 | background-color: #aaffaa; } 64 | 65 | .highlight .go { 66 | color: #888888; } 67 | 68 | .highlight .gp { 69 | color: #555555; } 70 | 71 | .highlight .gs { 72 | font-weight: bold; } 73 | 74 | .highlight .gu { 75 | color: #aaaaaa; } 76 | 77 | .highlight .gt { 78 | color: #aa0000; } 79 | 80 | .highlight .kc { 81 | color: #000000; 82 | font-weight: bold; } 83 | 84 | .highlight .kd { 85 | color: #000000; 86 | font-weight: bold; } 87 | 88 | .highlight .kp { 89 | color: #000000; 90 | font-weight: bold; } 91 | 92 | .highlight .kr { 93 | color: #000000; 94 | font-weight: bold; } 95 | 96 | .highlight .kt { 97 | color: #445588; } 98 | 99 | .highlight .m { 100 | color: #009999; } 101 | 102 | .highlight .s { 103 | color: #d14; } 104 | 105 | .highlight .na { 106 | color: #008080; } 107 | 108 | .highlight .nb { 109 | color: #0086B3; } 110 | 111 | .highlight .nc { 112 | color: #445588; 113 | font-weight: bold; } 114 | 115 | .highlight .no { 116 | color: #008080; } 117 | 118 | .highlight .ni { 119 | color: #800080; } 120 | 121 | .highlight .ne { 122 | color: #990000; 123 | font-weight: bold; } 124 | 125 | .highlight .nf { 126 | color: #990000; } 127 | 128 | .highlight .nn { 129 | color: #555555; } 130 | 131 | .highlight .nt { 132 | color: #000080; } 133 | 134 | .highlight .nv { 135 | color: #008080; } 136 | 137 | .highlight .ow { 138 | color: #000000; 139 | font-weight: bold; } 140 | 141 | .highlight .w { 142 | color: #bbbbbb; } 143 | 144 | .highlight .mf { 145 | color: #009999; } 146 | 147 | .highlight .mh { 148 | color: #009999; } 149 | 150 | .highlight .mi { 151 | color: #009999; } 152 | 153 | .highlight .mo { 154 | color: #009999; } 155 | 156 | .highlight .sb { 157 | color: #d14; } 158 | 159 | .highlight .sc { 160 | color: #d14; } 161 | 162 | .highlight .sd { 163 | color: #d14; } 164 | 165 | .highlight .s2 { 166 | color: #d14; } 167 | 168 | .highlight .se { 169 | color: #d14; } 170 | 171 | .highlight .sh { 172 | color: #d14; } 173 | 174 | .highlight .si { 175 | color: #d14; } 176 | 177 | .highlight .sx { 178 | color: #d14; } 179 | 180 | .highlight .sr { 181 | color: #009926; } 182 | 183 | .highlight .s1 { 184 | color: #d14; } 185 | 186 | .highlight .ss { 187 | color: #990073; } 188 | 189 | .highlight .bp { 190 | color: #999999; } 191 | 192 | .highlight .vc { 193 | color: #008080; } 194 | 195 | .highlight .vg { 196 | color: #008080; } 197 | 198 | .highlight .vi { 199 | color: #008080; } 200 | 201 | .highlight .il { 202 | color: #009999; } 203 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | /*! Jazzy - https://github.com/realm/jazzy 2 | * Copyright Realm Inc. 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 6 | background: transparent; 7 | border: 0; 8 | margin: 0; 9 | outline: 0; 10 | padding: 0; 11 | vertical-align: baseline; } 12 | 13 | body { 14 | background-color: #f2f2f2; 15 | font-family: Helvetica, freesans, Arial, sans-serif; 16 | font-size: 14px; 17 | -webkit-font-smoothing: subpixel-antialiased; 18 | word-wrap: break-word; } 19 | 20 | h1, h2, h3 { 21 | margin-top: 0.8em; 22 | margin-bottom: 0.3em; 23 | font-weight: 100; 24 | color: black; } 25 | 26 | h1 { 27 | font-size: 2.5em; } 28 | 29 | h2 { 30 | font-size: 2em; 31 | border-bottom: 1px solid #e2e2e2; } 32 | 33 | h4 { 34 | font-size: 13px; 35 | line-height: 1.5; 36 | margin-top: 21px; } 37 | 38 | h5 { 39 | font-size: 1.1em; } 40 | 41 | h6 { 42 | font-size: 1.1em; 43 | color: #777; } 44 | 45 | .section-name { 46 | color: gray; 47 | display: block; 48 | font-family: Helvetica; 49 | font-size: 22px; 50 | font-weight: 100; 51 | margin-bottom: 15px; } 52 | 53 | pre, code { 54 | font: 0.95em Menlo, monospace; 55 | color: #777; 56 | word-wrap: normal; } 57 | 58 | p code, li code { 59 | background-color: #eee; 60 | padding: 2px 4px; 61 | border-radius: 4px; } 62 | 63 | pre > code { 64 | padding: 0; } 65 | 66 | a { 67 | color: #0088cc; 68 | text-decoration: none; } 69 | a code { 70 | color: inherit; } 71 | 72 | ul { 73 | padding-left: 15px; } 74 | 75 | li { 76 | line-height: 1.8em; } 77 | 78 | img { 79 | max-width: 100%; } 80 | 81 | blockquote { 82 | margin-left: 0; 83 | padding: 0 10px; 84 | border-left: 4px solid #ccc; } 85 | 86 | hr { 87 | height: 1px; 88 | border: none; 89 | background-color: #e2e2e2; } 90 | 91 | .footnote-ref { 92 | display: inline-block; 93 | scroll-margin-top: 70px; } 94 | 95 | .footnote-def { 96 | scroll-margin-top: 70px; } 97 | 98 | .content-wrapper { 99 | margin: 0 auto; 100 | width: 980px; } 101 | 102 | header { 103 | font-size: 0.85em; 104 | line-height: 32px; 105 | background-color: #414141; 106 | position: fixed; 107 | width: 100%; 108 | z-index: 3; } 109 | header img { 110 | padding-right: 6px; 111 | vertical-align: -3px; 112 | height: 16px; } 113 | header a { 114 | color: #fff; } 115 | header p { 116 | float: left; 117 | color: #999; } 118 | header .header-right { 119 | float: right; 120 | margin-left: 16px; } 121 | 122 | #breadcrumbs { 123 | background-color: #f2f2f2; 124 | height: 21px; 125 | padding-top: 17px; 126 | position: fixed; 127 | width: 100%; 128 | z-index: 2; 129 | margin-top: 32px; } 130 | #breadcrumbs #carat { 131 | height: 10px; 132 | margin: 0 5px; } 133 | 134 | .sidebar { 135 | background-color: #f9f9f9; 136 | border: 1px solid #e2e2e2; 137 | overflow-y: auto; 138 | overflow-x: hidden; 139 | position: fixed; 140 | top: 70px; 141 | bottom: 0; 142 | width: 230px; 143 | word-wrap: normal; } 144 | 145 | .nav-groups { 146 | list-style-type: none; 147 | background: #fff; 148 | padding-left: 0; } 149 | 150 | .nav-group-name { 151 | border-bottom: 1px solid #e2e2e2; 152 | font-size: 1.1em; 153 | font-weight: 100; 154 | padding: 15px 0 15px 20px; } 155 | .nav-group-name > a { 156 | color: #333; } 157 | 158 | .nav-group-tasks { 159 | margin-top: 5px; } 160 | 161 | .nav-group-task { 162 | font-size: 0.9em; 163 | list-style-type: none; 164 | white-space: nowrap; } 165 | .nav-group-task a { 166 | color: #888; } 167 | 168 | .main-content { 169 | background-color: #fff; 170 | border: 1px solid #e2e2e2; 171 | margin-left: 246px; 172 | position: absolute; 173 | overflow: hidden; 174 | padding-bottom: 20px; 175 | top: 70px; 176 | width: 734px; } 177 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 178 | margin-bottom: 1em; } 179 | .main-content p { 180 | line-height: 1.8em; } 181 | .main-content section .section:first-child { 182 | margin-top: 0; 183 | padding-top: 0; } 184 | .main-content section .task-group-section .task-group:first-of-type { 185 | padding-top: 10px; } 186 | .main-content section .task-group-section .task-group:first-of-type .section-name { 187 | padding-top: 15px; } 188 | .main-content section .heading:before { 189 | content: ""; 190 | display: block; 191 | padding-top: 70px; 192 | margin: -70px 0 0; } 193 | .main-content .section-name p { 194 | margin-bottom: inherit; 195 | line-height: inherit; } 196 | .main-content .section-name code { 197 | background-color: inherit; 198 | padding: inherit; 199 | color: inherit; } 200 | 201 | .section { 202 | padding: 0 25px; } 203 | 204 | .highlight { 205 | background-color: #eee; 206 | padding: 10px 12px; 207 | border: 1px solid #e2e2e2; 208 | border-radius: 4px; 209 | overflow-x: auto; } 210 | 211 | .declaration .highlight { 212 | overflow-x: initial; 213 | padding: 0 40px 40px 0; 214 | margin-bottom: -25px; 215 | background-color: transparent; 216 | border: none; } 217 | 218 | .section-name { 219 | margin: 0; 220 | margin-left: 18px; } 221 | 222 | .task-group-section { 223 | margin-top: 10px; 224 | padding-left: 6px; 225 | border-top: 1px solid #e2e2e2; } 226 | 227 | .task-group { 228 | padding-top: 0px; } 229 | 230 | .task-name-container a[name]:before { 231 | content: ""; 232 | display: block; 233 | padding-top: 70px; 234 | margin: -70px 0 0; } 235 | 236 | .section-name-container { 237 | position: relative; 238 | display: inline-block; } 239 | .section-name-container .section-name-link { 240 | position: absolute; 241 | top: 0; 242 | left: 0; 243 | bottom: 0; 244 | right: 0; 245 | margin-bottom: 0; } 246 | .section-name-container .section-name { 247 | position: relative; 248 | pointer-events: none; 249 | z-index: 1; } 250 | .section-name-container .section-name a { 251 | pointer-events: auto; } 252 | 253 | .item { 254 | padding-top: 8px; 255 | width: 100%; 256 | list-style-type: none; } 257 | .item a[name]:before { 258 | content: ""; 259 | display: block; 260 | padding-top: 70px; 261 | margin: -70px 0 0; } 262 | .item code { 263 | background-color: transparent; 264 | padding: 0; } 265 | .item .token, .item .direct-link { 266 | display: inline-block; 267 | text-indent: -20px; 268 | padding-left: 3px; 269 | margin-left: 35px; 270 | font-size: 11.9px; 271 | transition: all 300ms; } 272 | .item .token-open { 273 | margin-left: 20px; } 274 | .item .discouraged { 275 | text-decoration: line-through; } 276 | .item .declaration-note { 277 | font-size: .85em; 278 | color: gray; 279 | font-style: italic; } 280 | 281 | .pointer-container { 282 | border-bottom: 1px solid #e2e2e2; 283 | left: -23px; 284 | padding-bottom: 13px; 285 | position: relative; 286 | width: 110%; } 287 | 288 | .pointer { 289 | background: #f9f9f9; 290 | border-left: 1px solid #e2e2e2; 291 | border-top: 1px solid #e2e2e2; 292 | height: 12px; 293 | left: 21px; 294 | top: -7px; 295 | -webkit-transform: rotate(45deg); 296 | -moz-transform: rotate(45deg); 297 | -o-transform: rotate(45deg); 298 | transform: rotate(45deg); 299 | position: absolute; 300 | width: 12px; } 301 | 302 | .height-container { 303 | display: none; 304 | left: -25px; 305 | padding: 0 25px; 306 | position: relative; 307 | width: 100%; 308 | overflow: hidden; } 309 | .height-container .section { 310 | background: #f9f9f9; 311 | border-bottom: 1px solid #e2e2e2; 312 | left: -25px; 313 | position: relative; 314 | width: 100%; 315 | padding-top: 10px; 316 | padding-bottom: 5px; } 317 | 318 | .aside, .language { 319 | padding: 6px 12px; 320 | margin: 12px 0; 321 | border-left: 5px solid #dddddd; 322 | overflow-y: hidden; } 323 | .aside .aside-title, .language .aside-title { 324 | font-size: 9px; 325 | letter-spacing: 2px; 326 | text-transform: uppercase; 327 | padding-bottom: 0; 328 | margin: 0; 329 | color: #aaa; 330 | -webkit-user-select: none; } 331 | .aside p:last-child, .language p:last-child { 332 | margin-bottom: 0; } 333 | 334 | .language { 335 | border-left: 5px solid #cde9f4; } 336 | .language .aside-title { 337 | color: #4b8afb; } 338 | 339 | .aside-warning, .aside-deprecated, .aside-unavailable { 340 | border-left: 5px solid #ff6666; } 341 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 342 | color: #ff0000; } 343 | 344 | .graybox { 345 | border-collapse: collapse; 346 | width: 100%; } 347 | .graybox p { 348 | margin: 0; 349 | word-break: break-word; 350 | min-width: 50px; } 351 | .graybox td { 352 | border: 1px solid #e2e2e2; 353 | padding: 5px 25px 5px 10px; 354 | vertical-align: middle; } 355 | .graybox tr td:first-of-type { 356 | text-align: right; 357 | padding: 7px; 358 | vertical-align: top; 359 | word-break: normal; 360 | width: 40px; } 361 | 362 | .slightly-smaller { 363 | font-size: 0.9em; } 364 | 365 | #footer { 366 | position: relative; 367 | top: 10px; 368 | bottom: 0px; 369 | margin-left: 25px; } 370 | #footer p { 371 | margin: 0; 372 | color: #aaa; 373 | font-size: 0.8em; } 374 | 375 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 376 | display: none; } 377 | 378 | html.dash .main-content { 379 | width: 980px; 380 | margin-left: 0; 381 | border: none; 382 | width: 100%; 383 | top: 0; 384 | padding-bottom: 0; } 385 | 386 | html.dash .height-container { 387 | display: block; } 388 | 389 | html.dash .item .token { 390 | margin-left: 0; } 391 | 392 | html.dash .content-wrapper { 393 | width: auto; } 394 | 395 | html.dash #footer { 396 | position: static; } 397 | 398 | form[role=search] { 399 | float: right; } 400 | form[role=search] input { 401 | font: Helvetica, freesans, Arial, sans-serif; 402 | margin-top: 6px; 403 | font-size: 13px; 404 | line-height: 20px; 405 | padding: 0px 10px; 406 | border: none; 407 | border-radius: 1em; } 408 | .loading form[role=search] input { 409 | background: white url(../img/spinner.gif) center right 4px no-repeat; } 410 | form[role=search] .tt-menu { 411 | margin: 0; 412 | min-width: 300px; 413 | background: #fff; 414 | color: #333; 415 | border: 1px solid #e2e2e2; 416 | z-index: 4; } 417 | form[role=search] .tt-highlight { 418 | font-weight: bold; } 419 | form[role=search] .tt-suggestion { 420 | font: Helvetica, freesans, Arial, sans-serif; 421 | font-size: 14px; 422 | padding: 0 8px; } 423 | form[role=search] .tt-suggestion span { 424 | display: table-cell; 425 | white-space: nowrap; } 426 | form[role=search] .tt-suggestion .doc-parent-name { 427 | width: 100%; 428 | text-align: right; 429 | font-weight: normal; 430 | font-size: 0.9em; 431 | padding-left: 16px; } 432 | form[role=search] .tt-suggestion:hover, 433 | form[role=search] .tt-suggestion.tt-cursor { 434 | cursor: pointer; 435 | background-color: #4183c4; 436 | color: #fff; } 437 | form[role=search] .tt-suggestion:hover .doc-parent-name, 438 | form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { 439 | color: #fff; } 440 | -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexedbits/StatusItemController/bbded09a286590564dbf9cf8f9f5e5ffbd5e8fab/docs/img/spinner.gif -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | StatusItemController Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |

StatusItemController 2.0.1 Docs (100% documented)

20 |

GitHubView on GitHub

21 |
22 |
23 | 24 |
25 |
26 |
27 |
28 |
29 | 34 |
35 |
36 | 62 |
63 |
64 |
65 | 66 |

StatusItemController CI

67 | 68 |

A “view controller” for menu bar Mac apps

69 | 70 |


71 |

About

72 | 73 |

This library provides a StatusItemController component that you can use to create menu bar apps, or apps with menu bar items in macOS.

74 | 75 |

This component is used in Red Eye and Lucifer.

76 |

Usage

77 | 78 |
    79 |
  1. Subclass StatusItemController
  2. 80 |
  3. Implement the following methods: 81 | 82 |
      83 |
    1. buildMenu() -> NSMenu
    2. 84 |
    3. leftClickAction()
    4. 85 |
    5. rightClickAction()
    6. 86 |
  4. 87 |
  5. Create an instance of your StatusItemController subclass in your NSApplicationDelegate.
  6. 88 |
89 |

Requirements

90 | 91 |
    92 |
  • macOS 11.0+
  • 93 |
  • Swift 5.9+
  • 94 |
  • Xcode 15.0+
  • 95 |
  • SwiftLint
  • 96 |
97 |

Installation

98 |

CocoaPods

99 |
pod 'StatusItemController', '~> 2.0.0'
100 | 
101 |

Swift Package Manager

102 | 103 |

Add StatusItemController to the dependencies value of your Package.swift.

104 |
dependencies: [
105 |     .package(url: "https://github.com/hexedbits/StatusItemController", from: "2.0.0")
106 | ]
107 | 
108 | 109 |

Alternatively, you can add the package directly via Xcode.

110 |

Documentation

111 | 112 |

You can read the documentation here. Generated with jazzy. Hosted by GitHub Pages.

113 |

Notes on Testing

114 | 115 |

Unfortunately, StatusItemController cannot be tested directly. Attempting to create an NSStatusItem outside of an app context throws an assert, which makes sense. Thus, in order to test StatusItemController it must be embedded in an app. Tests can be found in the Example App test suite. See #15 for more details.

116 |

Contributing

117 | 118 |

Interested in making contributions to this project? Please review the guides below.

119 | 120 | 126 | 127 |

Also consider sponsoring this project or buying my apps! ✌️

128 |

Credits

129 | 130 |

Created and maintained by Jesse Squires.

131 |

License

132 | 133 |

Released under the MIT License. See LICENSE for details.

134 | 135 |
136 |

Copyright © 2020-present Jesse Squires.

137 |
138 | 139 |
140 |
141 | 145 |
146 |
147 | 148 | 149 | -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | // Jazzy - https://github.com/realm/jazzy 2 | // Copyright Realm Inc. 3 | // SPDX-License-Identifier: MIT 4 | 5 | window.jazzy = {'docset': false} 6 | if (typeof window.dash != 'undefined') { 7 | document.documentElement.className += ' dash' 8 | window.jazzy.docset = true 9 | } 10 | if (navigator.userAgent.match(/xcode/i)) { 11 | document.documentElement.className += ' xcode' 12 | window.jazzy.docset = true 13 | } 14 | 15 | function toggleItem($link, $content) { 16 | var animationDuration = 300; 17 | $link.toggleClass('token-open'); 18 | $content.slideToggle(animationDuration); 19 | } 20 | 21 | function itemLinkToContent($link) { 22 | return $link.parent().parent().next(); 23 | } 24 | 25 | // On doc load + hash-change, open any targetted item 26 | function openCurrentItemIfClosed() { 27 | if (window.jazzy.docset) { 28 | return; 29 | } 30 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 31 | $content = itemLinkToContent($link); 32 | if ($content.is(':hidden')) { 33 | toggleItem($link, $content); 34 | } 35 | } 36 | 37 | $(openCurrentItemIfClosed); 38 | $(window).on('hashchange', openCurrentItemIfClosed); 39 | 40 | // On item link ('token') click, toggle its discussion 41 | $('.token').on('click', function(event) { 42 | if (window.jazzy.docset) { 43 | return; 44 | } 45 | var $link = $(this); 46 | toggleItem($link, itemLinkToContent($link)); 47 | 48 | // Keeps the document from jumping to the hash. 49 | var href = $link.attr('href'); 50 | if (history.pushState) { 51 | history.pushState({}, '', href); 52 | } else { 53 | location.hash = href; 54 | } 55 | event.preventDefault(); 56 | }); 57 | 58 | // Clicks on links to the current, closed, item need to open the item 59 | $("a:not('.token')").on('click', function() { 60 | if (location == this.href) { 61 | openCurrentItemIfClosed(); 62 | } 63 | }); 64 | 65 | // KaTeX rendering 66 | if ("katex" in window) { 67 | $($('.math').each( (_, element) => { 68 | katex.render(element.textContent, element, { 69 | displayMode: $(element).hasClass('m-block'), 70 | throwOnError: false, 71 | trust: true 72 | }); 73 | })) 74 | } 75 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | // Jazzy - https://github.com/realm/jazzy 2 | // Copyright Realm Inc. 3 | // SPDX-License-Identifier: MIT 4 | 5 | $(function(){ 6 | var $typeahead = $('[data-typeahead]'); 7 | var $form = $typeahead.parents('form'); 8 | var searchURL = $form.attr('action'); 9 | 10 | function displayTemplate(result) { 11 | return result.name; 12 | } 13 | 14 | function suggestionTemplate(result) { 15 | var t = '
'; 16 | t += '' + result.name + ''; 17 | if (result.parent_name) { 18 | t += '' + result.parent_name + ''; 19 | } 20 | t += '
'; 21 | return t; 22 | } 23 | 24 | $typeahead.one('focus', function() { 25 | $form.addClass('loading'); 26 | 27 | $.getJSON(searchURL).then(function(searchData) { 28 | const searchIndex = lunr(function() { 29 | this.ref('url'); 30 | this.field('name'); 31 | this.field('abstract'); 32 | for (const [url, doc] of Object.entries(searchData)) { 33 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 34 | } 35 | }); 36 | 37 | $typeahead.typeahead( 38 | { 39 | highlight: true, 40 | minLength: 3, 41 | autoselect: true 42 | }, 43 | { 44 | limit: 10, 45 | display: displayTemplate, 46 | templates: { suggestion: suggestionTemplate }, 47 | source: function(query, sync) { 48 | const lcSearch = query.toLowerCase(); 49 | const results = searchIndex.query(function(q) { 50 | q.term(lcSearch, { boost: 100 }); 51 | q.term(lcSearch, { 52 | boost: 10, 53 | wildcard: lunr.Query.wildcard.TRAILING 54 | }); 55 | }).map(function(result) { 56 | var doc = searchData[result.ref]; 57 | doc.url = result.ref; 58 | return doc; 59 | }); 60 | sync(results); 61 | } 62 | } 63 | ); 64 | $form.removeClass('loading'); 65 | $typeahead.trigger('focus'); 66 | }); 67 | }); 68 | 69 | var baseURL = searchURL.slice(0, -"search.json".length); 70 | 71 | $typeahead.on('typeahead:select', function(e, result) { 72 | window.location = baseURL + result.url; 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /docs/js/lunr.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 3 | * Copyright (C) 2020 Oliver Nightingale 4 | * @license MIT 5 | */ 6 | !function(){var e=function(t){var r=new e.Builder;return r.pipeline.add(e.trimmer,e.stopWordFilter,e.stemmer),r.searchPipeline.add(e.stemmer),t.call(r,r),r.build()};e.version="2.3.9",e.utils={},e.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),e.utils.asString=function(e){return void 0===e||null===e?"":e.toString()},e.utils.clone=function(e){if(null===e||void 0===e)return e;for(var t=Object.create(null),r=Object.keys(e),i=0;i0){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); 7 | -------------------------------------------------------------------------------- /docs/search.json: -------------------------------------------------------------------------------- 1 | {"Extensions/NSMenuItem.html#/s:So10NSMenuItemC06StatusB10ControllerE5title5image6target6action13keyEquivalent9isEnabledABSS_So7NSImageCSgyXlSg10ObjectiveC8SelectorVSgSSSbtcfc":{"name":"init(title:image:target:action:keyEquivalent:isEnabled:)","abstract":"\u003cp\u003eA convenience init for \u003ccode\u003eNSMenuItem\u003c/code\u003e.\u003c/p\u003e","parent_name":"NSMenuItem"},"Extensions/NSEvent.html#/s:So7NSEventC20StatusItemControllerE14isRightClickUpSbvp":{"name":"isRightClickUp","abstract":"\u003cp\u003eReturns \u003ccode\u003etrue\u003c/code\u003e if the event is \u003ccode\u003e.rightMouseUp\u003c/code\u003e or equivalent.","parent_name":"NSEvent"},"Extensions/NSApplication.html#/s:So13NSApplicationC20StatusItemControllerE26isCurrentEventRightClickUpSbvp":{"name":"isCurrentEventRightClickUp","abstract":"\u003cp\u003eReturns \u003ccode\u003etrue\u003c/code\u003e if the application\u0026rsquo;s current event is \u003ccode\u003e.rightMouseUp\u003c/code\u003e or equivalent.","parent_name":"NSApplication"},"Extensions/NSApplication.html":{"name":"NSApplication"},"Extensions/NSEvent.html":{"name":"NSEvent"},"Extensions/NSMenuItem.html":{"name":"NSMenuItem"},"Classes/StatusItemController.html#/s:20StatusItemControllerAAC06statusB0So08NSStatusB0Cvp":{"name":"statusItem","abstract":"\u003cp\u003eThe status item.\u003c/p\u003e","parent_name":"StatusItemController"},"Classes/StatusItemController.html#/s:20StatusItemControllerAAC5image6lengthABSo7NSImageC_14CoreFoundation7CGFloatVtcfc":{"name":"init(image:length:)","abstract":"\u003cp\u003eCreates a new \u003ccode\u003eStatusItemController\u003c/code\u003e.\u003c/p\u003e","parent_name":"StatusItemController"},"Classes/StatusItemController.html#/s:20StatusItemControllerAAC9buildMenuSo6NSMenuCyF":{"name":"buildMenu()","abstract":"\u003cp\u003eConstructs an \u003ccode\u003eNSMenu\u003c/code\u003e to display for the status item.\u003c/p\u003e","parent_name":"StatusItemController"},"Classes/StatusItemController.html#/s:20StatusItemControllerAAC15leftClickActionyyF":{"name":"leftClickAction()","abstract":"\u003cp\u003eThe action to be executed on the \u003ccode\u003e.leftMouseDown\u003c/code\u003e event.\u003c/p\u003e","parent_name":"StatusItemController"},"Classes/StatusItemController.html#/s:20StatusItemControllerAAC16rightClickActionyyF":{"name":"rightClickAction()","abstract":"\u003cp\u003eThe action to be executed on \u003ccode\u003e.rightMouseUp\u003c/code\u003e event.\u003c/p\u003e","parent_name":"StatusItemController"},"Classes/StatusItemController.html#/s:20StatusItemControllerAAC8openMenuyyF":{"name":"openMenu()","abstract":"\u003cp\u003eOpens the status item menu.","parent_name":"StatusItemController"},"Classes/StatusItemController.html#/s:20StatusItemControllerAAC8hideMenuyyF":{"name":"hideMenu()","abstract":"\u003cp\u003eHides the status item menu.\u003c/p\u003e","parent_name":"StatusItemController"},"Classes/StatusItemController.html#/c:@M@StatusItemController@objc(cs)StatusItemController(im)quit":{"name":"quit()","abstract":"\u003cp\u003eQuits the application.","parent_name":"StatusItemController"},"Classes/StatusItemController.html":{"name":"StatusItemController","abstract":"\u003cp\u003eController for an \u003ccode\u003eNSStatusItem\u003c/code\u003e. Designed to be subclassed.\u003c/p\u003e"},"Classes.html":{"name":"Classes","abstract":"\u003cp\u003eThe following classes are available globally.\u003c/p\u003e"},"Extensions.html":{"name":"Extensions","abstract":"\u003cp\u003eThe following extensions are available globally.\u003c/p\u003e"}} -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/jsq/Developer/GitHub/StatusItemController" 6 | } -------------------------------------------------------------------------------- /scripts/build_docs.zsh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # Created by Jesse Squires 4 | # https://www.jessesquires.com 5 | # 6 | # Copyright © 2020-present Jesse Squires 7 | # 8 | # Jazzy: https://github.com/realm/jazzy/releases/latest 9 | # Generates documentation using jazzy and checks for installation. 10 | 11 | VERSION="0.14.4" 12 | 13 | FOUND=$(jazzy --version) 14 | LINK="https://github.com/realm/jazzy" 15 | INSTALL="gem install jazzy" 16 | 17 | PROJECT="StatusItemController" 18 | 19 | if which jazzy >/dev/null; then 20 | jazzy \ 21 | --clean \ 22 | --author "Jesse Squires" \ 23 | --author_url "https://jessesquires.com" \ 24 | --github_url "https://github.com/hexedbits/$PROJECT" \ 25 | --module "$PROJECT" \ 26 | --source-directory . \ 27 | --readme "README.md" \ 28 | --documentation "Guides/*.md" \ 29 | --output docs/ 30 | else 31 | echo " 32 | Error: Jazzy not installed! 33 | 34 | Download: $LINK 35 | Install: $INSTALL 36 | " 37 | exit 1 38 | fi 39 | 40 | if [ "$FOUND" != "jazzy version: $VERSION" ]; then 41 | echo " 42 | Warning: incorrect Jazzy installed! Please upgrade. 43 | Expected: $VERSION 44 | Found: $FOUND 45 | 46 | Download: $LINK 47 | Install: $INSTALL 48 | " 49 | fi 50 | 51 | exit 52 | -------------------------------------------------------------------------------- /scripts/lint.zsh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # Created by Jesse Squires 4 | # https://www.jessesquires.com 5 | # 6 | # Copyright © 2020-present Jesse Squires 7 | # 8 | # SwiftLint: https://github.com/realm/SwiftLint/releases/latest 9 | # 10 | # Runs SwiftLint and checks for installation of correct version. 11 | 12 | set -e 13 | export PATH="$PATH:/opt/homebrew/bin" 14 | 15 | PROJECT="StatusItemController.xcodeproj" 16 | SCHEME="StatusItemController" 17 | 18 | VERSION="0.54.0" 19 | 20 | FOUND=$(swiftlint version) 21 | LINK="https://github.com/realm/SwiftLint" 22 | INSTALL="brew install swiftlint" 23 | 24 | CONFIG="./.swiftlint.yml" 25 | 26 | if which swiftlint >/dev/null; then 27 | echo "Running swiftlint..." 28 | echo "" 29 | 30 | # no arguments, just lint without fixing 31 | if [[ $# -eq 0 ]]; then 32 | swiftlint --config $CONFIG 33 | echo "" 34 | fi 35 | 36 | for argval in "$@" 37 | do 38 | # run --fix 39 | if [[ "$argval" == "fix" ]]; then 40 | echo "Auto-correcting lint errors..." 41 | echo "" 42 | swiftlint --fix --progress --config $CONFIG && swiftlint --config $CONFIG 43 | echo "" 44 | # run analyze 45 | elif [[ "$argval" == "analyze" ]]; then 46 | LOG="xcodebuild.log" 47 | echo "Running anaylze..." 48 | echo "" 49 | xcodebuild -scheme $SCHEME -project $PROJECT clean build-for-testing > $LOG 50 | swiftlint analyze --fix --progress --format --strict --config $CONFIG --compiler-log-path $LOG 51 | rm $LOG 52 | echo "" 53 | else 54 | echo "Error: invalid arguments." 55 | echo "Usage: $0 [fix] [analyze]" 56 | echo "" 57 | fi 58 | done 59 | else 60 | echo " 61 | Error: SwiftLint not installed! 62 | 63 | Download: $LINK 64 | Install: $INSTALL 65 | " 66 | fi 67 | 68 | if [ $FOUND != $VERSION ]; then 69 | echo " 70 | Warning: incorrect SwiftLint installed! Please upgrade. 71 | Expected: $VERSION 72 | Found: $FOUND 73 | 74 | Download: $LINK 75 | Install: $INSTALL 76 | " 77 | fi 78 | 79 | exit 80 | --------------------------------------------------------------------------------