├── .gitignore
├── LICENSE
├── README.md
├── status-bar-todo.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
└── status-bar-todo
├── AddTodoViewController.swift
├── AddTodoViewController.xib
├── AppDelegate.swift
├── Assets.xcassets
├── AppIcon.appiconset
│ └── Contents.json
└── Contents.json
├── Base.lproj
└── MainMenu.xib
├── CheckboxTableCellView.swift
├── EditTodosWindowController.swift
├── EditTodosWindowController.xib
├── Info.plist
├── TodoItem.swift
├── TodoItemTableCellView.swift
└── TodoItemsController.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xcuserstate
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 | *.ipa
27 | *.dSYM.zip
28 | *.dSYM
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | # Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/Preview.html
64 | fastlane/screenshots
65 | fastlane/test_output
66 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Onix-Systems
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # status-bar-todo
2 |
3 | Simple macOS app to keep TODO-list in status bar.
4 |
5 | This app can do limited number of tasks:
6 |
7 | 1. Add TODO
8 | 1. Mark/unmark TODO as completed
9 | 1. Delete one or all TODOs
10 | 1. Edit single todo
11 |
12 | Here's how it look like:
13 |
14 |
15 |
16 | ## Installation
17 |
18 | Clone project and run it.
19 |
20 | ## LICENSE
21 |
22 | status-bar-todo is released under the MIT license. See [LICENSE](LICENSE) for details.
23 |
--------------------------------------------------------------------------------
/status-bar-todo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1E545FC81E0AD7E500D19810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E545FC71E0AD7E500D19810 /* AppDelegate.swift */; };
11 | 1E545FCA1E0AD7E500D19810 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1E545FC91E0AD7E500D19810 /* Assets.xcassets */; };
12 | 1E545FCD1E0AD7E500D19810 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1E545FCB1E0AD7E500D19810 /* MainMenu.xib */; };
13 | 1E545FD61E0AE62400D19810 /* EditTodosWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E545FD41E0AE62400D19810 /* EditTodosWindowController.swift */; };
14 | 1E545FD71E0AE62400D19810 /* EditTodosWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1E545FD51E0AE62400D19810 /* EditTodosWindowController.xib */; };
15 | 1E8FB7541E0BCD5200BF6D20 /* TodoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E8FB7531E0BCD5200BF6D20 /* TodoItem.swift */; };
16 | 1E8FB7561E0BCD6400BF6D20 /* TodoItemsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E8FB7551E0BCD6400BF6D20 /* TodoItemsController.swift */; };
17 | 1E8FB75D1E0BF88300BF6D20 /* AddTodoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E8FB75B1E0BF88300BF6D20 /* AddTodoViewController.swift */; };
18 | 1E8FB75E1E0BF88300BF6D20 /* AddTodoViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1E8FB75C1E0BF88300BF6D20 /* AddTodoViewController.xib */; };
19 | 1EC8A5F21E1EAD5E003E401F /* CheckboxTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EC8A5F11E1EAD5D003E401F /* CheckboxTableCellView.swift */; };
20 | 1EC8A5F41E1EAD75003E401F /* TodoItemTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EC8A5F31E1EAD75003E401F /* TodoItemTableCellView.swift */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXFileReference section */
24 | 1E545FC41E0AD7E500D19810 /* status-bar-todo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "status-bar-todo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
25 | 1E545FC71E0AD7E500D19810 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
26 | 1E545FC91E0AD7E500D19810 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
27 | 1E545FCC1E0AD7E500D19810 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
28 | 1E545FCE1E0AD7E500D19810 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
29 | 1E545FD41E0AE62400D19810 /* EditTodosWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditTodosWindowController.swift; sourceTree = ""; };
30 | 1E545FD51E0AE62400D19810 /* EditTodosWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EditTodosWindowController.xib; sourceTree = ""; };
31 | 1E8FB7531E0BCD5200BF6D20 /* TodoItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoItem.swift; sourceTree = ""; };
32 | 1E8FB7551E0BCD6400BF6D20 /* TodoItemsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoItemsController.swift; sourceTree = ""; };
33 | 1E8FB75B1E0BF88300BF6D20 /* AddTodoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddTodoViewController.swift; sourceTree = ""; };
34 | 1E8FB75C1E0BF88300BF6D20 /* AddTodoViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AddTodoViewController.xib; sourceTree = ""; };
35 | 1EC8A5F11E1EAD5D003E401F /* CheckboxTableCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxTableCellView.swift; sourceTree = ""; };
36 | 1EC8A5F31E1EAD75003E401F /* TodoItemTableCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoItemTableCellView.swift; sourceTree = ""; };
37 | /* End PBXFileReference section */
38 |
39 | /* Begin PBXFrameworksBuildPhase section */
40 | 1E545FC11E0AD7E500D19810 /* Frameworks */ = {
41 | isa = PBXFrameworksBuildPhase;
42 | buildActionMask = 2147483647;
43 | files = (
44 | );
45 | runOnlyForDeploymentPostprocessing = 0;
46 | };
47 | /* End PBXFrameworksBuildPhase section */
48 |
49 | /* Begin PBXGroup section */
50 | 1E545FBB1E0AD7E500D19810 = {
51 | isa = PBXGroup;
52 | children = (
53 | 1E545FC61E0AD7E500D19810 /* todo-status-bar */,
54 | 1E545FC51E0AD7E500D19810 /* Products */,
55 | );
56 | sourceTree = "";
57 | };
58 | 1E545FC51E0AD7E500D19810 /* Products */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 1E545FC41E0AD7E500D19810 /* status-bar-todo.app */,
62 | );
63 | name = Products;
64 | sourceTree = "";
65 | };
66 | 1E545FC61E0AD7E500D19810 /* todo-status-bar */ = {
67 | isa = PBXGroup;
68 | children = (
69 | 1E545FC71E0AD7E500D19810 /* AppDelegate.swift */,
70 | 1E8FB7531E0BCD5200BF6D20 /* TodoItem.swift */,
71 | 1E8FB7551E0BCD6400BF6D20 /* TodoItemsController.swift */,
72 | 1E8FB75B1E0BF88300BF6D20 /* AddTodoViewController.swift */,
73 | 1E8FB75C1E0BF88300BF6D20 /* AddTodoViewController.xib */,
74 | 1E545FD51E0AE62400D19810 /* EditTodosWindowController.xib */,
75 | 1E545FD41E0AE62400D19810 /* EditTodosWindowController.swift */,
76 | 1EC8A5F11E1EAD5D003E401F /* CheckboxTableCellView.swift */,
77 | 1EC8A5F31E1EAD75003E401F /* TodoItemTableCellView.swift */,
78 | 1E545FC91E0AD7E500D19810 /* Assets.xcassets */,
79 | 1E545FCB1E0AD7E500D19810 /* MainMenu.xib */,
80 | 1E545FCE1E0AD7E500D19810 /* Info.plist */,
81 | );
82 | name = "todo-status-bar";
83 | path = "status-bar-todo";
84 | sourceTree = "";
85 | };
86 | /* End PBXGroup section */
87 |
88 | /* Begin PBXNativeTarget section */
89 | 1E545FC31E0AD7E500D19810 /* status-bar-todo */ = {
90 | isa = PBXNativeTarget;
91 | buildConfigurationList = 1E545FD11E0AD7E500D19810 /* Build configuration list for PBXNativeTarget "status-bar-todo" */;
92 | buildPhases = (
93 | 1E545FC01E0AD7E500D19810 /* Sources */,
94 | 1E545FC11E0AD7E500D19810 /* Frameworks */,
95 | 1E545FC21E0AD7E500D19810 /* Resources */,
96 | );
97 | buildRules = (
98 | );
99 | dependencies = (
100 | );
101 | name = "status-bar-todo";
102 | productName = "todo-status-bar";
103 | productReference = 1E545FC41E0AD7E500D19810 /* status-bar-todo.app */;
104 | productType = "com.apple.product-type.application";
105 | };
106 | /* End PBXNativeTarget section */
107 |
108 | /* Begin PBXProject section */
109 | 1E545FBC1E0AD7E500D19810 /* Project object */ = {
110 | isa = PBXProject;
111 | attributes = {
112 | LastSwiftUpdateCheck = 0820;
113 | LastUpgradeCheck = 0820;
114 | ORGANIZATIONNAME = "Onix-Systems";
115 | TargetAttributes = {
116 | 1E545FC31E0AD7E500D19810 = {
117 | CreatedOnToolsVersion = 8.2.1;
118 | DevelopmentTeam = GJX986XEH7;
119 | ProvisioningStyle = Automatic;
120 | };
121 | };
122 | };
123 | buildConfigurationList = 1E545FBF1E0AD7E500D19810 /* Build configuration list for PBXProject "status-bar-todo" */;
124 | compatibilityVersion = "Xcode 3.2";
125 | developmentRegion = English;
126 | hasScannedForEncodings = 0;
127 | knownRegions = (
128 | en,
129 | Base,
130 | );
131 | mainGroup = 1E545FBB1E0AD7E500D19810;
132 | productRefGroup = 1E545FC51E0AD7E500D19810 /* Products */;
133 | projectDirPath = "";
134 | projectRoot = "";
135 | targets = (
136 | 1E545FC31E0AD7E500D19810 /* status-bar-todo */,
137 | );
138 | };
139 | /* End PBXProject section */
140 |
141 | /* Begin PBXResourcesBuildPhase section */
142 | 1E545FC21E0AD7E500D19810 /* Resources */ = {
143 | isa = PBXResourcesBuildPhase;
144 | buildActionMask = 2147483647;
145 | files = (
146 | 1E8FB75E1E0BF88300BF6D20 /* AddTodoViewController.xib in Resources */,
147 | 1E545FD71E0AE62400D19810 /* EditTodosWindowController.xib in Resources */,
148 | 1E545FCA1E0AD7E500D19810 /* Assets.xcassets in Resources */,
149 | 1E545FCD1E0AD7E500D19810 /* MainMenu.xib in Resources */,
150 | );
151 | runOnlyForDeploymentPostprocessing = 0;
152 | };
153 | /* End PBXResourcesBuildPhase section */
154 |
155 | /* Begin PBXSourcesBuildPhase section */
156 | 1E545FC01E0AD7E500D19810 /* Sources */ = {
157 | isa = PBXSourcesBuildPhase;
158 | buildActionMask = 2147483647;
159 | files = (
160 | 1E8FB7561E0BCD6400BF6D20 /* TodoItemsController.swift in Sources */,
161 | 1E545FC81E0AD7E500D19810 /* AppDelegate.swift in Sources */,
162 | 1EC8A5F21E1EAD5E003E401F /* CheckboxTableCellView.swift in Sources */,
163 | 1EC8A5F41E1EAD75003E401F /* TodoItemTableCellView.swift in Sources */,
164 | 1E8FB7541E0BCD5200BF6D20 /* TodoItem.swift in Sources */,
165 | 1E8FB75D1E0BF88300BF6D20 /* AddTodoViewController.swift in Sources */,
166 | 1E545FD61E0AE62400D19810 /* EditTodosWindowController.swift in Sources */,
167 | );
168 | runOnlyForDeploymentPostprocessing = 0;
169 | };
170 | /* End PBXSourcesBuildPhase section */
171 |
172 | /* Begin PBXVariantGroup section */
173 | 1E545FCB1E0AD7E500D19810 /* MainMenu.xib */ = {
174 | isa = PBXVariantGroup;
175 | children = (
176 | 1E545FCC1E0AD7E500D19810 /* Base */,
177 | );
178 | name = MainMenu.xib;
179 | sourceTree = "";
180 | };
181 | /* End PBXVariantGroup section */
182 |
183 | /* Begin XCBuildConfiguration section */
184 | 1E545FCF1E0AD7E500D19810 /* Debug */ = {
185 | isa = XCBuildConfiguration;
186 | buildSettings = {
187 | ALWAYS_SEARCH_USER_PATHS = NO;
188 | CLANG_ANALYZER_NONNULL = YES;
189 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
190 | CLANG_CXX_LIBRARY = "libc++";
191 | CLANG_ENABLE_MODULES = YES;
192 | CLANG_ENABLE_OBJC_ARC = YES;
193 | CLANG_WARN_BOOL_CONVERSION = YES;
194 | CLANG_WARN_CONSTANT_CONVERSION = YES;
195 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
196 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
197 | CLANG_WARN_EMPTY_BODY = YES;
198 | CLANG_WARN_ENUM_CONVERSION = YES;
199 | CLANG_WARN_INFINITE_RECURSION = YES;
200 | CLANG_WARN_INT_CONVERSION = YES;
201 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
202 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
203 | CLANG_WARN_UNREACHABLE_CODE = YES;
204 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
205 | CODE_SIGN_IDENTITY = "-";
206 | COPY_PHASE_STRIP = NO;
207 | DEBUG_INFORMATION_FORMAT = dwarf;
208 | ENABLE_STRICT_OBJC_MSGSEND = YES;
209 | ENABLE_TESTABILITY = YES;
210 | GCC_C_LANGUAGE_STANDARD = gnu99;
211 | GCC_DYNAMIC_NO_PIC = NO;
212 | GCC_NO_COMMON_BLOCKS = YES;
213 | GCC_OPTIMIZATION_LEVEL = 0;
214 | GCC_PREPROCESSOR_DEFINITIONS = (
215 | "DEBUG=1",
216 | "$(inherited)",
217 | );
218 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
219 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
220 | GCC_WARN_UNDECLARED_SELECTOR = YES;
221 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
222 | GCC_WARN_UNUSED_FUNCTION = YES;
223 | GCC_WARN_UNUSED_VARIABLE = YES;
224 | MACOSX_DEPLOYMENT_TARGET = 10.12;
225 | MTL_ENABLE_DEBUG_INFO = YES;
226 | ONLY_ACTIVE_ARCH = YES;
227 | SDKROOT = macosx;
228 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
229 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
230 | };
231 | name = Debug;
232 | };
233 | 1E545FD01E0AD7E500D19810 /* Release */ = {
234 | isa = XCBuildConfiguration;
235 | buildSettings = {
236 | ALWAYS_SEARCH_USER_PATHS = NO;
237 | CLANG_ANALYZER_NONNULL = YES;
238 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
239 | CLANG_CXX_LIBRARY = "libc++";
240 | CLANG_ENABLE_MODULES = YES;
241 | CLANG_ENABLE_OBJC_ARC = YES;
242 | CLANG_WARN_BOOL_CONVERSION = YES;
243 | CLANG_WARN_CONSTANT_CONVERSION = YES;
244 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
245 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
246 | CLANG_WARN_EMPTY_BODY = YES;
247 | CLANG_WARN_ENUM_CONVERSION = YES;
248 | CLANG_WARN_INFINITE_RECURSION = YES;
249 | CLANG_WARN_INT_CONVERSION = YES;
250 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
251 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
252 | CLANG_WARN_UNREACHABLE_CODE = YES;
253 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
254 | CODE_SIGN_IDENTITY = "-";
255 | COPY_PHASE_STRIP = NO;
256 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
257 | ENABLE_NS_ASSERTIONS = NO;
258 | ENABLE_STRICT_OBJC_MSGSEND = YES;
259 | GCC_C_LANGUAGE_STANDARD = gnu99;
260 | GCC_NO_COMMON_BLOCKS = YES;
261 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
262 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
263 | GCC_WARN_UNDECLARED_SELECTOR = YES;
264 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
265 | GCC_WARN_UNUSED_FUNCTION = YES;
266 | GCC_WARN_UNUSED_VARIABLE = YES;
267 | MACOSX_DEPLOYMENT_TARGET = 10.12;
268 | MTL_ENABLE_DEBUG_INFO = NO;
269 | SDKROOT = macosx;
270 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
271 | };
272 | name = Release;
273 | };
274 | 1E545FD21E0AD7E500D19810 /* Debug */ = {
275 | isa = XCBuildConfiguration;
276 | buildSettings = {
277 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
278 | COMBINE_HIDPI_IMAGES = YES;
279 | DEVELOPMENT_TEAM = GJX986XEH7;
280 | INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist";
281 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
282 | PRODUCT_BUNDLE_IDENTIFIER = "com.onix-systems.status-bar-todo";
283 | PRODUCT_NAME = "$(TARGET_NAME)";
284 | SWIFT_VERSION = 3.0;
285 | };
286 | name = Debug;
287 | };
288 | 1E545FD31E0AD7E500D19810 /* Release */ = {
289 | isa = XCBuildConfiguration;
290 | buildSettings = {
291 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
292 | COMBINE_HIDPI_IMAGES = YES;
293 | DEVELOPMENT_TEAM = GJX986XEH7;
294 | INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist";
295 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
296 | PRODUCT_BUNDLE_IDENTIFIER = "com.onix-systems.status-bar-todo";
297 | PRODUCT_NAME = "$(TARGET_NAME)";
298 | SWIFT_VERSION = 3.0;
299 | };
300 | name = Release;
301 | };
302 | /* End XCBuildConfiguration section */
303 |
304 | /* Begin XCConfigurationList section */
305 | 1E545FBF1E0AD7E500D19810 /* Build configuration list for PBXProject "status-bar-todo" */ = {
306 | isa = XCConfigurationList;
307 | buildConfigurations = (
308 | 1E545FCF1E0AD7E500D19810 /* Debug */,
309 | 1E545FD01E0AD7E500D19810 /* Release */,
310 | );
311 | defaultConfigurationIsVisible = 0;
312 | defaultConfigurationName = Release;
313 | };
314 | 1E545FD11E0AD7E500D19810 /* Build configuration list for PBXNativeTarget "status-bar-todo" */ = {
315 | isa = XCConfigurationList;
316 | buildConfigurations = (
317 | 1E545FD21E0AD7E500D19810 /* Debug */,
318 | 1E545FD31E0AD7E500D19810 /* Release */,
319 | );
320 | defaultConfigurationIsVisible = 0;
321 | defaultConfigurationName = Release;
322 | };
323 | /* End XCConfigurationList section */
324 | };
325 | rootObject = 1E545FBC1E0AD7E500D19810 /* Project object */;
326 | }
327 |
--------------------------------------------------------------------------------
/status-bar-todo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/status-bar-todo/AddTodoViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddTodoViewController.swift
3 | // todo-status-bar
4 | //
5 | // Created by Stanislav Derpoliuk on 12/22/16.
6 | // Copyright © 2016 Onix-Systems. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | protocol AddTodoViewControllerDelegate: class {
12 | func addTodoViewController(_ controller: AddTodoViewController, didAddTodoWith title: String)
13 | func addTodoViewControllerDidCancel(_ controller: AddTodoViewController)
14 | }
15 |
16 | class AddTodoViewController: NSViewController, NSControlTextEditingDelegate {
17 |
18 | weak var delegate: AddTodoViewControllerDelegate?
19 |
20 | func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool {
21 | guard let string = fieldEditor.string else { return true }
22 | delegate?.addTodoViewController(self, didAddTodoWith: string)
23 | return true
24 | }
25 |
26 | override func cancelOperation(_ sender: Any?) {
27 | delegate?.addTodoViewControllerDidCancel(self)
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/status-bar-todo/AddTodoViewController.xib:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/status-bar-todo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // todo-status-bar
4 | //
5 | // Created by Stanislav Derpoliuk on 12/21/16.
6 | // Copyright © 2016 Onix-Systems. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate, EditTodosWindowControllerDelegate {
13 |
14 | @IBOutlet weak var window: NSWindow!
15 |
16 | let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)
17 | lazy var todoItemsController: TodoItemsController = TodoItemsController()
18 | lazy var editTodosWindowController: EditTodosWindowController = EditTodosWindowController(windowNibName: "EditTodosWindowController")
19 |
20 | func applicationDidFinishLaunching(_ aNotification: Notification) {
21 | updateStatusItem()
22 | }
23 |
24 | // MARK: - Validate Menu Item
25 |
26 | override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
27 | if let todoItem = menuItem.representedObject as? TodoItem {
28 | menuItem.state = todoItem.completed ? NSOnState : NSOffState
29 | guard let isVisible = editTodosWindowController.window?.isVisible else { return true }
30 | return !isVisible
31 | }
32 | return true
33 | }
34 |
35 | // MARK: - Setup Status Item
36 |
37 | private func updateStatusItem() {
38 | updateStatusItemButton()
39 | updateStatsItemMenu()
40 | }
41 |
42 | private func updateStatusItemButton() {
43 | guard let button = statusItem.button else { return }
44 | let totalCount = todoItemsController.todoItems.count
45 | let completedCount = todoItemsController.todoItems.filter { $0.completed }.count
46 | button.title = "☑️\(completedCount)/\(totalCount)"
47 | }
48 |
49 | private func updateStatsItemMenu() {
50 | let menu = NSMenu()
51 | menuItems(todoItems: todoItemsController.todoItems).forEach(menu.addItem)
52 | menu.addItem(NSMenuItem.separator())
53 | menu.addItem(editMenuItem)
54 | menu.addItem(NSMenuItem.separator())
55 | menu.addItem(quitMenuItem)
56 | statusItem.menu = menu
57 | }
58 |
59 | private func menuItems(todoItems: [TodoItem]) -> [NSMenuItem] {
60 | var items = [NSMenuItem]()
61 | todoItems.forEach { todoItem in
62 | let todo = NSMenuItem(title: todoItem.title, action: #selector(menuTodoItemPressed(_:)), keyEquivalent: "")
63 | todo.representedObject = todoItem
64 | if todoItem.completed {
65 | let attributes = [
66 | NSStrikethroughStyleAttributeName: NSNumber(value: NSUnderlineStyle.styleSingle.rawValue),
67 | NSFontAttributeName: NSFont.menuBarFont(ofSize: 0)
68 | ]
69 | let attributedString = NSAttributedString(string: todoItem.title, attributes: attributes)
70 | todo.attributedTitle = attributedString
71 | }
72 | items.append(todo)
73 | }
74 | return items
75 | }
76 |
77 | private var editMenuItem: NSMenuItem {
78 | return NSMenuItem(title: "Edit TODOs...", action: #selector(menuEditItemPressed(_:)), keyEquivalent: "")
79 | }
80 |
81 | private var quitMenuItem: NSMenuItem {
82 | return NSMenuItem(title: "Quit", action: #selector(NSApp.terminate(_:)), keyEquivalent: "")
83 | }
84 |
85 | // MARK: - Menu Actions
86 |
87 | @objc private func menuTodoItemPressed(_ sender: NSMenuItem) {
88 | guard let todoItem = sender.representedObject as? TodoItem else { return }
89 | todoItem.completed = !todoItem.completed
90 | updateStatusItem()
91 | }
92 |
93 | @objc private func menuEditItemPressed(_ sender: NSMenuItem) {
94 | NSApp.activate(ignoringOtherApps: true)
95 | editTodosWindowController.delegate = self
96 | editTodosWindowController.todoItemsController = todoItemsController
97 | editTodosWindowController.window?.center()
98 | editTodosWindowController.window?.makeFirstResponder(nil)
99 | editTodosWindowController.window?.makeKeyAndOrderFront(editTodosWindowController)
100 | editTodosWindowController.tableView.reloadData()
101 | }
102 |
103 | func editTodosWindowControllerDidUpdateTodoItems(_ controller: EditTodosWindowController) {
104 | updateStatusItem()
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/status-bar-todo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/status-bar-todo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/status-bar-todo/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
--------------------------------------------------------------------------------
/status-bar-todo/CheckboxTableCellView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CheckboxTableCellView.swift
3 | // todo-status-bar
4 | //
5 | // Created by Stanislav Derpoliuk on 1/5/17.
6 | // Copyright © 2017 Onix-Systems. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | final class CheckboxTableCellView: NSTableCellView {
12 |
13 | @IBOutlet var checkboxButton: NSButton!
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/status-bar-todo/EditTodosWindowController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EditTodosWindowController.swift
3 | // todo-status-bar
4 | //
5 | // Created by Stanislav Derpoliuk on 12/21/16.
6 | // Copyright © 2016 Onix-Systems. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | protocol EditTodosWindowControllerDelegate: class {
12 | func editTodosWindowControllerDidUpdateTodoItems(_ controller: EditTodosWindowController)
13 | }
14 |
15 | class EditTodosWindowController: NSWindowController, NSTableViewDelegate, NSTableViewDataSource, AddTodoViewControllerDelegate {
16 |
17 | weak var delegate: EditTodosWindowControllerDelegate?
18 | var todoItemsController: TodoItemsController?
19 | @IBOutlet var tableView: NSTableView!
20 |
21 | private var addTodoPanel: NSPanel?
22 |
23 | override func windowDidLoad() {
24 | super.windowDidLoad()
25 | setupTableView()
26 | }
27 |
28 | private func setupTableView() {
29 | tableView.delegate = self
30 | tableView.dataSource = self
31 | tableView.doubleAction = #selector(doubleClick(_:))
32 | }
33 |
34 | private func hideAddTodoPanel() {
35 | guard let window = window, let panel = addTodoPanel else { return }
36 | window.endSheet(panel)
37 | }
38 |
39 | // MAKR: - NSTableViewDataSource
40 |
41 | func numberOfRows(in tableView: NSTableView) -> Int {
42 | return todoItemsController?.todoItems.count ?? 0
43 | }
44 |
45 | // MARK: - NSTableViewDelegate
46 |
47 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
48 | guard
49 | let todoItem = todoItemsController?.todoItems[row],
50 | let identifier = tableColumn?.identifier,
51 | let cellView = tableView.make(withIdentifier: identifier, owner: self) as? NSTableCellView else {
52 | return nil
53 | }
54 | if identifier == "TextCell" {
55 | cellView.textField?.stringValue = todoItem.title
56 | if let cellView = cellView as? TodoItemTableCellView {
57 | cellView.todoItem = todoItem
58 | }
59 | } else if identifier == "CheckboxCell" {
60 | if let cellView = cellView as? CheckboxTableCellView {
61 | cellView.checkboxButton.state = todoItem.completed ? NSOnState : NSOffState
62 | cellView.checkboxButton.tag = row
63 | cellView.checkboxButton.target = self
64 | cellView.checkboxButton.action = #selector(checkboxButtonStateChanged(_:))
65 | }
66 | }
67 | return cellView
68 | }
69 |
70 | // MARK: - AddTodoViewControllerDelegate
71 |
72 | func addTodoViewController(_ controller: AddTodoViewController, didAddTodoWith title: String) {
73 | todoItemsController?.addTodoItem(title: title)
74 | delegate?.editTodosWindowControllerDidUpdateTodoItems(self)
75 | tableView.reloadData()
76 | hideAddTodoPanel()
77 | }
78 |
79 | func addTodoViewControllerDidCancel(_ controller: AddTodoViewController) {
80 | hideAddTodoPanel()
81 | }
82 |
83 | // MARK: - Actions
84 |
85 | @objc private func checkboxButtonStateChanged(_ sender: NSButton) {
86 | guard let todoItemsController = todoItemsController else { return }
87 | let todoItem = todoItemsController.todoItems[sender.tag]
88 | todoItemsController.mark(todoItem: todoItem, completed: sender.state == NSOnState)
89 | delegate?.editTodosWindowControllerDidUpdateTodoItems(self)
90 | }
91 |
92 | @IBAction private func addButtonPressed(_ sender: NSButton) {
93 | let addTodoViewController = AddTodoViewController()
94 | addTodoViewController.delegate = self
95 | guard let window = window else { return }
96 | let panel = NSPanel(contentViewController: addTodoViewController)
97 | var styleMask = panel.styleMask
98 | styleMask.remove(.resizable)
99 | panel.styleMask = styleMask
100 | self.addTodoPanel = panel
101 | window.beginSheet(panel)
102 | }
103 |
104 | @IBAction private func doubleClick(_ tableView: NSTableView) {
105 | let row = tableView.clickedRow
106 | guard let view = tableView.view(atColumn: 1, row: row, makeIfNecessary: false) as? NSTableCellView, let textField = view.textField else {
107 | return
108 | }
109 | textField.isEditable = true
110 | textField.target = self
111 | textField.action = #selector(todoTextFieldDidEndEditing(_:))
112 | textField.tag = row
113 | if textField.acceptsFirstResponder {
114 | window?.makeFirstResponder(textField)
115 | }
116 | }
117 |
118 | @IBAction private func clearAllButtonPressed(_ sender: NSButton) {
119 | let alert = NSAlert()
120 | alert.alertStyle = .critical
121 | alert.messageText = "Are you sure you want to delete all TODOs from the list?"
122 | alert.addButton(withTitle: "Cancel")
123 | alert.addButton(withTitle: "OK")
124 | let response = alert.runModal()
125 | if response == NSAlertSecondButtonReturn {
126 | todoItemsController?.deleteAll()
127 | tableView.reloadData()
128 | delegate?.editTodosWindowControllerDidUpdateTodoItems(self)
129 | }
130 | }
131 |
132 | @IBAction private func todoTextFieldDidEndEditing(_ sender: NSTextField) {
133 | defer {
134 | sender.isEditable = false
135 | }
136 | guard let todoItemsController = todoItemsController else { return }
137 | let todoItem = todoItemsController.todoItems[sender.tag]
138 | todoItemsController.update(title: sender.stringValue, forTodoItem: todoItem)
139 | delegate?.editTodosWindowControllerDidUpdateTodoItems(self)
140 | }
141 |
142 | func deleteMenuItemPressed(_ sender: NSMenuItem) {
143 | guard
144 | let todoItemsController = todoItemsController,
145 | let todoItem = sender.representedObject as? TodoItem,
146 | let index = todoItemsController.todoItems.index(of: todoItem)
147 | else {
148 | return
149 | }
150 | todoItemsController.deleteTodoItem(at: index)
151 | tableView.reloadData()
152 | delegate?.editTodosWindowControllerDidUpdateTodoItems(self)
153 | }
154 |
155 | override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
156 | return true
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/status-bar-todo/EditTodosWindowController.xib:
--------------------------------------------------------------------------------
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 |
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 |
132 |
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 |
--------------------------------------------------------------------------------
/status-bar-todo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
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 | APPL
19 | CFBundleShortVersionString
20 | 0.1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | LSUIElement
26 |
27 | NSHumanReadableCopyright
28 | Copyright © 2016 Onix-Systems. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/status-bar-todo/TodoItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TodoItem.swift
3 | // todo-status-bar
4 | //
5 | // Created by Stanislav Derpoliuk on 12/22/16.
6 | // Copyright © 2016 Onix-Systems. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | final class TodoItem: NSObject, NSCoding {
12 | var title: String
13 | var completed = false
14 |
15 | init(title: String) {
16 | self.title = title
17 | }
18 |
19 | init?(coder aDecoder: NSCoder) {
20 | self.title = (aDecoder.decodeObject(forKey: "title") as? String) ?? ""
21 | self.completed = aDecoder.decodeBool(forKey: "completed")
22 | }
23 |
24 | func encode(with aCoder: NSCoder) {
25 | aCoder.encode(title, forKey: "title")
26 | aCoder.encode(completed, forKey: "completed")
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/status-bar-todo/TodoItemTableCellView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TodoItemTableCellView.swift
3 | // todo-status-bar
4 | //
5 | // Created by Stanislav Derpoliuk on 1/5/17.
6 | // Copyright © 2017 Onix-Systems. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | final class TodoItemTableCellView: NSTableCellView {
12 |
13 | var todoItem: TodoItem?
14 |
15 | override func menu(for event: NSEvent) -> NSMenu? {
16 | let delete = NSMenuItem(title: "Delete", action: #selector(EditTodosWindowController.deleteMenuItemPressed(_:)), keyEquivalent: "")
17 | delete.representedObject = todoItem
18 | let menu = NSMenu()
19 | menu.addItem(delete)
20 | return menu
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/status-bar-todo/TodoItemsController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TodoItemsController.swift
3 | // todo-status-bar
4 | //
5 | // Created by Stanislav Derpoliuk on 12/22/16.
6 | // Copyright © 2016 Onix-Systems. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | final class TodoItemsController {
12 |
13 | private let itemsKey = "TodoItems"
14 | private(set) var todoItems = [TodoItem]()
15 |
16 | init() {
17 | guard
18 | let data = UserDefaults.standard.object(forKey: itemsKey) as? Data,
19 | let items = NSKeyedUnarchiver.unarchiveObject(with: data) as? [TodoItem] else {
20 | return
21 | }
22 | todoItems = items
23 | }
24 |
25 | func mark(todoItem: TodoItem, completed: Bool) {
26 | todoItem.completed = completed
27 | saveTodoItems()
28 | }
29 |
30 | func addTodoItem(title: String) {
31 | let todoItem = TodoItem(title: title)
32 | todoItems.append(todoItem)
33 | saveTodoItems()
34 | }
35 |
36 | func deleteAll() {
37 | todoItems = []
38 | saveTodoItems()
39 | }
40 |
41 | func deleteTodoItem(at index: Int) {
42 | todoItems.remove(at: index)
43 | saveTodoItems()
44 | }
45 |
46 | func update(title: String, forTodoItem todoItem: TodoItem) {
47 | todoItem.title = title
48 | saveTodoItems()
49 | }
50 |
51 | private func saveTodoItems() {
52 | let data = NSKeyedArchiver.archivedData(withRootObject: todoItems)
53 | UserDefaults.standard.set(data, forKey: itemsKey)
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------