├── .gitignore ├── LICENSE ├── README.md ├── To-do Bar.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── To-do Bar ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── 1024.png │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 256-1.png │ │ ├── 256.png │ │ ├── 32-1.png │ │ ├── 32.png │ │ ├── 512-1.png │ │ ├── 512.png │ │ ├── 64.png │ │ └── Contents.json │ ├── BarIcon.imageset │ │ ├── 20.png │ │ ├── 40.png │ │ ├── 60.png │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── Info.plist ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json └── To_do_Bar.entitlements ├── To-do BarTests ├── Info.plist └── To_do_BarTests.swift ├── To-do BarUITests ├── Info.plist └── To_do_BarUITests.swift └── img ├── To-do Bar.icns ├── To-do Bar.sketch ├── screen1.png ├── screen1_v1.png ├── screen2.png ├── screen2_v2.png ├── tutorial.gif ├── tutorial.mp4 ├── tutorial_v1.gif └── tutorial_v1.mp4 /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Created by https://www.gitignore.io/api/macos,swift 4 | # Edit at https://www.gitignore.io/?templates=macos,swift 5 | 6 | ### macOS ### 7 | # General 8 | .DS_Store 9 | .AppleDouble 10 | .LSOverride 11 | 12 | # Icon must end with two \r 13 | Icon 14 | 15 | # Thumbnails 16 | ._* 17 | 18 | # Files that might appear in the root of a volume 19 | .DocumentRevisions-V100 20 | .fseventsd 21 | .Spotlight-V100 22 | .TemporaryItems 23 | .Trashes 24 | .VolumeIcon.icns 25 | .com.apple.timemachine.donotpresent 26 | 27 | # Directories potentially created on remote AFP share 28 | .AppleDB 29 | .AppleDesktop 30 | Network Trash Folder 31 | Temporary Items 32 | .apdisk 33 | 34 | ### Swift ### 35 | # Xcode 36 | # 37 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 38 | 39 | ## Build generated 40 | build/ 41 | DerivedData/ 42 | 43 | ## Various settings 44 | *.pbxuser 45 | !default.pbxuser 46 | *.mode1v3 47 | !default.mode1v3 48 | *.mode2v3 49 | !default.mode2v3 50 | *.perspectivev3 51 | !default.perspectivev3 52 | xcuserdata/ 53 | 54 | ## Other 55 | *.moved-aside 56 | *.xccheckout 57 | *.xcscmblueprint 58 | 59 | ## Obj-C/Swift specific 60 | *.hmap 61 | *.ipa 62 | *.dSYM.zip 63 | *.dSYM 64 | 65 | ## Playgrounds 66 | timeline.xctimeline 67 | playground.xcworkspace 68 | 69 | # Swift Package Manager 70 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 71 | # Packages/ 72 | # Package.pins 73 | # Package.resolved 74 | .build/ 75 | 76 | # CocoaPods 77 | # We recommend against adding the Pods directory to your .gitignore. However 78 | # you should judge for yourself, the pros and cons are mentioned at: 79 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 80 | # Pods/ 81 | # Add this line if you want to avoid checking in source code from the Xcode workspace 82 | # *.xcworkspace 83 | 84 | # Carthage 85 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 86 | # Carthage/Checkouts 87 | 88 | Carthage/Build 89 | 90 | # fastlane 91 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 92 | # screenshots whenever they are needed. 93 | # For more information about the recommended setup visit: 94 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 95 | 96 | fastlane/report.xml 97 | fastlane/Preview.html 98 | fastlane/screenshots/**/*.png 99 | fastlane/test_output 100 | 101 | # Code Injection 102 | # After new code Injection tools there's a generated folder /iOSInjectionProject 103 | # https://github.com/johnno1962/injectionforxcode 104 | 105 | iOSInjectionProject/ 106 | 107 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 108 | *.xcscmblueprint 109 | *.xccheckout 110 | 111 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 112 | build/ 113 | DerivedData/ 114 | *.moved-aside 115 | *.pbxuser 116 | !default.pbxuser 117 | *.mode1v3 118 | !default.mode1v3 119 | *.mode2v3 120 | !default.mode2v3 121 | *.perspectivev3 122 | !default.perspectivev3 123 | 124 | ### Xcode Patch ### 125 | /*.gcno 126 | **/xcshareddata/WorkspaceSettings.xcsettings 127 | 128 | # End of https://www.gitignore.io/api/macos,swift 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Zhexiong Liu 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | download 7 | 8 | platform 9 | 10 | 11 | systemrequirements 12 | 13 |

14 | 15 | ## To-do Bar 16 | To-do Bar empowers [MacOS](https://www.apple.com/macos/) [Reminders](https://support.apple.com/guide/reminders/)/[Calendar](https://support.apple.com/guide/calendar/), allowing todo tasks available in menu bar. 17 | 18 |

19 | 20 | 21 |

22 | 23 | 24 | ## Manual download 25 | 26 | * [Download latest version](https://github.com/ZhexiongLiu/TodoBar/releases/latest) 27 | * Open and drag the app to the Applications folder. 28 | 29 | ## Usage 30 | 31 | * Grant To-do Bar access to your MacOS Reminders/Calenders. 32 | * Click To-do Bar menu icon to show your todo list. 33 | * Click your reminder items to mark as completed. 34 | * Calendar items would be hidden when time passed. 35 | 36 |

37 | 38 |

39 | 40 | ## Related App 41 | [Ticker Bar](https://github.com/ZhexiongLiu/TickerBar) 42 | 43 | ## License 44 | 45 | MIT © [Zhexiong Liu](https://github.com/ZhexiongLiu) 46 | -------------------------------------------------------------------------------- /To-do Bar.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 52C2B32D240C46BB006E217F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C2B32C240C46BB006E217F /* AppDelegate.swift */; }; 11 | 52C2B331240C46BC006E217F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52C2B330240C46BC006E217F /* Assets.xcassets */; }; 12 | 52C2B334240C46BC006E217F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52C2B333240C46BC006E217F /* Preview Assets.xcassets */; }; 13 | 52C2B337240C46BC006E217F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52C2B335240C46BC006E217F /* Main.storyboard */; }; 14 | 52C2B343240C46BC006E217F /* To_do_BarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C2B342240C46BC006E217F /* To_do_BarTests.swift */; }; 15 | 52C2B34E240C46BC006E217F /* To_do_BarUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C2B34D240C46BC006E217F /* To_do_BarUITests.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXContainerItemProxy section */ 19 | 52C2B33F240C46BC006E217F /* PBXContainerItemProxy */ = { 20 | isa = PBXContainerItemProxy; 21 | containerPortal = 52C2B321240C46BB006E217F /* Project object */; 22 | proxyType = 1; 23 | remoteGlobalIDString = 52C2B328240C46BB006E217F; 24 | remoteInfo = "To-do Bar"; 25 | }; 26 | 52C2B34A240C46BC006E217F /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 52C2B321240C46BB006E217F /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = 52C2B328240C46BB006E217F; 31 | remoteInfo = "To-do Bar"; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 52C2B329240C46BB006E217F /* To-do Bar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "To-do Bar.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 52C2B32C240C46BB006E217F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 38 | 52C2B330240C46BC006E217F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 39 | 52C2B333240C46BC006E217F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 40 | 52C2B336240C46BC006E217F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 41 | 52C2B338240C46BC006E217F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | 52C2B339240C46BC006E217F /* To_do_Bar.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = To_do_Bar.entitlements; sourceTree = ""; }; 43 | 52C2B33E240C46BC006E217F /* To-do BarTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "To-do BarTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 52C2B342240C46BC006E217F /* To_do_BarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = To_do_BarTests.swift; sourceTree = ""; }; 45 | 52C2B344240C46BC006E217F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | 52C2B349240C46BC006E217F /* To-do BarUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "To-do BarUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 52C2B34D240C46BC006E217F /* To_do_BarUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = To_do_BarUITests.swift; sourceTree = ""; }; 48 | 52C2B34F240C46BC006E217F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | 52C2B326240C46BB006E217F /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | 52C2B33B240C46BC006E217F /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | 52C2B346240C46BC006E217F /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | /* End PBXFrameworksBuildPhase section */ 74 | 75 | /* Begin PBXGroup section */ 76 | 52C2B320240C46BB006E217F = { 77 | isa = PBXGroup; 78 | children = ( 79 | 52C2B32B240C46BB006E217F /* To-do Bar */, 80 | 52C2B341240C46BC006E217F /* To-do BarTests */, 81 | 52C2B34C240C46BC006E217F /* To-do BarUITests */, 82 | 52C2B32A240C46BB006E217F /* Products */, 83 | ); 84 | sourceTree = ""; 85 | }; 86 | 52C2B32A240C46BB006E217F /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 52C2B329240C46BB006E217F /* To-do Bar.app */, 90 | 52C2B33E240C46BC006E217F /* To-do BarTests.xctest */, 91 | 52C2B349240C46BC006E217F /* To-do BarUITests.xctest */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | 52C2B32B240C46BB006E217F /* To-do Bar */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 52C2B32C240C46BB006E217F /* AppDelegate.swift */, 100 | 52C2B330240C46BC006E217F /* Assets.xcassets */, 101 | 52C2B335240C46BC006E217F /* Main.storyboard */, 102 | 52C2B338240C46BC006E217F /* Info.plist */, 103 | 52C2B339240C46BC006E217F /* To_do_Bar.entitlements */, 104 | 52C2B332240C46BC006E217F /* Preview Content */, 105 | ); 106 | path = "To-do Bar"; 107 | sourceTree = ""; 108 | }; 109 | 52C2B332240C46BC006E217F /* Preview Content */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 52C2B333240C46BC006E217F /* Preview Assets.xcassets */, 113 | ); 114 | path = "Preview Content"; 115 | sourceTree = ""; 116 | }; 117 | 52C2B341240C46BC006E217F /* To-do BarTests */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 52C2B342240C46BC006E217F /* To_do_BarTests.swift */, 121 | 52C2B344240C46BC006E217F /* Info.plist */, 122 | ); 123 | path = "To-do BarTests"; 124 | sourceTree = ""; 125 | }; 126 | 52C2B34C240C46BC006E217F /* To-do BarUITests */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 52C2B34D240C46BC006E217F /* To_do_BarUITests.swift */, 130 | 52C2B34F240C46BC006E217F /* Info.plist */, 131 | ); 132 | path = "To-do BarUITests"; 133 | sourceTree = ""; 134 | }; 135 | /* End PBXGroup section */ 136 | 137 | /* Begin PBXNativeTarget section */ 138 | 52C2B328240C46BB006E217F /* To-do Bar */ = { 139 | isa = PBXNativeTarget; 140 | buildConfigurationList = 52C2B352240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do Bar" */; 141 | buildPhases = ( 142 | 52C2B325240C46BB006E217F /* Sources */, 143 | 52C2B326240C46BB006E217F /* Frameworks */, 144 | 52C2B327240C46BB006E217F /* Resources */, 145 | ); 146 | buildRules = ( 147 | ); 148 | dependencies = ( 149 | ); 150 | name = "To-do Bar"; 151 | productName = "To-do Bar"; 152 | productReference = 52C2B329240C46BB006E217F /* To-do Bar.app */; 153 | productType = "com.apple.product-type.application"; 154 | }; 155 | 52C2B33D240C46BC006E217F /* To-do BarTests */ = { 156 | isa = PBXNativeTarget; 157 | buildConfigurationList = 52C2B355240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do BarTests" */; 158 | buildPhases = ( 159 | 52C2B33A240C46BC006E217F /* Sources */, 160 | 52C2B33B240C46BC006E217F /* Frameworks */, 161 | 52C2B33C240C46BC006E217F /* Resources */, 162 | ); 163 | buildRules = ( 164 | ); 165 | dependencies = ( 166 | 52C2B340240C46BC006E217F /* PBXTargetDependency */, 167 | ); 168 | name = "To-do BarTests"; 169 | productName = "To-do BarTests"; 170 | productReference = 52C2B33E240C46BC006E217F /* To-do BarTests.xctest */; 171 | productType = "com.apple.product-type.bundle.unit-test"; 172 | }; 173 | 52C2B348240C46BC006E217F /* To-do BarUITests */ = { 174 | isa = PBXNativeTarget; 175 | buildConfigurationList = 52C2B358240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do BarUITests" */; 176 | buildPhases = ( 177 | 52C2B345240C46BC006E217F /* Sources */, 178 | 52C2B346240C46BC006E217F /* Frameworks */, 179 | 52C2B347240C46BC006E217F /* Resources */, 180 | ); 181 | buildRules = ( 182 | ); 183 | dependencies = ( 184 | 52C2B34B240C46BC006E217F /* PBXTargetDependency */, 185 | ); 186 | name = "To-do BarUITests"; 187 | productName = "To-do BarUITests"; 188 | productReference = 52C2B349240C46BC006E217F /* To-do BarUITests.xctest */; 189 | productType = "com.apple.product-type.bundle.ui-testing"; 190 | }; 191 | /* End PBXNativeTarget section */ 192 | 193 | /* Begin PBXProject section */ 194 | 52C2B321240C46BB006E217F /* Project object */ = { 195 | isa = PBXProject; 196 | attributes = { 197 | LastSwiftUpdateCheck = 1130; 198 | LastUpgradeCheck = 1130; 199 | ORGANIZATIONNAME = "Zhexiong Liu"; 200 | TargetAttributes = { 201 | 52C2B328240C46BB006E217F = { 202 | CreatedOnToolsVersion = 11.3.1; 203 | }; 204 | 52C2B33D240C46BC006E217F = { 205 | CreatedOnToolsVersion = 11.3.1; 206 | TestTargetID = 52C2B328240C46BB006E217F; 207 | }; 208 | 52C2B348240C46BC006E217F = { 209 | CreatedOnToolsVersion = 11.3.1; 210 | TestTargetID = 52C2B328240C46BB006E217F; 211 | }; 212 | }; 213 | }; 214 | buildConfigurationList = 52C2B324240C46BB006E217F /* Build configuration list for PBXProject "To-do Bar" */; 215 | compatibilityVersion = "Xcode 9.3"; 216 | developmentRegion = en; 217 | hasScannedForEncodings = 0; 218 | knownRegions = ( 219 | en, 220 | Base, 221 | ); 222 | mainGroup = 52C2B320240C46BB006E217F; 223 | productRefGroup = 52C2B32A240C46BB006E217F /* Products */; 224 | projectDirPath = ""; 225 | projectRoot = ""; 226 | targets = ( 227 | 52C2B328240C46BB006E217F /* To-do Bar */, 228 | 52C2B33D240C46BC006E217F /* To-do BarTests */, 229 | 52C2B348240C46BC006E217F /* To-do BarUITests */, 230 | ); 231 | }; 232 | /* End PBXProject section */ 233 | 234 | /* Begin PBXResourcesBuildPhase section */ 235 | 52C2B327240C46BB006E217F /* Resources */ = { 236 | isa = PBXResourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | 52C2B337240C46BC006E217F /* Main.storyboard in Resources */, 240 | 52C2B334240C46BC006E217F /* Preview Assets.xcassets in Resources */, 241 | 52C2B331240C46BC006E217F /* Assets.xcassets in Resources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | 52C2B33C240C46BC006E217F /* Resources */ = { 246 | isa = PBXResourcesBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | 52C2B347240C46BC006E217F /* Resources */ = { 253 | isa = PBXResourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | }; 259 | /* End PBXResourcesBuildPhase section */ 260 | 261 | /* Begin PBXSourcesBuildPhase section */ 262 | 52C2B325240C46BB006E217F /* Sources */ = { 263 | isa = PBXSourcesBuildPhase; 264 | buildActionMask = 2147483647; 265 | files = ( 266 | 52C2B32D240C46BB006E217F /* AppDelegate.swift in Sources */, 267 | ); 268 | runOnlyForDeploymentPostprocessing = 0; 269 | }; 270 | 52C2B33A240C46BC006E217F /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 52C2B343240C46BC006E217F /* To_do_BarTests.swift in Sources */, 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | }; 278 | 52C2B345240C46BC006E217F /* Sources */ = { 279 | isa = PBXSourcesBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | 52C2B34E240C46BC006E217F /* To_do_BarUITests.swift in Sources */, 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | /* End PBXSourcesBuildPhase section */ 287 | 288 | /* Begin PBXTargetDependency section */ 289 | 52C2B340240C46BC006E217F /* PBXTargetDependency */ = { 290 | isa = PBXTargetDependency; 291 | target = 52C2B328240C46BB006E217F /* To-do Bar */; 292 | targetProxy = 52C2B33F240C46BC006E217F /* PBXContainerItemProxy */; 293 | }; 294 | 52C2B34B240C46BC006E217F /* PBXTargetDependency */ = { 295 | isa = PBXTargetDependency; 296 | target = 52C2B328240C46BB006E217F /* To-do Bar */; 297 | targetProxy = 52C2B34A240C46BC006E217F /* PBXContainerItemProxy */; 298 | }; 299 | /* End PBXTargetDependency section */ 300 | 301 | /* Begin PBXVariantGroup section */ 302 | 52C2B335240C46BC006E217F /* Main.storyboard */ = { 303 | isa = PBXVariantGroup; 304 | children = ( 305 | 52C2B336240C46BC006E217F /* Base */, 306 | ); 307 | name = Main.storyboard; 308 | sourceTree = ""; 309 | }; 310 | /* End PBXVariantGroup section */ 311 | 312 | /* Begin XCBuildConfiguration section */ 313 | 52C2B350240C46BC006E217F /* Debug */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | ALWAYS_SEARCH_USER_PATHS = NO; 317 | CLANG_ANALYZER_NONNULL = YES; 318 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 319 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 320 | CLANG_CXX_LIBRARY = "libc++"; 321 | CLANG_ENABLE_MODULES = YES; 322 | CLANG_ENABLE_OBJC_ARC = YES; 323 | CLANG_ENABLE_OBJC_WEAK = YES; 324 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 325 | CLANG_WARN_BOOL_CONVERSION = YES; 326 | CLANG_WARN_COMMA = YES; 327 | CLANG_WARN_CONSTANT_CONVERSION = YES; 328 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 329 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 330 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 331 | CLANG_WARN_EMPTY_BODY = YES; 332 | CLANG_WARN_ENUM_CONVERSION = YES; 333 | CLANG_WARN_INFINITE_RECURSION = YES; 334 | CLANG_WARN_INT_CONVERSION = YES; 335 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 336 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 337 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 339 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 340 | CLANG_WARN_STRICT_PROTOTYPES = YES; 341 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 342 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 343 | CLANG_WARN_UNREACHABLE_CODE = YES; 344 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 345 | COPY_PHASE_STRIP = NO; 346 | DEBUG_INFORMATION_FORMAT = dwarf; 347 | ENABLE_STRICT_OBJC_MSGSEND = YES; 348 | ENABLE_TESTABILITY = YES; 349 | GCC_C_LANGUAGE_STANDARD = gnu11; 350 | GCC_DYNAMIC_NO_PIC = NO; 351 | GCC_NO_COMMON_BLOCKS = YES; 352 | GCC_OPTIMIZATION_LEVEL = 0; 353 | GCC_PREPROCESSOR_DEFINITIONS = ( 354 | "DEBUG=1", 355 | "$(inherited)", 356 | ); 357 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 358 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 359 | GCC_WARN_UNDECLARED_SELECTOR = YES; 360 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 361 | GCC_WARN_UNUSED_FUNCTION = YES; 362 | GCC_WARN_UNUSED_VARIABLE = YES; 363 | MACOSX_DEPLOYMENT_TARGET = 10.15; 364 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 365 | MTL_FAST_MATH = YES; 366 | ONLY_ACTIVE_ARCH = YES; 367 | SDKROOT = macosx; 368 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 369 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 370 | }; 371 | name = Debug; 372 | }; 373 | 52C2B351240C46BC006E217F /* Release */ = { 374 | isa = XCBuildConfiguration; 375 | buildSettings = { 376 | ALWAYS_SEARCH_USER_PATHS = NO; 377 | CLANG_ANALYZER_NONNULL = YES; 378 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 379 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 380 | CLANG_CXX_LIBRARY = "libc++"; 381 | CLANG_ENABLE_MODULES = YES; 382 | CLANG_ENABLE_OBJC_ARC = YES; 383 | CLANG_ENABLE_OBJC_WEAK = YES; 384 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 385 | CLANG_WARN_BOOL_CONVERSION = YES; 386 | CLANG_WARN_COMMA = YES; 387 | CLANG_WARN_CONSTANT_CONVERSION = YES; 388 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 389 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 390 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 391 | CLANG_WARN_EMPTY_BODY = YES; 392 | CLANG_WARN_ENUM_CONVERSION = YES; 393 | CLANG_WARN_INFINITE_RECURSION = YES; 394 | CLANG_WARN_INT_CONVERSION = YES; 395 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 396 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 397 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 398 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 399 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 400 | CLANG_WARN_STRICT_PROTOTYPES = YES; 401 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 402 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 403 | CLANG_WARN_UNREACHABLE_CODE = YES; 404 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 405 | COPY_PHASE_STRIP = NO; 406 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 407 | ENABLE_NS_ASSERTIONS = NO; 408 | ENABLE_STRICT_OBJC_MSGSEND = YES; 409 | GCC_C_LANGUAGE_STANDARD = gnu11; 410 | GCC_NO_COMMON_BLOCKS = YES; 411 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 412 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 413 | GCC_WARN_UNDECLARED_SELECTOR = YES; 414 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 415 | GCC_WARN_UNUSED_FUNCTION = YES; 416 | GCC_WARN_UNUSED_VARIABLE = YES; 417 | MACOSX_DEPLOYMENT_TARGET = 10.15; 418 | MTL_ENABLE_DEBUG_INFO = NO; 419 | MTL_FAST_MATH = YES; 420 | SDKROOT = macosx; 421 | SWIFT_COMPILATION_MODE = wholemodule; 422 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 423 | }; 424 | name = Release; 425 | }; 426 | 52C2B353240C46BC006E217F /* Debug */ = { 427 | isa = XCBuildConfiguration; 428 | buildSettings = { 429 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 430 | CODE_SIGN_ENTITLEMENTS = "To-do Bar/To_do_Bar.entitlements"; 431 | CODE_SIGN_STYLE = Automatic; 432 | COMBINE_HIDPI_IMAGES = YES; 433 | DEVELOPMENT_ASSET_PATHS = "\"To-do Bar/Preview Content\""; 434 | DEVELOPMENT_TEAM = 5TAHPLUH9U; 435 | ENABLE_PREVIEWS = YES; 436 | INFOPLIST_FILE = "To-do Bar/Info.plist"; 437 | LD_RUNPATH_SEARCH_PATHS = ( 438 | "$(inherited)", 439 | "@executable_path/../Frameworks", 440 | ); 441 | MACOSX_DEPLOYMENT_TARGET = 10.15; 442 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-Bar"; 443 | PRODUCT_NAME = "$(TARGET_NAME)"; 444 | SWIFT_VERSION = 5.0; 445 | }; 446 | name = Debug; 447 | }; 448 | 52C2B354240C46BC006E217F /* Release */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 452 | CODE_SIGN_ENTITLEMENTS = "To-do Bar/To_do_Bar.entitlements"; 453 | CODE_SIGN_STYLE = Automatic; 454 | COMBINE_HIDPI_IMAGES = YES; 455 | DEVELOPMENT_ASSET_PATHS = "\"To-do Bar/Preview Content\""; 456 | DEVELOPMENT_TEAM = 5TAHPLUH9U; 457 | ENABLE_PREVIEWS = YES; 458 | INFOPLIST_FILE = "To-do Bar/Info.plist"; 459 | LD_RUNPATH_SEARCH_PATHS = ( 460 | "$(inherited)", 461 | "@executable_path/../Frameworks", 462 | ); 463 | MACOSX_DEPLOYMENT_TARGET = 10.15; 464 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-Bar"; 465 | PRODUCT_NAME = "$(TARGET_NAME)"; 466 | SWIFT_VERSION = 5.0; 467 | }; 468 | name = Release; 469 | }; 470 | 52C2B356240C46BC006E217F /* Debug */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 474 | BUNDLE_LOADER = "$(TEST_HOST)"; 475 | CODE_SIGN_STYLE = Automatic; 476 | COMBINE_HIDPI_IMAGES = YES; 477 | INFOPLIST_FILE = "To-do BarTests/Info.plist"; 478 | LD_RUNPATH_SEARCH_PATHS = ( 479 | "$(inherited)", 480 | "@executable_path/../Frameworks", 481 | "@loader_path/../Frameworks", 482 | ); 483 | MACOSX_DEPLOYMENT_TARGET = 10.15; 484 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-BarTests"; 485 | PRODUCT_NAME = "$(TARGET_NAME)"; 486 | SWIFT_VERSION = 5.0; 487 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/To-do Bar.app/Contents/MacOS/To-do Bar"; 488 | }; 489 | name = Debug; 490 | }; 491 | 52C2B357240C46BC006E217F /* Release */ = { 492 | isa = XCBuildConfiguration; 493 | buildSettings = { 494 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 495 | BUNDLE_LOADER = "$(TEST_HOST)"; 496 | CODE_SIGN_STYLE = Automatic; 497 | COMBINE_HIDPI_IMAGES = YES; 498 | INFOPLIST_FILE = "To-do BarTests/Info.plist"; 499 | LD_RUNPATH_SEARCH_PATHS = ( 500 | "$(inherited)", 501 | "@executable_path/../Frameworks", 502 | "@loader_path/../Frameworks", 503 | ); 504 | MACOSX_DEPLOYMENT_TARGET = 10.15; 505 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-BarTests"; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | SWIFT_VERSION = 5.0; 508 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/To-do Bar.app/Contents/MacOS/To-do Bar"; 509 | }; 510 | name = Release; 511 | }; 512 | 52C2B359240C46BC006E217F /* Debug */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 516 | CODE_SIGN_STYLE = Automatic; 517 | COMBINE_HIDPI_IMAGES = YES; 518 | INFOPLIST_FILE = "To-do BarUITests/Info.plist"; 519 | LD_RUNPATH_SEARCH_PATHS = ( 520 | "$(inherited)", 521 | "@executable_path/../Frameworks", 522 | "@loader_path/../Frameworks", 523 | ); 524 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-BarUITests"; 525 | PRODUCT_NAME = "$(TARGET_NAME)"; 526 | SWIFT_VERSION = 5.0; 527 | TEST_TARGET_NAME = "To-do Bar"; 528 | }; 529 | name = Debug; 530 | }; 531 | 52C2B35A240C46BC006E217F /* Release */ = { 532 | isa = XCBuildConfiguration; 533 | buildSettings = { 534 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 535 | CODE_SIGN_STYLE = Automatic; 536 | COMBINE_HIDPI_IMAGES = YES; 537 | INFOPLIST_FILE = "To-do BarUITests/Info.plist"; 538 | LD_RUNPATH_SEARCH_PATHS = ( 539 | "$(inherited)", 540 | "@executable_path/../Frameworks", 541 | "@loader_path/../Frameworks", 542 | ); 543 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-BarUITests"; 544 | PRODUCT_NAME = "$(TARGET_NAME)"; 545 | SWIFT_VERSION = 5.0; 546 | TEST_TARGET_NAME = "To-do Bar"; 547 | }; 548 | name = Release; 549 | }; 550 | /* End XCBuildConfiguration section */ 551 | 552 | /* Begin XCConfigurationList section */ 553 | 52C2B324240C46BB006E217F /* Build configuration list for PBXProject "To-do Bar" */ = { 554 | isa = XCConfigurationList; 555 | buildConfigurations = ( 556 | 52C2B350240C46BC006E217F /* Debug */, 557 | 52C2B351240C46BC006E217F /* Release */, 558 | ); 559 | defaultConfigurationIsVisible = 0; 560 | defaultConfigurationName = Release; 561 | }; 562 | 52C2B352240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do Bar" */ = { 563 | isa = XCConfigurationList; 564 | buildConfigurations = ( 565 | 52C2B353240C46BC006E217F /* Debug */, 566 | 52C2B354240C46BC006E217F /* Release */, 567 | ); 568 | defaultConfigurationIsVisible = 0; 569 | defaultConfigurationName = Release; 570 | }; 571 | 52C2B355240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do BarTests" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | 52C2B356240C46BC006E217F /* Debug */, 575 | 52C2B357240C46BC006E217F /* Release */, 576 | ); 577 | defaultConfigurationIsVisible = 0; 578 | defaultConfigurationName = Release; 579 | }; 580 | 52C2B358240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do BarUITests" */ = { 581 | isa = XCConfigurationList; 582 | buildConfigurations = ( 583 | 52C2B359240C46BC006E217F /* Debug */, 584 | 52C2B35A240C46BC006E217F /* Release */, 585 | ); 586 | defaultConfigurationIsVisible = 0; 587 | defaultConfigurationName = Release; 588 | }; 589 | /* End XCConfigurationList section */ 590 | }; 591 | rootObject = 52C2B321240C46BB006E217F /* Project object */; 592 | } 593 | -------------------------------------------------------------------------------- /To-do Bar.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /To-do Bar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /To-do Bar/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // To-do Bar 4 | // 5 | // Created by Zhexiong Liu on 3/1/20. 6 | // Copyright © 2020 Zhexiong Liu. All rights reserved. 7 | // 8 | 9 | 10 | import Cocoa 11 | import SwiftUI 12 | import EventKit 13 | import NaturalLanguage 14 | 15 | @NSApplicationMain 16 | class AppDelegate: NSObject, NSApplicationDelegate { 17 | 18 | var window: NSWindow! 19 | var statusBarItem: NSStatusItem! 20 | 21 | var eventStore = EKEventStore() 22 | var statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) 23 | 24 | var calendarGrant = false 25 | var remindersGrant = false 26 | var reminderCount = 0 27 | 28 | let defaultWidth = 200 29 | let maxStringLength = 30 30 | 31 | func applicationDidFinishLaunching(_ aNotification: Notification) { 32 | 33 | let group = DispatchGroup() 34 | 35 | group.enter() 36 | eventStore.requestAccess(to: .event) { 37 | (granted, error) in 38 | if let error = error { 39 | print(error) 40 | return 41 | } 42 | if granted { 43 | self.calendarGrant = true 44 | } 45 | group.leave() 46 | } 47 | 48 | 49 | group.enter() 50 | eventStore.requestAccess(to: .reminder) { 51 | (granted, error) in 52 | if let error = error { 53 | print(error) 54 | return 55 | } 56 | if granted { 57 | self.remindersGrant = true 58 | } 59 | group.leave() 60 | } 61 | 62 | 63 | group.wait() 64 | print(calendarGrant, remindersGrant) 65 | 66 | 67 | if let button = self.statusItem.button { 68 | DispatchQueue.main.async { 69 | button.image = NSImage(named: "BarIcon") 70 | if self.reminderCount > 0 { 71 | button.imagePosition = NSControl.ImagePosition.imageLeft 72 | button.title = String(self.reminderCount) 73 | // print(self.reminderCount) 74 | } 75 | } 76 | } 77 | 78 | // calendarGrant = false 79 | // remindersGrant = true 80 | if !calendarGrant && !remindersGrant { 81 | defaultMenu() 82 | } 83 | 84 | 85 | if calendarGrant || remindersGrant { 86 | 87 | let timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(fetchItems), userInfo: nil, repeats: true) 88 | RunLoop.main.add(timer, forMode: RunLoop.Mode.common) 89 | 90 | } 91 | } 92 | 93 | 94 | @ objc func defaultMenu() { 95 | 96 | let statusBarMenu = NSMenu(title: "TodoMenuBar") 97 | statusBarMenu.minimumWidth = CGFloat(defaultWidth) 98 | self.statusItem.menu = statusBarMenu 99 | 100 | 101 | statusBarMenu.addItem(NSMenuItem.separator()) 102 | 103 | statusBarMenu.addItem( 104 | withTitle: "Please Grant APP Access", 105 | action: nil, 106 | keyEquivalent: "") 107 | 108 | statusBarMenu.addItem( 109 | withTitle: "to Reminders or Calendar", 110 | action: nil, 111 | keyEquivalent: "") 112 | 113 | statusBarMenu.addItem( 114 | withTitle: "Open System Preferences", 115 | action: #selector(AppDelegate.OpenSystemPreferencesAPP), 116 | keyEquivalent: ",") 117 | 118 | statusBarMenu.addItem( 119 | withTitle: "Quit", 120 | action: #selector(AppDelegate.QuitAPP), 121 | keyEquivalent: "q") 122 | } 123 | 124 | 125 | @objc func calendarMenu(statusBarMenu: NSMenu) { 126 | 127 | // statusBarMenu.addItem(NSMenuItem.separator()) 128 | if calendarGrant { 129 | statusBarMenu.addItem( 130 | withTitle: "Calendar", 131 | action: #selector(AppDelegate.OpenCalendar), 132 | keyEquivalent: "c") 133 | } 134 | // statusBarMenu.addItem(NSMenuItem.separator()) 135 | 136 | var allEvents: [EKEvent] = [] 137 | let calendars = eventStore.calendars(for: .event) 138 | for calendar in calendars { 139 | let predicate = eventStore.predicateForEvents(withStart: Date(), end: Date.tomorrow, calendars: [calendar]) 140 | let events = eventStore.events(matching: predicate) 141 | allEvents.append(contentsOf: events) 142 | } 143 | 144 | var startDateString = "" 145 | var endDateString = "" 146 | var Datestring = "" 147 | var EventSring = "" 148 | var allDayEvent: [EKEvent] = [] 149 | for event in allEvents { 150 | startDateString = event.startDate.toString(dateFormat: "h:mm a") 151 | endDateString = event.endDate.toString(dateFormat: "h:mm a") 152 | 153 | if event.isAllDay { 154 | allDayEvent.append(event) 155 | } 156 | 157 | if Date().toLocalTime() < event.startDate.toLocalTime() { 158 | 159 | Datestring = startDateString + "-" + endDateString 160 | EventSring = event.title as String 161 | if EventSring.count >= self.maxStringLength { 162 | EventSring = String(EventSring.prefix(self.maxStringLength)) + "..." 163 | } 164 | 165 | statusBarMenu.addItem(NSMenuItem.separator()) 166 | 167 | statusBarMenu.addItem( 168 | withTitle: Datestring, 169 | action: #selector(AppDelegate.DoNothingAPP), 170 | keyEquivalent: "") 171 | 172 | statusBarMenu.addItem( 173 | withTitle: EventSring, 174 | action: #selector(AppDelegate.DoNothingAPP), 175 | keyEquivalent: "") 176 | 177 | } 178 | 179 | if !event.isAllDay && Date().toLocalTime() > event.startDate.toLocalTime() && Date().toLocalTime() < event.endDate.toLocalTime() { 180 | 181 | Datestring = "Now" + "-" + endDateString 182 | 183 | EventSring = event.title as String 184 | if EventSring.count >= self.maxStringLength { 185 | EventSring = String(EventSring.prefix(self.maxStringLength)) + "..." 186 | } 187 | 188 | statusBarMenu.addItem(NSMenuItem.separator()) 189 | 190 | statusBarMenu.addItem( 191 | withTitle: Datestring, 192 | action: #selector(AppDelegate.DoNothingAPP), 193 | keyEquivalent: "") 194 | 195 | statusBarMenu.addItem( 196 | withTitle: EventSring, 197 | action: #selector(AppDelegate.DoNothingAPP), 198 | keyEquivalent: "") 199 | } 200 | } 201 | 202 | for event in allDayEvent { 203 | 204 | Datestring = "All Day" 205 | EventSring = event.title as String 206 | if EventSring.count >= self.maxStringLength { 207 | EventSring = String(EventSring.prefix(self.maxStringLength)) + "..." 208 | } 209 | 210 | statusBarMenu.addItem(NSMenuItem.separator()) 211 | 212 | statusBarMenu.addItem( 213 | withTitle: Datestring, 214 | action: #selector(AppDelegate.DoNothingAPP), 215 | keyEquivalent: "") 216 | 217 | statusBarMenu.addItem( 218 | withTitle: EventSring, 219 | action: #selector(AppDelegate.DoNothingAPP), 220 | keyEquivalent: "") 221 | } 222 | } 223 | 224 | 225 | @objc func remindersMenu(statusBarMenu: NSMenu) { 226 | 227 | 228 | let textView = NSView(frame: NSRect(x: 0, y: 0, width: 280, height: 25)) 229 | let textField = TextInput() 230 | textField.frame = NSRect(x: 10, y: 0, width: textView.frame.width - 20, height: textView.frame.height) 231 | textView.addSubview(textField) 232 | // textView.becomeFirstResponder() 233 | 234 | 235 | let textFieldInMenutest = NSMenuItem() 236 | statusBarMenu.addItem(textFieldInMenutest) 237 | textFieldInMenutest.view = textView 238 | 239 | self.reminderCount = 0 240 | 241 | statusBarMenu.addItem(NSMenuItem.separator()) 242 | if remindersGrant { 243 | statusBarMenu.addItem( 244 | withTitle: "Reminders", 245 | action: #selector(AppDelegate.OpenReminders), 246 | keyEquivalent: "r") 247 | } 248 | statusBarMenu.addItem(NSMenuItem.separator()) 249 | 250 | let predicated: NSPredicate? = eventStore.predicateForIncompleteReminders(withDueDateStarting: nil, ending: nil, calendars: nil) 251 | if let aPredicate = predicated { 252 | eventStore.fetchReminders(matching: aPredicate, completion: { 253 | (_ reminders: [Any]?) -> Void in 254 | for reminder: EKReminder? in reminders as? [EKReminder?] ?? [EKReminder?]() { 255 | 256 | // print(reminder?.dueDateComponents?.date) 257 | // print(reminder?.dueDateComponents?.date?.toLocalTime()) 258 | // print(Date()) 259 | // print(Date().toLocalTime()) 260 | // print(Date.tomorrow.toLocalTime()) 261 | 262 | if reminder?.dueDateComponents?.date?.toLocalTime() ?? Date().toLocalTime() <= Date.tomorrow.toLocalTime() { 263 | self.reminderCount = self.reminderCount + 1 264 | // print(self.reminderCount) 265 | var ReminderString = reminder!.title as String 266 | if ReminderString.count >= self.maxStringLength { 267 | ReminderString = String(ReminderString.prefix(self.maxStringLength)) + "..." 268 | } 269 | statusBarMenu.addItem( 270 | withTitle: ReminderString, 271 | action: #selector(AppDelegate.removeItems(sender:)), 272 | keyEquivalent: "") 273 | } 274 | } 275 | 276 | statusBarMenu.addItem(NSMenuItem.separator()) 277 | 278 | self.calendarMenu(statusBarMenu: statusBarMenu) 279 | self.referenceMenu(statusBarMenu: statusBarMenu) 280 | 281 | self.updateMenuBar() 282 | }) 283 | } 284 | } 285 | 286 | @objc func updateMenuBar() { 287 | if let button = self.statusItem.button { 288 | DispatchQueue.main.async { 289 | button.image = NSImage(named: "BarIcon") 290 | if self.reminderCount > 0 { 291 | button.imagePosition = NSControl.ImagePosition.imageLeft 292 | button.title = String(self.reminderCount) 293 | // print(self.reminderCount) 294 | } else { 295 | button.imagePosition = NSControl.ImagePosition.imageOnly 296 | } 297 | } 298 | } 299 | } 300 | 301 | @objc func referenceMenu(statusBarMenu: NSMenu) { 302 | 303 | statusBarMenu.addItem(NSMenuItem.separator()) 304 | 305 | statusBarMenu.addItem( 306 | withTitle: "Reference", 307 | action: #selector(AppDelegate.OpenReferenceURL), 308 | keyEquivalent: ",") 309 | 310 | statusBarMenu.addItem( 311 | withTitle: "Quit", 312 | action: #selector(AppDelegate.QuitAPP), 313 | keyEquivalent: "q") 314 | } 315 | 316 | 317 | @objc func fetchItems() { 318 | 319 | 320 | let statusBarMenu = NSMenu(title: "TodoMenuBar ") 321 | statusBarMenu.minimumWidth = 200 322 | statusItem.menu = statusBarMenu 323 | 324 | // statusBarMenu.addItem( 325 | // withTitle: "Tasks for " + Date().toString(dateFormat: "EEE MMM d, yyyy"), 326 | // action: nil, 327 | // keyEquivalent: "") 328 | 329 | statusBarMenu.addItem(NSMenuItem.separator()) 330 | 331 | remindersMenu(statusBarMenu: statusBarMenu) 332 | 333 | } 334 | 335 | 336 | @objc func removeItems(sender: Any) { 337 | let statusBarMenu = NSMenu(title: "TodoMenuBar") 338 | statusItem.menu = statusBarMenu 339 | 340 | let item = (sender as! NSMenuItem).title 341 | 342 | let predicated: NSPredicate? = eventStore.predicateForIncompleteReminders(withDueDateStarting: nil, ending: nil, calendars: nil) 343 | if let aPredicate = predicated { 344 | eventStore.fetchReminders(matching: aPredicate, completion: { 345 | (_ reminders: [Any]?) -> Void in 346 | 347 | for reminder: EKReminder? in reminders as? [EKReminder?] ?? [EKReminder?]() { 348 | 349 | if reminder?.dueDateComponents?.date?.toLocalTime() ?? Date().toLocalTime() <= Date.tomorrow.toLocalTime() { 350 | let target_item = reminder?.title! 351 | // print(item.prefix(self.maxStringLength), target_item?.prefix(self.maxStringLength)) 352 | if item.prefix(self.maxStringLength) == target_item?.prefix(self.maxStringLength) { 353 | reminder?.isCompleted = true 354 | do { 355 | // self.reminderCount = self.reminderCount - 1 356 | try self.eventStore.save(reminder!, commit: true) 357 | print("completed reminder successed") 358 | // self.fetchItems() 359 | } catch { 360 | print("error \(error)") 361 | } 362 | } 363 | } 364 | } 365 | }) 366 | } 367 | } 368 | 369 | 370 | @objc func QuitAPP(_ sender: Any) { 371 | NSApplication.shared.terminate(self) 372 | } 373 | 374 | 375 | @objc func RefreshAPP(_ sender: Any) { 376 | fetchItems() 377 | } 378 | 379 | @objc func DoNothingAPP(_ sender: Any) { 380 | } 381 | 382 | @objc func OpenReminders(_ sender: Any) { 383 | let url = NSURL(fileURLWithPath: "/System/Applications/Reminders.app", isDirectory: true) as URL 384 | let configuration = NSWorkspace.OpenConfiguration() 385 | NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: nil) 386 | } 387 | 388 | 389 | @objc func OpenCalendar(_ sender: Any) { 390 | let url = NSURL(fileURLWithPath: "/System/Applications/Calendar.app", isDirectory: true) as URL 391 | let configuration = NSWorkspace.OpenConfiguration() 392 | NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: nil) 393 | } 394 | 395 | @objc func OpenSystemPreferencesAPP(_ sender: Any) { 396 | let url = NSURL(fileURLWithPath: "/System/Applications/System Preferences.app", isDirectory: true) as URL 397 | let configuration = NSWorkspace.OpenConfiguration() 398 | NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: nil) 399 | } 400 | 401 | @objc func OpenReferenceURL() { 402 | let url = URL(string: "https://github.com/ZhexiongLiu/TodoBar")! 403 | NSWorkspace.shared.open(url) 404 | } 405 | 406 | @objc func OpenContentWindow() { 407 | 408 | let alert = NSAlert.init() 409 | alert.messageText = "To-do Bar" 410 | alert.informativeText = "Grant access to Reminders/Calendar in Privacy Preference if you'd like to show tasks in menu bar. @ZhexiongLiu" 411 | alert.addButton(withTitle: "OK") 412 | alert.addButton(withTitle: "Cancel") 413 | alert.runModal() 414 | } 415 | 416 | 417 | } 418 | 419 | 420 | class TextInput: NSTextField, NSTextFieldDelegate { 421 | 422 | override func viewDidChangeBackingProperties() { 423 | self.delegate = self 424 | self.placeholderString = "Enter your reminders here..." 425 | self.isEditable = true 426 | self.textColor = .black 427 | self.selectText(nil) 428 | self.isSelectable = true 429 | self.drawsBackground = false 430 | self.font = NSFont.systemFont(ofSize: 14) 431 | self.cell?.usesSingleLineMode = true 432 | self.bezelStyle = NSTextField.BezelStyle.roundedBezel 433 | // _ = self.becomeFirstResponder() 434 | self.window?.makeFirstResponder(self) 435 | } 436 | 437 | 438 | func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { 439 | if (commandSelector == #selector(NSResponder.insertNewline(_:))) { 440 | // hit enter key 441 | if let textField = control as? NSTextField { 442 | let data = getDateString(thisString: textField.stringValue as NSString) 443 | let dateString = data[0] as NSString 444 | let text = data[1] 445 | 446 | var date = Date() 447 | if dateString != "" { 448 | let dateDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue) 449 | if let tcr: NSTextCheckingResult = dateDetector?.firstMatch(in: dateString as String, options: [], range: NSMakeRange(0, dateString.length)), let this_date = tcr.date { 450 | date = this_date 451 | } 452 | 453 | let eventStore = EKEventStore() 454 | let reminder = EKReminder(eventStore: eventStore) 455 | let alarm = EKAlarm(absoluteDate: date) 456 | 457 | reminder.title = text 458 | reminder.addAlarm(alarm) 459 | reminder.dueDateComponents = dateComponentFromNSDate(date: date as NSDate) as DateComponents 460 | reminder.calendar = eventStore.defaultCalendarForNewReminders() 461 | do { 462 | try eventStore.save(reminder, commit: true) 463 | print("saved") 464 | } catch { 465 | print("error: \(error)") 466 | } 467 | 468 | } else { 469 | let eventStore = EKEventStore() 470 | let reminder = EKReminder(eventStore: eventStore) 471 | reminder.title = text 472 | reminder.calendar = eventStore.defaultCalendarForNewReminders() 473 | do { 474 | try eventStore.save(reminder, commit: true) 475 | print("saved") 476 | } catch { 477 | print("error: \(error)") 478 | } 479 | } 480 | textField.stringValue = "" 481 | } 482 | return true 483 | } 484 | return false 485 | } 486 | 487 | func dateComponentFromNSDate(date: NSDate) -> NSDateComponents { 488 | let calendarUnit = Set([.minute, .hour, .day, .month, .year]) 489 | let dateComponents = Calendar.current.dateComponents(calendarUnit, 490 | from: date as Date) 491 | return dateComponents as NSDateComponents 492 | } 493 | 494 | 495 | func getDateString(thisString: NSString) -> Array { 496 | 497 | var newString = thisString as String 498 | var newDateString = "" 499 | 500 | let types: NSTextCheckingResult.CheckingType = [.address, .date] 501 | let dataDetector = try? NSDataDetector(types: types.rawValue) 502 | 503 | dataDetector?.enumerateMatches(in: thisString as String, range: NSMakeRange(0, thisString.length), using: { 504 | (match, flags, _) in 505 | 506 | let matchString = thisString.substring(with: (match?.range)!) as NSString 507 | if match?.resultType == .date { 508 | newString = thisString.replacingOccurrences(of: matchString as String, with: "") 509 | newDateString = matchString as String 510 | } 511 | }) 512 | 513 | newString = newString.replacingOccurrences(of: " " as String, with: " ") 514 | newString = newString.trimmingCharacters(in: .whitespacesAndNewlines) 515 | 516 | print([newDateString, newString]) 517 | return [newDateString, newString] 518 | } 519 | } 520 | 521 | 522 | extension Date { 523 | func toString(dateFormat format: String) -> String { 524 | let dateFormatter = DateFormatter() 525 | dateFormatter.locale = Locale(identifier: "en-US") 526 | dateFormatter.dateFormat = format 527 | return dateFormatter.string(from: self) 528 | } 529 | 530 | static var yesterday: Date { 531 | return Date().dayBefore 532 | } 533 | static var tomorrow: Date { 534 | return Date().dayAfter 535 | } 536 | var dayBefore: Date { 537 | return Calendar.current.date(byAdding: .day, value: -1, to: midnight)! 538 | } 539 | var dayAfter: Date { 540 | return Calendar.current.date(byAdding: .day, value: 1, to: midnight)! 541 | } 542 | var midnight: Date { 543 | return Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: self)! 544 | } 545 | var month: Int { 546 | return Calendar.current.component(.month, from: self) 547 | } 548 | var isLastDayOfMonth: Bool { 549 | return dayAfter.month != month 550 | } 551 | 552 | func toGlobalTime() -> Date { 553 | let timezone = TimeZone.current 554 | let seconds = -TimeInterval(timezone.secondsFromGMT(for: self)) 555 | return Date(timeInterval: seconds, since: self) 556 | } 557 | 558 | func toLocalTime() -> Date { 559 | let timezone = TimeZone.current 560 | let seconds = TimeInterval(timezone.secondsFromGMT(for: self)) 561 | return Date(timeInterval: seconds, since: self) 562 | } 563 | } 564 | 565 | -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/256-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/256-1.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/32-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/32-1.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/512-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/512-1.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "32-1.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "256-1.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "512-1.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/BarIcon.imageset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/BarIcon.imageset/20.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/BarIcon.imageset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/BarIcon.imageset/40.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/BarIcon.imageset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/BarIcon.imageset/60.png -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/BarIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "20.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "40.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "60.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } -------------------------------------------------------------------------------- /To-do Bar/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /To-do Bar/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | Default 532 | 533 | 534 | 535 | 536 | 537 | 538 | Left to Right 539 | 540 | 541 | 542 | 543 | 544 | 545 | Right to Left 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | Default 557 | 558 | 559 | 560 | 561 | 562 | 563 | Left to Right 564 | 565 | 566 | 567 | 568 | 569 | 570 | Right to Left 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | -------------------------------------------------------------------------------- /To-do Bar/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 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2020 Zhexiong Liu. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | NSSupportsAutomaticTermination 32 | 33 | NSSupportsSuddenTermination 34 | 35 | NSCalendarsUsageDescription 36 | 37 | NSRemindersUsageDescription 38 | 39 | LSUIElement 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /To-do Bar/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /To-do Bar/To_do_Bar.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.personal-information.calendars 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /To-do BarTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /To-do BarTests/To_do_BarTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // To_do_BarTests.swift 3 | // To-do BarTests 4 | // 5 | // Created by Zhexiong Liu on 3/1/20. 6 | // Copyright © 2020 Zhexiong Liu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import To_do_Bar 11 | 12 | class To_do_BarTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /To-do BarUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /To-do BarUITests/To_do_BarUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // To_do_BarUITests.swift 3 | // To-do BarUITests 4 | // 5 | // Created by Zhexiong Liu on 3/1/20. 6 | // Copyright © 2020 Zhexiong Liu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class To_do_BarUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 20 | } 21 | 22 | override func tearDown() { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | } 25 | 26 | func testExample() { 27 | // UI tests must launch the application that they test. 28 | let app = XCUIApplication() 29 | app.launch() 30 | 31 | // Use recording to get started writing UI tests. 32 | // Use XCTAssert and related functions to verify your tests produce the correct results. 33 | } 34 | 35 | func testLaunchPerformance() { 36 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 37 | // This measures how long it takes to launch your application. 38 | measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { 39 | XCUIApplication().launch() 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /img/To-do Bar.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/To-do Bar.icns -------------------------------------------------------------------------------- /img/To-do Bar.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/To-do Bar.sketch -------------------------------------------------------------------------------- /img/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/screen1.png -------------------------------------------------------------------------------- /img/screen1_v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/screen1_v1.png -------------------------------------------------------------------------------- /img/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/screen2.png -------------------------------------------------------------------------------- /img/screen2_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/screen2_v2.png -------------------------------------------------------------------------------- /img/tutorial.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/tutorial.gif -------------------------------------------------------------------------------- /img/tutorial.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/tutorial.mp4 -------------------------------------------------------------------------------- /img/tutorial_v1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/tutorial_v1.gif -------------------------------------------------------------------------------- /img/tutorial_v1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/tutorial_v1.mp4 --------------------------------------------------------------------------------