Compare commits

..
58 Commits
Author SHA1 Message Date
toxblh 5504e5d640 #171 Added DarkMode button v0.21 2019-05-22 18:25:18 +01:00
toxblh 8a73fe01e4 Revert "#154 .cghidEventTap -> .cgAnnotatedSessionEventTap"
This reverts commit 5a8117ac7f.
2019-05-15 16:40:09 +01:00
toxblh 5fbb2bafc6 Revert "Fix #164"
This reverts commit 40fa61fcb5.
2019-05-15 16:39:52 +01:00
toxblh 5b6b6dfa56 Revert "fixed tests"
This reverts commit 7c9dd26eb0.
2019-05-15 16:39:32 +01:00
toxblh 7c9dd26eb0 fixed tests 2019-05-09 22:19:21 +01:00
toxblh 40fa61fcb5 Fix #164 2019-05-09 22:14:48 +01:00
toxblh 5a8117ac7f #154 .cghidEventTap -> .cgAnnotatedSessionEventTap 2019-05-07 10:37:29 +01:00
toxblh a606767ddc Revert "migration to swift 5"
This reverts commit 4497c7102c.
2019-04-02 17:10:18 +01:00
toxblh 6badc37dc3 Merge branch 'master' of https://github.com/Toxblh/MTMR 2019-04-02 17:07:27 +01:00
toxblh 4497c7102c migration to swift 5 2019-04-02 17:07:23 +01:00
Anton Palgunov b6c6bb945b Update README.md 2019-03-28 13:30:16 +00:00
Anton Palgunov 3e399d14c2 Update README.md 2019-03-27 10:58:41 +00:00
toxblh 624c8ac6d8 upd readme 2019-03-21 21:52:55 +00:00
toxblh ea68ce33c1 Merge branch 'master' of https://github.com/Toxblh/MTMR 2019-03-21 21:52:01 +00:00
toxblh 744ea067db v0.20.1 2019-03-21 21:51:05 +00:00
toxblh 8064ab759c test old build type. try resolve #155 2019-03-21 21:50:38 +00:00
toxblh 556c28df85 flip param for network 2019-03-21 21:49:34 +00:00
Anton Palgunov e68f930efa Update README.md 2019-03-21 12:26:29 +00:00
Anton Palgunov 647ac0ba20 Update README.md 2019-03-03 14:37:56 +00:00
Anton Palgunov 780a8ba81e Update README.md 2019-02-25 09:20:08 +00:00
Anton Palgunov 71f5030fa6 spellchecking 2019-02-24 23:37:58 +00:00
Anton Palgunov f435c0b530 Troubleshooting in Readme 2019-02-24 23:34:56 +00:00
toxblh e278482807 Network plugin 2019-02-24 23:10:53 +00:00
toxblh 51d246bf7f fix tesh script 2019-02-24 23:08:22 +00:00
toxblh c1cd402241 fixed codesign. 2019-02-22 18:12:07 +00:00
toxblh c496156cf2 0.19.5 2019-02-12 21:35:47 +00:00
Anton Palgunov 154ea09e98 Merge pull request #139 from ReDetection/apple-script-robustness
Stable AppleScript
2019-02-04 05:55:38 +00:00
Serg 3b493fd5be create applescript on the queue it will run on 2019-01-28 10:36:04 +07:00
toxblh b718b1c9dd Style format 2019-01-24 00:04:30 +03:00
toxblh acc248a579 Close #135 2019-01-24 00:04:14 +03:00
toxblh 59581ffdc8 Merge branch 'master' of https://github.com/Toxblh/MTMR 2019-01-23 12:43:54 +03:00
toxblh 238b15b6a4 fix drop MTMR without timeZone 2019-01-23 12:43:25 +03:00
toxblh 732fd5c5b5 add helpers for fast update homebrew 2019-01-22 19:20:40 +03:00
toxblh f4b8e1f39a switch from localized to custom templates 2019-01-22 12:46:17 +03:00
Anton Palgunov e988951ff9 Update README.md 2019-01-22 12:26:42 +03:00
toxblh 807290ff0c improve build.sh 2019-01-22 12:21:38 +03:00
toxblh 0775ea0412 added timeZone settings for timeButton. #136 2019-01-22 12:11:31 +03:00
toxblh 1d1d666986 added key full for currency widget. 2019-01-21 19:59:10 +03:00
toxblh 560d8ed508 test.sh for build without xcode 2019-01-17 12:51:39 +00:00
Anton Palgunov 06d6f06adf Merge pull request #134 from koenpunt/patch-1
remove force cast
2019-01-17 11:57:37 +00:00
toxblh 0fafe3912e upd gitignore 2019-01-17 01:51:42 +00:00
Anton Palgunov 319241d4d4 Update README.md 2019-01-17 00:11:40 +00:00
Koen Punt 61d5e8f77e remove force cast 2019-01-16 11:00:31 +01:00
Anton Palgunov e62530d7ab add user preset aadi_vs_anand 2019-01-15 11:39:22 +00:00
Anton Palgunov 43675027d5 aadi_vs_anand files 2019-01-15 11:37:14 +00:00
Anton Palgunov b70c040111 payPal link 2019-01-02 15:58:36 +00:00
toxblh e795f77ba1 Merge branch 'master' of https://github.com/Toxblh/MTMR 2019-01-02 15:56:02 +00:00
toxblh 37b0462bc1 add image 2019-01-02 15:55:56 +00:00
Anton Palgunov d2d33dee3b Update README.md 2018-12-28 23:07:31 +00:00
Anton Palgunov c0fc40b1c9 add Discord link 2018-12-28 23:06:53 +00:00
Anton Palgunov 47a2027443 Merge pull request #122 from Bassoon08/update-readme
Update README
2018-10-30 07:36:51 +00:00
Mel Shafer b3c1d6b323 update readME 2018-10-29 22:06:09 -04:00
Anton Palgunov a15cc6506f Update README.md 2018-10-25 10:33:37 +01:00
toxblh 35a6ceae07 fix tests. Deleted not used code 2018-10-21 00:01:19 +01:00
toxblh be1c439867 upd version 2018-10-20 23:22:42 +01:00
toxblh 9f84b38084 Added pomodoro timer 2018-10-20 23:21:30 +01:00
toxblh 86954a7981 code format 2018-10-20 17:53:49 +01:00
toxblh 8554dfeb5e Fix problem with blocked apps if turn off Hide control Strip.
Also for blocked apps available state in Menu
2018-10-20 12:09:33 +01:00
48 changed files with 1842 additions and 972 deletions
+2
View File
@@ -69,3 +69,5 @@ fastlane/report.xml
fastlane/Preview.html fastlane/Preview.html
fastlane/screenshots fastlane/screenshots
fastlane/test_output fastlane/test_output
.vscode
+25 -9
View File
@@ -38,12 +38,15 @@
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; }; B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; };
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; }; B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; };
B0679BC1215AE73F000FC6B4 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */; }; B0679BC1215AE73F000FC6B4 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */; };
B08126EF217BD0B900A98970 /* PomodoroBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08126EE217BD0B900A98970 /* PomodoroBarItem.swift */; };
B08126F1217BE19000A98970 /* WidgetProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08126F0217BE19000A98970 /* WidgetProtocol.swift */; };
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08173262135F02B005D4908 /* NightShiftBarItem.swift */; }; B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08173262135F02B005D4908 /* NightShiftBarItem.swift */; };
B081732A2135F354005D4908 /* CoreBrightness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B08173292135F354005D4908 /* CoreBrightness.framework */; }; B081732A2135F354005D4908 /* CoreBrightness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B08173292135F354005D4908 /* CoreBrightness.framework */; };
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B081732B213739FE005D4908 /* DnDBarItem.swift */; }; B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B081732B213739FE005D4908 /* DnDBarItem.swift */; };
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B252205C7D8000BC04DC /* AppDelegate.swift */; }; B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B252205C7D8000BC04DC /* AppDelegate.swift */; };
B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B082B256205C7D8000BC04DC /* Assets.xcassets */; }; B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B082B256205C7D8000BC04DC /* Assets.xcassets */; };
B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B082B258205C7D8000BC04DC /* Main.storyboard */; }; B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B082B258205C7D8000BC04DC /* Main.storyboard */; };
B0846A752220C968000288A7 /* NetworkBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0846A742220C968000288A7 /* NetworkBarItem.swift */; };
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */; }; B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */; };
B09EB1E6207C0F8E00D5C1E0 /* MultitouchSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */; }; B09EB1E6207C0F8E00D5C1E0 /* MultitouchSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */; };
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */; }; B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */; };
@@ -58,6 +61,7 @@
B0B17439207D6B590004B740 /* Vox.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */; }; B0B17439207D6B590004B740 /* Vox.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */; };
B0B1743A207D6B590004B740 /* iTunes.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */; }; B0B1743A207D6B590004B740 /* iTunes.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */; };
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */; }; B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */; };
B0F54A7A2295AC7D00B4C509 /* DarkModeBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */; };
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */; }; B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@@ -110,6 +114,8 @@
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TouchBarPrivateApi-Bridging.h"; sourceTree = "<group>"; }; B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TouchBarPrivateApi-Bridging.h"; sourceTree = "<group>"; };
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DFRFoundation.framework; path = ../../../../../System/Library/PrivateFrameworks/DFRFoundation.framework; sourceTree = "<group>"; }; B059D62C205F11E8006E6B86 /* DFRFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DFRFoundation.framework; path = ../../../../../System/Library/PrivateFrameworks/DFRFoundation.framework; sourceTree = "<group>"; };
B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = "<group>"; }; B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = "<group>"; };
B08126EE217BD0B900A98970 /* PomodoroBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PomodoroBarItem.swift; sourceTree = "<group>"; };
B08126F0217BE19000A98970 /* WidgetProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetProtocol.swift; sourceTree = "<group>"; };
B08173262135F02B005D4908 /* NightShiftBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightShiftBarItem.swift; sourceTree = "<group>"; }; B08173262135F02B005D4908 /* NightShiftBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightShiftBarItem.swift; sourceTree = "<group>"; };
B08173282135F128005D4908 /* CBBlueLightClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CBBlueLightClient.h; sourceTree = "<group>"; }; B08173282135F128005D4908 /* CBBlueLightClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CBBlueLightClient.h; sourceTree = "<group>"; };
B08173292135F354005D4908 /* CoreBrightness.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBrightness.framework; path = ../../../../../System/Library/PrivateFrameworks/CoreBrightness.framework; sourceTree = "<group>"; }; B08173292135F354005D4908 /* CoreBrightness.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBrightness.framework; path = ../../../../../System/Library/PrivateFrameworks/CoreBrightness.framework; sourceTree = "<group>"; };
@@ -122,6 +128,7 @@
B082B25C205C7D8000BC04DC /* MTMR.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MTMR.entitlements; sourceTree = "<group>"; }; B082B25C205C7D8000BC04DC /* MTMR.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MTMR.entitlements; sourceTree = "<group>"; };
B082B261205C7D8000BC04DC /* MTMRTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MTMRTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; B082B261205C7D8000BC04DC /* MTMRTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MTMRTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B082B267205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; B082B267205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B0846A742220C968000288A7 /* NetworkBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkBarItem.swift; sourceTree = "<group>"; };
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticFeedback.swift; sourceTree = "<group>"; }; B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticFeedback.swift; sourceTree = "<group>"; };
B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = ../../../../../System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = "<group>"; }; B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = ../../../../../System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = "<group>"; };
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPress.swift; sourceTree = "<group>"; }; B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPress.swift; sourceTree = "<group>"; };
@@ -136,6 +143,7 @@
B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Vox.nowPlaying.scpt; sourceTree = "<group>"; }; B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Vox.nowPlaying.scpt; sourceTree = "<group>"; };
B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iTunes.nowPlaying.scpt; sourceTree = "<group>"; }; B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iTunes.nowPlaying.scpt; sourceTree = "<group>"; };
B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportNSTouchBar.swift; sourceTree = "<group>"; }; B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportNSTouchBar.swift; sourceTree = "<group>"; };
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarkModeBarItem.swift; sourceTree = "<group>"; };
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TouchBarSupport.m; sourceTree = "<group>"; }; B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TouchBarSupport.m; sourceTree = "<group>"; };
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarSupport.h; sourceTree = "<group>"; }; B0F8771B207AC92700D6E430 /* TouchBarSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarSupport.h; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@@ -272,18 +280,22 @@
B0B88A07208CD12000A2C160 /* Widgets */ = { B0B88A07208CD12000A2C160 /* Widgets */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */,
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */,
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */,
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */, 6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */,
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */,
B081732B213739FE005D4908 /* DnDBarItem.swift */,
60669B4220AD8FA80074E817 /* GroupBarItem.swift */,
60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */, 60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */,
60C44AFC20A373A100C0EC91 /* MusicBarItem.swift */, 60C44AFC20A373A100C0EC91 /* MusicBarItem.swift */,
60669B4220AD8FA80074E817 /* GroupBarItem.swift */, B0846A742220C968000288A7 /* NetworkBarItem.swift */,
B08173262135F02B005D4908 /* NightShiftBarItem.swift */, B08173262135F02B005D4908 /* NightShiftBarItem.swift */,
B081732B213739FE005D4908 /* DnDBarItem.swift */, B08126EE217BD0B900A98970 /* PomodoroBarItem.swift */,
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */,
B08126F0217BE19000A98970 /* WidgetProtocol.swift */,
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */,
); );
path = Widgets; path = Widgets;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -334,7 +346,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0920; LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1000; LastUpgradeCheck = 1010;
ORGANIZATIONNAME = "Anton Palgunov"; ORGANIZATIONNAME = "Anton Palgunov";
TargetAttributes = { TargetAttributes = {
B082B24E205C7D8000BC04DC = { B082B24E205C7D8000BC04DC = {
@@ -431,10 +443,14 @@
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */, B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */,
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */, B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */,
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */, 36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */,
B0846A752220C968000288A7 /* NetworkBarItem.swift in Sources */,
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */, B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */,
B08126F1217BE19000A98970 /* WidgetProtocol.swift in Sources */,
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */, B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */,
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */, 6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */,
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */, B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
B0F54A7A2295AC7D00B4C509 /* DarkModeBarItem.swift in Sources */,
B08126EF217BD0B900A98970 /* PomodoroBarItem.swift in Sources */,
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */, B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */,
60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */, 60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */,
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */, B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
@@ -3,6 +3,6 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>BuildSystemType</key> <key>BuildSystemType</key>
<string>Latest</string> <string>Original</string>
</dict> </dict>
</plist> </plist>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1000" LastUpgradeVersion = "1010"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1000" LastUpgradeVersion = "1010"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
+48 -23
View File
@@ -11,16 +11,18 @@ import Sparkle
@NSApplicationMain @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate { class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength) let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
var isBlockedApp: Bool = false
private var fileSystemSource: DispatchSourceFileSystemObject? private var fileSystemSource: DispatchSourceFileSystemObject?
func applicationDidFinishLaunching(_ aNotification: Notification) { func applicationDidFinishLaunching(_: Notification) {
// Configure Sparkle // Configure Sparkle
SUUpdater.shared().automaticallyDownloadsUpdates = false SUUpdater.shared().automaticallyDownloadsUpdates = false
SUUpdater.shared().automaticallyChecksForUpdates = true SUUpdater.shared().automaticallyChecksForUpdates = true
SUUpdater.shared().checkForUpdatesInBackground() SUUpdater.shared().checkForUpdatesInBackground()
let _ = AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true] as NSDictionary) AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true] as NSDictionary)
TouchBarController.shared.setupControlStripPresence() TouchBarController.shared.setupControlStripPresence()
@@ -30,13 +32,33 @@ class AppDelegate: NSObject, NSApplicationDelegate {
createMenu() createMenu()
reloadOnDefaultConfigChanged() reloadOnDefaultConfigChanged()
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(updateIsBlockedApp), name: NSWorkspace.didLaunchApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(updateIsBlockedApp), name: NSWorkspace.didTerminateApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(updateIsBlockedApp), name: NSWorkspace.didActivateApplicationNotification, object: nil)
} }
func applicationWillTerminate(_ aNotification: Notification) { func applicationWillTerminate(_: Notification) {}
@objc func updateIsBlockedApp() {
var blacklistAppIdentifiers: [String] = []
if let blackListed = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.blackListedApps") {
blacklistAppIdentifiers = blackListed
}
var frontmostApplicationIdentifier: String? {
guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil }
return frontmostId
}
if frontmostApplicationIdentifier != nil {
isBlockedApp = blacklistAppIdentifiers.index(of: frontmostApplicationIdentifier!) != nil
} else {
isBlockedApp = false
}
createMenu()
} }
@objc func openPreferences(_ sender: Any?) { @objc func openPreferences(_: Any?) {
let task = Process() let task = Process()
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR") let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
let presetPath = appSupportDirectory.appending("/items.json") let presetPath = appSupportDirectory.appending("/items.json")
@@ -45,13 +67,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
task.launch() task.launch()
} }
@objc func toggleControlStrip(_ sender: Any?) { @objc func toggleControlStrip(_: Any?) {
TouchBarController.shared.showControlStripState = !TouchBarController.shared.showControlStripState TouchBarController.shared.showControlStripState = !TouchBarController.shared.showControlStripState
createMenu()
TouchBarController.shared.resetControlStrip() TouchBarController.shared.resetControlStrip()
createMenu()
} }
@objc func toggleBlackListedApp(_ sender: Any?) { @objc func toggleBlackListedApp(_: Any?) {
let appIdentifier = TouchBarController.shared.frontmostApplicationIdentifier let appIdentifier = TouchBarController.shared.frontmostApplicationIdentifier
if appIdentifier != nil { if appIdentifier != nil {
if let index = TouchBarController.shared.blacklistAppIdentifiers.index(of: appIdentifier!) { if let index = TouchBarController.shared.blacklistAppIdentifiers.index(of: appIdentifier!) {
@@ -64,19 +86,20 @@ class AppDelegate: NSObject, NSApplicationDelegate {
UserDefaults.standard.synchronize() UserDefaults.standard.synchronize()
TouchBarController.shared.updateActiveApp() TouchBarController.shared.updateActiveApp()
updateIsBlockedApp()
} }
} }
@objc func openPreset(_ sender: Any?) { @objc func openPreset(_: Any?) {
let dialog = NSOpenPanel(); let dialog = NSOpenPanel()
dialog.title = "Choose a items.json file" dialog.title = "Choose a items.json file"
dialog.showsResizeIndicator = true dialog.showsResizeIndicator = true
dialog.showsHiddenFiles = true dialog.showsHiddenFiles = true
dialog.canChooseDirectories = false dialog.canChooseDirectories = false
dialog.canCreateDirectories = false dialog.canCreateDirectories = false
dialog.allowsMultipleSelection = false dialog.allowsMultipleSelection = false
dialog.allowedFileTypes = ["json"] dialog.allowedFileTypes = ["json"]
dialog.directoryURL = NSURL.fileURL(withPath: NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR"), isDirectory: true) dialog.directoryURL = NSURL.fileURL(withPath: NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR"), isDirectory: true)
if dialog.runModal() == .OK, let path = dialog.url?.path { if dialog.runModal() == .OK, let path = dialog.url?.path {
@@ -84,7 +107,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} }
} }
@objc func toggleStartAtLogin(_ sender: Any?) { @objc func toggleStartAtLogin(_: Any?) {
LaunchAtLoginController().setLaunchAtLogin(!LaunchAtLoginController().launchAtLogin, for: NSURL.fileURL(withPath: Bundle.main.bundlePath)) LaunchAtLoginController().setLaunchAtLogin(!LaunchAtLoginController().launchAtLogin, for: NSURL.fileURL(withPath: Bundle.main.bundlePath))
createMenu() createMenu()
} }
@@ -95,6 +118,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let startAtLogin = NSMenuItem(title: "Start at login", action: #selector(toggleStartAtLogin(_:)), keyEquivalent: "L") let startAtLogin = NSMenuItem(title: "Start at login", action: #selector(toggleStartAtLogin(_:)), keyEquivalent: "L")
startAtLogin.state = LaunchAtLoginController().launchAtLogin ? .on : .off startAtLogin.state = LaunchAtLoginController().launchAtLogin ? .on : .off
let toggleBlackList = NSMenuItem(title: "Toggle current app in blacklist", action: #selector(toggleBlackListedApp(_:)), keyEquivalent: "B")
toggleBlackList.state = isBlockedApp ? .on : .off
let hideControlStrip = NSMenuItem(title: "Hide Control Strip", action: #selector(toggleControlStrip(_:)), keyEquivalent: "T") let hideControlStrip = NSMenuItem(title: "Hide Control Strip", action: #selector(toggleControlStrip(_:)), keyEquivalent: "T")
hideControlStrip.state = TouchBarController.shared.showControlStripState ? .off : .on hideControlStrip.state = TouchBarController.shared.showControlStripState ? .off : .on
@@ -108,7 +134,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem.separator())
menu.addItem(settingSeparator) menu.addItem(settingSeparator)
menu.addItem(hideControlStrip) menu.addItem(hideControlStrip)
menu.addItem(withTitle: "Toggle current app in blacklist" , action: #selector(toggleBlackListedApp(_:)), keyEquivalent: "B") menu.addItem(toggleBlackList)
menu.addItem(startAtLogin) menu.addItem(startAtLogin)
menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem.separator())
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q") menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
@@ -120,20 +146,19 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let fd = open(file.path, O_EVTONLY) let fd = open(file.path, O_EVTONLY)
self.fileSystemSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fd, eventMask: .write, queue: DispatchQueue(label: "DefaultConfigChanged")) fileSystemSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fd, eventMask: .write, queue: DispatchQueue(label: "DefaultConfigChanged"))
self.fileSystemSource?.setEventHandler(handler: { fileSystemSource?.setEventHandler(handler: {
print("Config changed, reloading...") print("Config changed, reloading...")
DispatchQueue.main.async { DispatchQueue.main.async {
TouchBarController.shared.reloadPreset(path: file.path) TouchBarController.shared.reloadPreset(path: file.path)
} }
}) })
self.fileSystemSource?.setCancelHandler(handler: { fileSystemSource?.setCancelHandler(handler: {
close(fd) close(fd)
}) })
self.fileSystemSource?.resume() fileSystemSource?.resume()
} }
} }
+18 -13
View File
@@ -8,14 +8,20 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval) { init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval) {
self.interval = interval self.interval = interval
super.init(identifier: identifier, title: "⏳") super.init(identifier: identifier, title: "⏳")
self.forceHideConstraint = self.view.widthAnchor.constraint(equalToConstant: 0) forceHideConstraint = view.widthAnchor.constraint(equalToConstant: 0)
guard let script = source.appleScript else { title = "scheduled"
self.title = "no script"
return
}
self.script = script
self.isBordered = false
DispatchQueue.appleScriptQueue.async { DispatchQueue.appleScriptQueue.async {
guard let script = source.appleScript else {
DispatchQueue.main.async {
self.title = "no script"
}
return
}
self.script = script
DispatchQueue.main.async {
self.isBordered = false
}
var error: NSDictionary? var error: NSDictionary?
guard script.compileAndReturnError(&error) else { guard script.compileAndReturnError(&error) else {
#if DEBUG #if DEBUG
@@ -30,23 +36,23 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
} }
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func refreshAndSchedule() { func refreshAndSchedule() {
#if DEBUG #if DEBUG
print("refresh happened (interval \(self.interval)), self \(self.identifier.rawValue))") print("refresh happened (interval \(interval)), self \(identifier.rawValue))")
#endif #endif
let scriptResult = self.execute() let scriptResult = execute()
DispatchQueue.main.async { DispatchQueue.main.async {
self.title = scriptResult self.title = scriptResult
self.forceHideConstraint.isActive = scriptResult == "" self.forceHideConstraint.isActive = scriptResult == ""
#if DEBUG #if DEBUG
print("did set new script result title \(scriptResult)") print("did set new script result title \(scriptResult)")
#endif #endif
} }
DispatchQueue.appleScriptQueue.asyncAfter(deadline: .now() + self.interval) { [weak self] in DispatchQueue.appleScriptQueue.asyncAfter(deadline: .now() + interval) { [weak self] in
self?.refreshAndSchedule() self?.refreshAndSchedule()
} }
} }
@@ -60,7 +66,6 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
} }
return output.stringValue ?? "" return output.stringValue ?? ""
} }
} }
extension DispatchQueue { extension DispatchQueue {
@@ -0,0 +1,24 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "sun-icon-256.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

@@ -0,0 +1,24 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "39857.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
+20 -25
View File
@@ -9,15 +9,15 @@
import Cocoa import Cocoa
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate { class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
var tapClosure: (() -> ())? var tapClosure: (() -> Void)?
var longTapClosure: (() -> ())? var longTapClosure: (() -> Void)?
private var button: NSButton! private var button: NSButton!
private var singleClick: NSClickGestureRecognizer! private var singleClick: NSClickGestureRecognizer!
private var longClick: NSPressGestureRecognizer! private var longClick: NSPressGestureRecognizer!
init(identifier: NSTouchBarItem.Identifier, title: String) { init(identifier: NSTouchBarItem.Identifier, title: String) {
self.attributedTitle = title.defaultTouchbarAttributedString attributedTitle = title.defaultTouchbarAttributedString
super.init(identifier: identifier) super.init(identifier: identifier)
button = CustomHeightButton(title: title, target: nil, action: nil) button = CustomHeightButton(title: title, target: nil, action: nil)
@@ -34,7 +34,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
button.attributedTitle = attributedTitle button.attributedTitle = attributedTitle
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@@ -52,17 +52,17 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
var title: String { var title: String {
get { get {
return self.attributedTitle.string return attributedTitle.string
} }
set { set {
self.attributedTitle = newValue.defaultTouchbarAttributedString attributedTitle = newValue.defaultTouchbarAttributedString
} }
} }
var attributedTitle: NSAttributedString { var attributedTitle: NSAttributedString {
didSet { didSet {
self.button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
self.button?.attributedTitle = attributedTitle button?.attributedTitle = attributedTitle
} }
} }
@@ -88,12 +88,12 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
button.imageScaling = .scaleProportionallyDown button.imageScaling = .scaleProportionallyDown
button.imageHugsTitle = true button.imageHugsTitle = true
button.attributedTitle = title button.attributedTitle = title
self.button?.imagePosition = title.length > 0 ? .imageLeading : .imageOnly button?.imagePosition = title.length > 0 ? .imageLeading : .imageOnly
button.image = image button.image = image
self.view = button view = button
self.view.addGestureRecognizer(longClick) view.addGestureRecognizer(longClick)
self.view.addGestureRecognizer(singleClick) view.addGestureRecognizer(singleClick)
} }
func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool { func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool {
@@ -108,7 +108,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
switch gr.state { switch gr.state {
case .ended: case .ended:
hf.tap(strong: 2) hf.tap(strong: 2)
self.tapClosure?() tapClosure?()
break break
default: default:
break break
@@ -130,19 +130,16 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
break break
default: default:
break break
} }
} }
} }
class CustomHeightButton : NSButton { class CustomHeightButton: NSButton {
override var intrinsicContentSize: NSSize { override var intrinsicContentSize: NSSize {
var size = super.intrinsicContentSize var size = super.intrinsicContentSize
size.height = 30 size.height = 30
return size return size
} }
} }
class CustomButtonCell: NSButtonCell { class CustomButtonCell: NSButtonCell {
@@ -155,32 +152,30 @@ class CustomButtonCell: NSButtonCell {
override func highlight(_ flag: Bool, withFrame cellFrame: NSRect, in controlView: NSView) { override func highlight(_ flag: Bool, withFrame cellFrame: NSRect, in controlView: NSView) {
super.highlight(flag, withFrame: cellFrame, in: controlView) super.highlight(flag, withFrame: cellFrame, in: controlView)
if !self.isBordered { if !isBordered {
if flag { if flag {
self.setAttributedTitle(self.attributedTitle, withColor: .lightGray) setAttributedTitle(attributedTitle, withColor: .lightGray)
} else if let parentItem = self.parentItem { } else if let parentItem = self.parentItem {
self.attributedTitle = parentItem.attributedTitle attributedTitle = parentItem.attributedTitle
} }
} }
} }
required init(coder: NSCoder) { required init(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func setAttributedTitle(_ title: NSAttributedString, withColor color: NSColor) { func setAttributedTitle(_ title: NSAttributedString, withColor color: NSColor) {
let attrTitle = NSMutableAttributedString(attributedString: title) let attrTitle = NSMutableAttributedString(attributedString: title)
attrTitle.addAttributes([.foregroundColor: color], range: NSRange(location: 0, length: attrTitle.length)) attrTitle.addAttributes([.foregroundColor: color], range: NSRange(location: 0, length: attrTitle.length))
self.attributedTitle = attrTitle attributedTitle = attrTitle
} }
} }
extension String { extension String {
var defaultTouchbarAttributedString: NSAttributedString { var defaultTouchbarAttributedString: NSAttributedString {
let attrTitle = NSMutableAttributedString(string: self, attributes: [.foregroundColor: NSColor.white, .font: NSFont.systemFont(ofSize: 15, weight: .regular), .baselineOffset: 1]) let attrTitle = NSMutableAttributedString(string: self, attributes: [.foregroundColor: NSColor.white, .font: NSFont.systemFont(ofSize: 15, weight: .regular), .baselineOffset: 1])
attrTitle.setAlignment(.center, range: NSRange(location: 0, length: self.count)) attrTitle.setAlignment(.center, range: NSRange(location: 0, length: count))
return attrTitle return attrTitle
} }
} }
+19 -20
View File
@@ -9,9 +9,9 @@
import Foundation import Foundation
class CustomSliderCell: NSSliderCell { class CustomSliderCell: NSSliderCell {
var knobImage:NSImage! var knobImage: NSImage!
private var _currentKnobRect:NSRect! private var _currentKnobRect: NSRect!
private var _barRect:NSRect! private var _barRect: NSRect!
required init(coder aDecoder: NSCoder) { required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
@@ -21,22 +21,22 @@ class CustomSliderCell: NSSliderCell {
super.init() super.init()
} }
init(knob:NSImage?) { init(knob: NSImage?) {
knobImage = knob; knobImage = knob
super.init() super.init()
} }
override func drawKnob(_ knobRect: NSRect) { override func drawKnob(_ knobRect: NSRect) {
if (knobImage == nil) { if knobImage == nil {
super.drawKnob(knobRect) super.drawKnob(knobRect)
return; return
} }
_currentKnobRect = knobRect; _currentKnobRect = knobRect
drawBar(inside: _barRect, flipped: true) drawBar(inside: _barRect, flipped: true)
let x = (knobRect.origin.x * (_barRect.size.width - (knobImage.size.width - knobRect.size.width)) / _barRect.size.width)+1; let x = (knobRect.origin.x * (_barRect.size.width - (knobImage.size.width - knobRect.size.width)) / _barRect.size.width) + 1
let y = knobRect.origin.y+3 let y = knobRect.origin.y + 3
knobImage.draw( knobImage.draw(
at: NSPoint(x: x, y: y), at: NSPoint(x: x, y: y),
@@ -46,7 +46,7 @@ class CustomSliderCell: NSSliderCell {
) )
} }
override func drawBar(inside aRect: NSRect, flipped: Bool) { override func drawBar(inside aRect: NSRect, flipped _: Bool) {
_barRect = aRect _barRect = aRect
let barRadius = CGFloat(2) let barRadius = CGFloat(2)
@@ -60,16 +60,15 @@ class CustomSliderCell: NSSliderCell {
var activeRect = bgRect var activeRect = bgRect
activeRect.size.width = CGFloat((Double(bgRect.size.width) / (self.maxValue - self.minValue)) * self.doubleValue) activeRect.size.width = CGFloat((Double(bgRect.size.width) / (maxValue - minValue)) * doubleValue)
let active = NSBezierPath(roundedRect: activeRect, xRadius: barRadius, yRadius: barRadius) let active = NSBezierPath(roundedRect: activeRect, xRadius: barRadius, yRadius: barRadius)
NSColor.darkGray.setFill() NSColor.darkGray.setFill()
active.fill() active.fill()
} }
} }
class CustomSlider:NSSlider { class CustomSlider: NSSlider {
var currentValue: CGFloat = 0
var currentValue:CGFloat = 0
override func setNeedsDisplay(_ invalidRect: NSRect) { override func setNeedsDisplay(_ invalidRect: NSRect) {
super.setNeedsDisplay(invalidRect) super.setNeedsDisplay(invalidRect)
@@ -77,15 +76,15 @@ class CustomSlider:NSSlider {
override func awakeFromNib() { override func awakeFromNib() {
super.awakeFromNib() super.awakeFromNib()
if ((self.cell?.isKind(of: CustomSliderCell.self)) == false) { if (cell?.isKind(of: CustomSliderCell.self)) == false {
let cell:CustomSliderCell = CustomSliderCell() let cell: CustomSliderCell = CustomSliderCell()
self.cell = cell self.cell = cell
} }
} }
convenience init(knob:NSImage) { convenience init(knob: NSImage) {
self.init() self.init()
self.cell = CustomSliderCell(knob: knob) cell = CustomSliderCell(knob: knob)
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
@@ -101,7 +100,7 @@ class CustomSlider:NSSlider {
return cell.knobImage return cell.knobImage
} }
func setKnobImage(image:NSImage) { func setKnobImage(image: NSImage) {
let cell = self.cell as! CustomSliderCell let cell = self.cell as! CustomSliderCell
cell.knobImage = image cell.knobImage = image
} }
+1 -3
View File
@@ -11,9 +11,7 @@ import Foundation
#endif #endif
extension String { extension String {
var ifNotEmpty: String? { var ifNotEmpty: String? {
return self.count > 0 ? self : nil return count > 0 ? self : nil
} }
} }
+2 -4
View File
@@ -10,7 +10,7 @@ import IOKit
class HapticFeedback { class HapticFeedback {
private var actuatorRef: CFTypeRef? private var actuatorRef: CFTypeRef?
private var deviceID: UInt64 = 0x200000001000000 private var deviceID: UInt64 = 0x200_0000_0100_0000
private var error: IOReturn = 0 private var error: IOReturn = 0
// Don't know how to do strong is enum one of // Don't know how to do strong is enum one of
@@ -24,7 +24,7 @@ class HapticFeedback {
// 16 nothing // 16 nothing
// you can get a plist `otool -s __TEXT __tpad_act_plist /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/Current/MultitouchSupport|tail -n +3|awk -F'\t' '{print $2}'|xxd -r -p` // you can get a plist `otool -s __TEXT __tpad_act_plist /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/Current/MultitouchSupport|tail -n +3|awk -F'\t' '{print $2}'|xxd -r -p`
func tap(strong:Int32) -> Void { func tap(strong: Int32) {
actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue() actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue()
guard actuatorRef != nil else { guard actuatorRef != nil else {
@@ -53,5 +53,3 @@ class HapticFeedback {
return return
} }
} }
+2 -2
View File
@@ -17,9 +17,9 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.18.5</string> <string>0.21</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>55</string> <string>207</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string> <string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
+232 -106
View File
@@ -1,9 +1,9 @@
import Foundation
import AppKit import AppKit
import Foundation
extension Data { extension Data {
func barItemDefinitions() -> [BarItemDefinition]? { func barItemDefinitions() -> [BarItemDefinition]? {
return try? JSONDecoder().decode([BarItemDefinition].self, from: self.utf8string!.stripComments().data(using: .utf8)!) return try? JSONDecoder().decode([BarItemDefinition].self, from: utf8string!.stripComments().data(using: .utf8)!)
} }
} }
@@ -17,7 +17,7 @@ struct BarItemDefinition: Decodable {
case type case type
} }
init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) { init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]) {
self.type = type self.type = type
self.action = action self.action = action
self.longAction = longAction self.longAction = longAction
@@ -38,68 +38,133 @@ struct BarItemDefinition: Decodable {
self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, additionalParameters: additionalParameters) self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, additionalParameters: additionalParameters)
} }
} }
} }
class SupportedTypesHolder { typealias ParametersDecoder = (Decoder) throws -> (
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, longAction: LongActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter]) item: ItemType,
private var supportedTypes: [String: ParametersDecoder] = [ action: ActionType,
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), longAction: .none, parameters: [.align: .align(.left)]) }, longAction: LongActionType,
parameters: [GeneralParameters.CodingKeys: GeneralParameter]
)
"delete": { _ in return (item: .staticButton(title: "del"), action: .keyPress(keycode: 117), longAction: .none, parameters: [:])}, class SupportedTypesHolder {
private var supportedTypes: [String: ParametersDecoder] = [
"escape": { _ in (
item: .staticButton(title: "esc"),
action: .keyPress(keycode: 53),
longAction: .none,
parameters: [.align: .align(.left)]
) },
"delete": { _ in (
item: .staticButton(title: "del"),
action: .keyPress(keycode: 117),
longAction: .none,
parameters: [:]
) },
"brightnessUp": { _ in "brightnessUp": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp")) let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
return (item: .staticButton(title: ""), action: .keyPress(keycode: 144), longAction: .none, parameters: [.image: imageParameter]) return (
item: .staticButton(title: ""),
action: .keyPress(keycode: 144),
longAction: .none,
parameters: [.image: imageParameter]
)
}, },
"brightnessDown": { _ in "brightnessDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown")) let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
return (item: .staticButton(title: ""), action: .keyPress(keycode: 145), longAction: .none, parameters: [.image: imageParameter]) return (
item: .staticButton(title: ""),
action: .keyPress(keycode: 145),
longAction: .none,
parameters: [.image: imageParameter]
)
}, },
"illuminationUp": { _ in "illuminationUp": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_up")) let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_up"))
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_UP), longAction: .none, parameters: [.image: imageParameter]) return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_UP),
longAction: .none,
parameters: [.image: imageParameter]
)
}, },
"illuminationDown": { _ in "illuminationDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_down")) let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_down"))
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_DOWN), longAction: .none, parameters: [.image: imageParameter]) return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_DOWN),
longAction: .none,
parameters: [.image: imageParameter]
)
}, },
"volumeDown": { _ in "volumeDown": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeDownTemplateName)!) let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeDownTemplateName)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), longAction: .none, parameters: [.image: imageParameter]) return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN),
longAction: .none,
parameters: [.image: imageParameter]
)
}, },
"volumeUp": { _ in "volumeUp": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeUpTemplateName)!) let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeUpTemplateName)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), longAction: .none, parameters: [.image: imageParameter]) return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP),
longAction: .none,
parameters: [.image: imageParameter]
)
}, },
"mute": { _ in "mute": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarAudioOutputMuteTemplateName)!) let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarAudioOutputMuteTemplateName)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), longAction: .none, parameters: [.image: imageParameter]) return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_MUTE),
longAction: .none,
parameters: [.image: imageParameter]
)
}, },
"previous": { _ in "previous": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarRewindTemplateName)!) let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarRewindTemplateName)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), longAction: .none, parameters: [.image: imageParameter]) return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS),
longAction: .none,
parameters: [.image: imageParameter]
)
}, },
"play": { _ in "play": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarPlayPauseTemplateName)!) let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarPlayPauseTemplateName)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), longAction: .none, parameters: [.image: imageParameter]) return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_PLAY),
longAction: .none,
parameters: [.image: imageParameter]
)
}, },
"next": { _ in "next": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarFastForwardTemplateName)!) let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarFastForwardTemplateName)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), longAction: .none, parameters: [.image: imageParameter]) return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_NEXT),
longAction: .none,
parameters: [.image: imageParameter]
)
}, },
"weather": { decoder in "weather": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key ; case icon_type } enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key; case icon_type }
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
let units = try container.decodeIfPresent(String.self, forKey: .units) let units = try container.decodeIfPresent(String.self, forKey: .units)
@@ -107,35 +172,66 @@ class SupportedTypesHolder {
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type)
let action = try ActionType(from: decoder) let action = try ActionType(from: decoder)
let longAction = try LongActionType(from: decoder) let longAction = try LongActionType(from: decoder)
return (item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"), action: action, longAction: longAction, parameters: [:]) return (
item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"),
action,
longAction,
parameters: [:]
)
}, },
"currency": { decoder in "currency": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to } enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to; case full }
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
let from = try container.decodeIfPresent(String.self, forKey: .from) let from = try container.decodeIfPresent(String.self, forKey: .from)
let to = try container.decodeIfPresent(String.self, forKey: .to) let to = try container.decodeIfPresent(String.self, forKey: .to)
let full = try container.decodeIfPresent(Bool.self, forKey: .full)
let action = try ActionType(from: decoder) let action = try ActionType(from: decoder)
let longAction = try LongActionType(from: decoder) let longAction = try LongActionType(from: decoder)
return (item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), action: action, longAction: longAction, parameters: [:]) return (
item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD", full: full ?? false),
action,
longAction,
parameters: [:]
)
}, },
"dock": { decoder in "dock": { _ in
return (item: .dock(), action: .none, longAction: .none, parameters: [:]) (
item: .dock(),
action: .none,
longAction: .none,
parameters: [:]
)
}, },
"inputsource": { decoder in "inputsource": { _ in
return (item: .inputsource(), action: .none, longAction: .none, parameters: [:]) (
item: .inputsource(),
action: .none,
longAction: .none,
parameters: [:]
)
}, },
"volume": { decoder in "volume": { decoder in
enum CodingKeys: String, CodingKey { case image } enum CodingKeys: String, CodingKey { case image }
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
if var img = try container.decodeIfPresent(Source.self, forKey: .image) { if var img = try container.decodeIfPresent(Source.self, forKey: .image) {
return (item: .volume(), action: .none, longAction: .none, parameters: [.image: .image(source: img)]) return (
item: .volume(),
action: .none,
longAction: .none,
parameters: [.image: .image(source: img)]
)
} else { } else {
return (item: .volume(), action: .none, longAction: .none, parameters: [:]) return (
item: .volume(),
action: .none,
longAction: .none,
parameters: [:]
)
} }
}, },
@@ -144,15 +240,35 @@ class SupportedTypesHolder {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
if var img = try container.decodeIfPresent(Source.self, forKey: .image) { if var img = try container.decodeIfPresent(Source.self, forKey: .image) {
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [.image: .image(source: img)]) return (
item: .brightness(refreshInterval: interval ?? 0.5),
action: .none,
longAction: .none,
parameters: [.image: .image(source: img)]
)
} else { } else {
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [:]) return (
item: .brightness(refreshInterval: interval ?? 0.5),
action: .none,
longAction: .none,
parameters: [:]
)
} }
}, },
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), longAction: .none, parameters: [:]) }, "sleep": { _ in (
item: .staticButton(title: "☕️"),
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]),
longAction: .none,
parameters: [:]
) },
"displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), longAction: .none, parameters: [:])}, "displaySleep": { _ in (
item: .staticButton(title: "☕️"),
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]),
longAction: .none,
parameters: [:]
) },
"music": { decoder in "music": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval } enum CodingKeys: String, CodingKey { case refreshInterval }
@@ -177,33 +293,17 @@ class SupportedTypesHolder {
parameters: [:] parameters: [:]
) )
}, },
"nightShift": {_ in
return (
item: .nightShift(),
action: .none,
longAction: .none,
parameters: [:]
)
},
"dnd": { _ in
return (
item: .dnd(),
action: .none,
longAction: .none,
parameters: [:]
)
}
] ]
static let sharedInstance = SupportedTypesHolder() static let sharedInstance = SupportedTypesHolder()
func lookup(by type: String) -> ParametersDecoder { func lookup(by type: String) -> ParametersDecoder {
return supportedTypes[type] ?? { decoder in return supportedTypes[type] ?? { decoder in (
return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), longAction: try LongActionType(from: decoder), parameters: [:]) item: try ItemType(from: decoder),
} action: try ActionType(from: decoder),
longAction: try LongActionType(from: decoder),
parameters: [:]
) }
} }
func register(typename: String, decoder: @escaping ParametersDecoder) { func register(typename: String, decoder: @escaping ParametersDecoder) {
@@ -212,7 +312,12 @@ class SupportedTypesHolder {
func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) { func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) {
register(typename: typename) { _ in register(typename: typename) { _ in
return (item: item, action: action, longAction: longAction, parameters: [:]) (
item: item,
action,
longAction,
parameters: [:]
)
} }
} }
} }
@@ -220,18 +325,21 @@ class SupportedTypesHolder {
enum ItemType: Decodable { enum ItemType: Decodable {
case staticButton(title: String) case staticButton(title: String)
case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double) case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double)
case timeButton(formatTemplate: String) case timeButton(formatTemplate: String, timeZone: String?)
case battery() case battery()
case dock() case dock()
case volume() case volume()
case brightness(refreshInterval: Double) case brightness(refreshInterval: Double)
case weather(interval: Double, units: String, api_key: String, icon_type: String) case weather(interval: Double, units: String, api_key: String, icon_type: String)
case currency(interval: Double, from: String, to: String) case currency(interval: Double, from: String, to: String, full: Bool)
case inputsource() case inputsource()
case music(interval: Double) case music(interval: Double)
case groupBar(items: [BarItemDefinition]) case groupBar(items: [BarItemDefinition])
case nightShift() case nightShift()
case dnd() case dnd()
case pomodoro(workTime: Double, restTime: Double)
case network(flip: Bool)
case darkMode()
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case type case type
@@ -240,6 +348,8 @@ enum ItemType: Decodable {
case refreshInterval case refreshInterval
case from case from
case to case to
case full
case timeZone
case units case units
case api_key case api_key
case icon_type case icon_type
@@ -248,6 +358,9 @@ enum ItemType: Decodable {
case url case url
case longUrl case longUrl
case items case items
case workTime
case restTime
case flip
} }
enum ItemTypeRaw: String, Decodable { enum ItemTypeRaw: String, Decodable {
@@ -265,13 +378,15 @@ enum ItemType: Decodable {
case groupBar case groupBar
case nightShift case nightShift
case dnd case dnd
case pomodoro
case network
case darkMode
} }
init(from decoder: Decoder) throws { init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(ItemTypeRaw.self, forKey: .type) let type = try container.decode(ItemTypeRaw.self, forKey: .type)
switch type { switch type {
case .appleScriptTitledButton: case .appleScriptTitledButton:
let source = try container.decode(Source.self, forKey: .source) let source = try container.decode(Source.self, forKey: .source)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0 let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
@@ -283,7 +398,8 @@ enum ItemType: Decodable {
case .timeButton: case .timeButton:
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm" let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
self = .timeButton(formatTemplate: template) let timeZone = try container.decodeIfPresent(String.self, forKey: .timeZone) ?? nil
self = .timeButton(formatTemplate: template, timeZone: timeZone)
case .battery: case .battery:
self = .battery() self = .battery()
@@ -309,7 +425,8 @@ enum ItemType: Decodable {
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0 let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB" let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD" let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
self = .currency(interval: interval, from: from, to: to) let full = try container.decodeIfPresent(Bool.self, forKey: .full) ?? false
self = .currency(interval: interval, from: from, to: to, full: full)
case .inputsource: case .inputsource:
self = .inputsource() self = .inputsource()
@@ -327,6 +444,18 @@ enum ItemType: Decodable {
case .dnd: case .dnd:
self = .dnd() self = .dnd()
case .pomodoro:
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime) ?? 1500.0
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime) ?? 600.0
self = .pomodoro(workTime: workTime, restTime: restTime)
case .network:
let flip = try container.decodeIfPresent(Bool.self, forKey: .flip) ?? false
self = .network(flip: flip)
case .darkMode:
self = .darkMode()
} }
} }
} }
@@ -337,7 +466,7 @@ enum ActionType: Decodable {
case keyPress(keycode: Int) case keyPress(keycode: Int)
case appleScript(source: SourceProtocol) case appleScript(source: SourceProtocol)
case shellScript(executable: String, parameters: [String]) case shellScript(executable: String, parameters: [String])
case custom(closure: ()->()) case custom(closure: () -> Void)
case openUrl(url: String) case openUrl(url: String)
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
@@ -362,41 +491,40 @@ enum ActionType: Decodable {
let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action) let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
switch type { switch type {
case .some(.hidKey): case .some(.hidKey):
let keycode = try container.decode(Int32.self, forKey: .keycode) let keycode = try container.decode(Int32.self, forKey: .keycode)
self = .hidKey(keycode: keycode) self = .hidKey(keycode: keycode)
case .some(.keyPress): case .some(.keyPress):
let keycode = try container.decode(Int.self, forKey: .keycode) let keycode = try container.decode(Int.self, forKey: .keycode)
self = .keyPress(keycode: keycode) self = .keyPress(keycode: keycode)
case .some(.appleScript): case .some(.appleScript):
let source = try container.decode(Source.self, forKey: .actionAppleScript) let source = try container.decode(Source.self, forKey: .actionAppleScript)
self = .appleScript(source: source) self = .appleScript(source: source)
case .some(.shellScript): case .some(.shellScript):
let executable = try container.decode(String.self, forKey: .executablePath) let executable = try container.decode(String.self, forKey: .executablePath)
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? [] let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
self = .shellScript(executable: executable, parameters: parameters) self = .shellScript(executable: executable, parameters: parameters)
case .some(.openUrl): case .some(.openUrl):
let url = try container.decode(String.self, forKey: .url) let url = try container.decode(String.self, forKey: .url)
self = .openUrl(url: url) self = .openUrl(url: url)
case .none: case .none:
self = .none self = .none
} }
} }
} }
enum LongActionType: Decodable { enum LongActionType: Decodable {
case none case none
case hidKey(keycode: Int32) case hidKey(keycode: Int32)
case keyPress(keycode: Int) case keyPress(keycode: Int)
case appleScript(source: SourceProtocol) case appleScript(source: SourceProtocol)
case shellScript(executable: String, parameters: [String]) case shellScript(executable: String, parameters: [String])
case custom(closure: ()->()) case custom(closure: () -> Void)
case openUrl(url: String) case openUrl(url: String)
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
@@ -421,41 +549,40 @@ enum LongActionType: Decodable {
let longType = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction) let longType = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction)
switch longType { switch longType {
case .some(.hidKey): case .some(.hidKey):
let keycode = try container.decode(Int32.self, forKey: .longKeycode) let keycode = try container.decode(Int32.self, forKey: .longKeycode)
self = .hidKey(keycode: keycode) self = .hidKey(keycode: keycode)
case .some(.keyPress): case .some(.keyPress):
let keycode = try container.decode(Int.self, forKey: .longKeycode) let keycode = try container.decode(Int.self, forKey: .longKeycode)
self = .keyPress(keycode: keycode) self = .keyPress(keycode: keycode)
case .some(.appleScript): case .some(.appleScript):
let source = try container.decode(Source.self, forKey: .longActionAppleScript) let source = try container.decode(Source.self, forKey: .longActionAppleScript)
self = .appleScript(source: source) self = .appleScript(source: source)
case .some(.shellScript): case .some(.shellScript):
let executable = try container.decode(String.self, forKey: .longExecutablePath) let executable = try container.decode(String.self, forKey: .longExecutablePath)
let parameters = try container.decodeIfPresent([String].self, forKey: .longShellArguments) ?? [] let parameters = try container.decodeIfPresent([String].self, forKey: .longShellArguments) ?? []
self = .shellScript(executable: executable, parameters: parameters) self = .shellScript(executable: executable, parameters: parameters)
case .some(.openUrl): case .some(.openUrl):
let longUrl = try container.decode(String.self, forKey: .longUrl) let longUrl = try container.decode(String.self, forKey: .longUrl)
self = .openUrl(url: longUrl) self = .openUrl(url: longUrl)
case .none: case .none:
self = .none self = .none
} }
} }
} }
enum GeneralParameter { enum GeneralParameter {
case width(_: CGFloat) case width(_: CGFloat)
case image(source: SourceProtocol) case image(source: SourceProtocol)
case align(_: Align) case align(_: Align)
case bordered(_: Bool) case bordered(_: Bool)
case background(_:NSColor) case background(_: NSColor)
case title(_:String) case title(_: String)
} }
struct GeneralParameters: Decodable { struct GeneralParameters: Decodable {
@@ -532,7 +659,7 @@ struct Source: Decodable, SourceProtocol {
} }
var appleScript: NSAppleScript? { var appleScript: NSAppleScript? {
return filePath?.fileURL.appleScript ?? self.string?.appleScript return filePath?.fileURL.appleScript ?? string?.appleScript
} }
private init(filePath: String?, base64: String?, inline: String?) { private init(filePath: String?, base64: String?, inline: String?) {
@@ -577,7 +704,6 @@ extension String {
var encoding: String.Encoding = .utf8 var encoding: String.Encoding = .utf8
return try? String(contentsOfFile: self, usedEncoding: &encoding) return try? String(contentsOfFile: self, usedEncoding: &encoding)
} }
} }
extension Data { extension Data {
@@ -608,7 +734,7 @@ extension String {
extension URL { extension URL {
var appleScript: NSAppleScript? { var appleScript: NSAppleScript? {
guard FileManager.default.fileExists(atPath: self.path) else { return nil } guard FileManager.default.fileExists(atPath: path) else { return nil }
return NSAppleScript(contentsOf: self, error: nil) return NSAppleScript(contentsOf: self, error: nil)
} }
} }
+1 -1
View File
@@ -18,7 +18,7 @@ struct GenericKeyPress: KeyPress {
} }
extension KeyPress { extension KeyPress {
func send () { func send() {
let src = CGEventSource(stateID: .hidSystemState) let src = CGEventSource(stateID: .hidSystemState)
let keyDown = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: true) let keyDown = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: true)
let keyUp = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: false) let keyUp = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: false)
+6 -6
View File
@@ -12,20 +12,20 @@ class ScrollViewItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
stackView.orientation = .horizontal stackView.orientation = .horizontal
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize)) let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
scrollView.documentView = stackView scrollView.documentView = stackView
self.view = scrollView view = scrollView
let twofingers = NSPanGestureRecognizer(target: self, action: #selector(twofingersHandler(_:))) let twofingers = NSPanGestureRecognizer(target: self, action: #selector(twofingersHandler(_:)))
twofingers.allowedTouchTypes = .direct twofingers.allowedTouchTypes = .direct
twofingers.numberOfTouchesRequired = 2 twofingers.numberOfTouchesRequired = 2
self.view.addGestureRecognizer(twofingers) view.addGestureRecognizer(twofingers)
let threefingers = NSPanGestureRecognizer(target: self, action: #selector(threefingersHandler(_:))) let threefingers = NSPanGestureRecognizer(target: self, action: #selector(threefingersHandler(_:)))
threefingers.allowedTouchTypes = .direct threefingers.allowedTouchTypes = .direct
threefingers.numberOfTouchesRequired = 3 threefingers.numberOfTouchesRequired = 3
self.view.addGestureRecognizer(threefingers) view.addGestureRecognizer(threefingers)
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@@ -36,7 +36,7 @@ class ScrollViewItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
case .began: case .began:
twofingersPrev = position twofingersPrev = position
case .changed: case .changed:
if (((position-twofingersPrev) > 10) || ((twofingersPrev-position) > 10)) { if ((position - twofingersPrev) > 10) || ((twofingersPrev - position) > 10) {
if position > twofingersPrev { if position > twofingersPrev {
HIDPostAuxKey(NX_KEYTYPE_SOUND_UP) HIDPostAuxKey(NX_KEYTYPE_SOUND_UP)
} else if position < twofingersPrev { } else if position < twofingersPrev {
@@ -58,7 +58,7 @@ class ScrollViewItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
case .began: case .began:
threefingersPrev = position threefingersPrev = position
case .changed: case .changed:
if (((position-threefingersPrev) > 15) || ((threefingersPrev-position) > 15)) { if ((position - threefingersPrev) > 15) || ((threefingersPrev - position) > 15) {
if position > threefingersPrev { if position > threefingersPrev {
GenericKeyPress(keyCode: CGKeyCode(144)).send() GenericKeyPress(keyCode: CGKeyCode(144)).send()
} else if position < threefingersPrev { } else if position < threefingersPrev {
+22 -25
View File
@@ -6,17 +6,17 @@
// Copyright © 2018 Anton Palgunov. All rights reserved. // Copyright © 2018 Anton Palgunov. All rights reserved.
// //
import Foundation
import AppKit import AppKit
import Foundation
extension String { extension String {
func trim() -> String { func trim() -> String {
return self.trimmingCharacters(in: NSCharacterSet.whitespaces) return trimmingCharacters(in: NSCharacterSet.whitespaces)
} }
func stripComments() -> String { func stripComments() -> String {
// ((\s|,)\/\*[\s\S]*?\*\/)|(( |, ")\/\/.*) // ((\s|,)\/\*[\s\S]*?\*\/)|(( |, ")\/\/.*)
return self.replacingOccurrences(of: "((\\s|,)\\/\\*[\\s\\S]*?\\*\\/)|(( |, \\\")\\/\\/.*)", with: "", options: .regularExpression) return replacingOccurrences(of: "((\\s|,)\\/\\*[\\s\\S]*?\\*\\/)|(( |, \\\")\\/\\/.*)", with: "", options: .regularExpression)
} }
var hexColor: NSColor? { var hexColor: NSColor? {
@@ -39,21 +39,20 @@ extension String {
} }
extension NSImage { extension NSImage {
func resize(maxSize:NSSize) -> NSImage { func resize(maxSize: NSSize) -> NSImage {
var ratio:Float = 0.0 var ratio: Float = 0.0
let imageWidth = Float(self.size.width) let imageWidth = Float(size.width)
let imageHeight = Float(self.size.height) let imageHeight = Float(size.height)
let maxWidth = Float(maxSize.width) let maxWidth = Float(maxSize.width)
let maxHeight = Float(maxSize.height) let maxHeight = Float(maxSize.height)
// Get ratio (landscape or portrait) // Get ratio (landscape or portrait)
if (imageWidth > imageHeight) { if imageWidth > imageHeight {
// Landscape // Landscape
ratio = maxWidth / imageWidth; ratio = maxWidth / imageWidth
} } else {
else {
// Portrait // Portrait
ratio = maxHeight / imageHeight; ratio = maxHeight / imageHeight
} }
// Calculate new size based on the ratio // Calculate new size based on the ratio
@@ -61,11 +60,11 @@ extension NSImage {
let newHeight = imageHeight * ratio let newHeight = imageHeight * ratio
// Create a new NSSize object with the newly calculated size // Create a new NSSize object with the newly calculated size
let newSize:NSSize = NSSize(width: Int(newWidth), height: Int(newHeight)) let newSize: NSSize = NSSize(width: Int(newWidth), height: Int(newHeight))
// Cast the NSImage to a CGImage // Cast the NSImage to a CGImage
var imageRect:NSRect = NSMakeRect(0, 0, self.size.width, self.size.height) var imageRect: NSRect = NSMakeRect(0, 0, size.width, size.height)
let imageRef = self.cgImage(forProposedRect: &imageRect, context: nil, hints: nil) let imageRef = cgImage(forProposedRect: &imageRect, context: nil, hints: nil)
// Create NSImage from the CGImage using the new size // Create NSImage from the CGImage using the new size
let imageWithNewSize = NSImage(cgImage: imageRef!, size: newSize) let imageWithNewSize = NSImage(cgImage: imageRef!, size: newSize)
@@ -74,34 +73,32 @@ extension NSImage {
return imageWithNewSize return imageWithNewSize
} }
func rotateByDegreess(degrees:CGFloat) -> NSImage { func rotateByDegreess(degrees: CGFloat) -> NSImage {
var imageBounds = NSZeroRect; imageBounds.size = size
var imageBounds = NSZeroRect ; imageBounds.size = self.size
let pathBounds = NSBezierPath(rect: imageBounds) let pathBounds = NSBezierPath(rect: imageBounds)
var transform = NSAffineTransform() var transform = NSAffineTransform()
transform.rotate(byDegrees: degrees) transform.rotate(byDegrees: degrees)
pathBounds.transform(using: transform as AffineTransform) pathBounds.transform(using: transform as AffineTransform)
let rotatedBounds:NSRect = NSMakeRect(NSZeroPoint.x, NSZeroPoint.y , self.size.width, self.size.height ) let rotatedBounds: NSRect = NSMakeRect(NSZeroPoint.x, NSZeroPoint.y, size.width, size.height)
let rotatedImage = NSImage(size: rotatedBounds.size) let rotatedImage = NSImage(size: rotatedBounds.size)
//Center the image within the rotated bounds // Center the image within the rotated bounds
imageBounds.origin.x = NSMidX(rotatedBounds) - (NSWidth(imageBounds) / 2) imageBounds.origin.x = NSMidX(rotatedBounds) - (NSWidth(imageBounds) / 2)
imageBounds.origin.y = NSMidY(rotatedBounds) - (NSHeight(imageBounds) / 2) imageBounds.origin.y = NSMidY(rotatedBounds) - (NSHeight(imageBounds) / 2)
// Start a new transform // Start a new transform
transform = NSAffineTransform() transform = NSAffineTransform()
// Move coordinate system to the center (since we want to rotate around the center) // Move coordinate system to the center (since we want to rotate around the center)
transform.translateX(by: +(NSWidth(rotatedBounds) / 2 ), yBy: +(NSHeight(rotatedBounds) / 2)) transform.translateX(by: +(NSWidth(rotatedBounds) / 2), yBy: +(NSHeight(rotatedBounds) / 2))
transform.rotate(byDegrees: degrees) transform.rotate(byDegrees: degrees)
// Move the coordinate system bak to normal // Move the coordinate system bak to normal
transform.translateX(by: -(NSWidth(rotatedBounds) / 2 ), yBy: -(NSHeight(rotatedBounds) / 2)) transform.translateX(by: -(NSWidth(rotatedBounds) / 2), yBy: -(NSHeight(rotatedBounds) / 2))
// Draw the original image, rotated, into the new image // Draw the original image, rotated, into the new image
rotatedImage.lockFocus() rotatedImage.lockFocus()
transform.concat() transform.concat()
self.draw(in: imageBounds, from: NSZeroRect, operation: NSCompositingOperation.copy, fraction: 1.0) draw(in: imageBounds, from: NSZeroRect, operation: NSCompositingOperation.copy, fraction: 1.0)
rotatedImage.unlockFocus() rotatedImage.unlockFocus()
return rotatedImage return rotatedImage
} }
} }
+3 -3
View File
@@ -6,7 +6,7 @@
// Copyright © 2018 Anton Palgunov. All rights reserved. // Copyright © 2018 Anton Palgunov. All rights reserved.
// //
func presentSystemModal(_ touchBar: NSTouchBar!, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) -> Void { func presentSystemModal(_ touchBar: NSTouchBar!, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) {
if #available(OSX 10.14, *) { if #available(OSX 10.14, *) {
NSTouchBar.presentSystemModalTouchBar(touchBar, systemTrayItemIdentifier: identifier) NSTouchBar.presentSystemModalTouchBar(touchBar, systemTrayItemIdentifier: identifier)
} else { } else {
@@ -14,7 +14,7 @@ func presentSystemModal(_ touchBar: NSTouchBar!, systemTrayItemIdentifier identi
} }
} }
func presentSystemModal(_ touchBar: NSTouchBar!, placement: Int64, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) -> Void { func presentSystemModal(_ touchBar: NSTouchBar!, placement: Int64, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) {
if #available(OSX 10.14, *) { if #available(OSX 10.14, *) {
NSTouchBar.presentSystemModalTouchBar(touchBar, placement: placement, systemTrayItemIdentifier: identifier) NSTouchBar.presentSystemModalTouchBar(touchBar, placement: placement, systemTrayItemIdentifier: identifier)
} else { } else {
@@ -22,7 +22,7 @@ func presentSystemModal(_ touchBar: NSTouchBar!, placement: Int64, systemTrayIte
} }
} }
func minimizeSystemModal(_ touchBar: NSTouchBar!) -> Void { func minimizeSystemModal(_ touchBar: NSTouchBar!) {
if #available(OSX 10.14, *) { if #available(OSX 10.14, *) {
NSTouchBar.minimizeSystemModalTouchBar(touchBar) NSTouchBar.minimizeSystemModalTouchBar(touchBar)
} else { } else {
+76 -74
View File
@@ -17,14 +17,13 @@ let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSuppor
let standardConfigPath = appSupportDirectory.appending("/items.json") let standardConfigPath = appSupportDirectory.appending("/items.json")
extension ItemType { extension ItemType {
var identifierBase: String { var identifierBase: String {
switch self { switch self {
case .staticButton(title: _): case .staticButton(title: _):
return "com.toxblh.mtmr.staticButton." return "com.toxblh.mtmr.staticButton."
case .appleScriptTitledButton(source: _): case .appleScriptTitledButton(source: _):
return "com.toxblh.mtmr.appleScriptButton." return "com.toxblh.mtmr.appleScriptButton."
case .timeButton(formatTemplate: _): case .timeButton(formatTemplate: _, timeZone: _):
return "com.toxblh.mtmr.timeButton." return "com.toxblh.mtmr.timeButton."
case .battery(): case .battery():
return "com.toxblh.mtmr.battery." return "com.toxblh.mtmr.battery."
@@ -36,7 +35,7 @@ extension ItemType {
return "com.toxblh.mtmr.brightness" return "com.toxblh.mtmr.brightness"
case .weather(interval: _, units: _, api_key: _, icon_type: _): case .weather(interval: _, units: _, api_key: _, icon_type: _):
return "com.toxblh.mtmr.weather" return "com.toxblh.mtmr.weather"
case .currency(interval: _, from: _, to: _): case .currency(interval: _, from: _, to: _, full: _):
return "com.toxblh.mtmr.currency" return "com.toxblh.mtmr.currency"
case .inputsource(): case .inputsource():
return "com.toxblh.mtmr.inputsource." return "com.toxblh.mtmr.inputsource."
@@ -48,9 +47,14 @@ extension ItemType {
return "com.toxblh.mtmr.nightShift." return "com.toxblh.mtmr.nightShift."
case .dnd(items: _): case .dnd(items: _):
return "com.toxblh.mtmr.dnd." return "com.toxblh.mtmr.dnd."
case .pomodoro(interval: _):
return PomodoroBarItem.identifier
case .network(flip: _):
return NetworkBarItem.identifier
case .darkMode(items: _):
return DarkModeBarItem.identifier
} }
} }
} }
extension NSTouchBarItem.Identifier { extension NSTouchBarItem.Identifier {
@@ -58,7 +62,6 @@ extension NSTouchBarItem.Identifier {
} }
class TouchBarController: NSObject, NSTouchBarDelegate { class TouchBarController: NSObject, NSTouchBarDelegate {
static let shared = TouchBarController() static let shared = TouchBarController()
var touchBar: NSTouchBar! var touchBar: NSTouchBar!
@@ -85,25 +88,23 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
var blacklistAppIdentifiers: [String] = [] var blacklistAppIdentifiers: [String] = []
var frontmostApplicationIdentifier: String? { var frontmostApplicationIdentifier: String? {
get { guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil }
guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil } return frontmostId
return frontmostId
}
} }
private override init() { private override init() {
super.init() super.init()
SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in self?.dismissTouchBar()}), longAction: .none) SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in self?.dismissTouchBar() }), longAction: .none)
SupportedTypesHolder.sharedInstance.register(typename: "close") { _ in SupportedTypesHolder.sharedInstance.register(typename: "close") { _ in
return (item: .staticButton(title: ""), action: .custom(closure: { [weak self] in (item: .staticButton(title: ""), action: .custom(closure: { [weak self] in
guard let `self` = self else { return } guard let `self` = self else { return }
self.reloadPreset(path: self.lastPresetPath) self.reloadPreset(path: self.lastPresetPath)
}), longAction: .none, parameters: [.width: .width(30), .image: .image(source: (NSImage(named: NSImage.stopProgressFreestandingTemplateName))!)]) }), longAction: .none, parameters: [.width: .width(30), .image: .image(source: (NSImage(named: NSImage.stopProgressFreestandingTemplateName))!)])
} }
if let blackListed = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.blackListedApps") { if let blackListed = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.blackListedApps") {
self.blacklistAppIdentifiers = blackListed blacklistAppIdentifiers = blackListed
} }
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didLaunchApplicationNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didLaunchApplicationNotification, object: nil)
@@ -117,44 +118,40 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
if let oldBar = self.touchBar { if let oldBar = self.touchBar {
minimizeSystemModal(oldBar) minimizeSystemModal(oldBar)
} }
self.touchBar = NSTouchBar() touchBar = NSTouchBar()
self.jsonItems = newJsonItems jsonItems = newJsonItems
self.itemDefinitions = [:] itemDefinitions = [:]
self.items = [:] items = [:]
self.leftIdentifiers = [] leftIdentifiers = []
self.centerItems = [] centerItems = []
self.rightIdentifiers = [] rightIdentifiers = []
loadItemDefinitions(jsonItems: self.jsonItems) loadItemDefinitions(jsonItems: jsonItems)
createItems() createItems()
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
return items[identifier] items[identifier]
}) })
self.centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString)) centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
self.scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems) scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
touchBar.delegate = self touchBar.delegate = self
touchBar.defaultItemIdentifiers = [] touchBar.defaultItemIdentifiers = []
touchBar.defaultItemIdentifiers = self.leftIdentifiers + [centerScrollArea] + self.rightIdentifiers touchBar.defaultItemIdentifiers = leftIdentifiers + [centerScrollArea] + rightIdentifiers
self.updateActiveApp() updateActiveApp()
} }
@objc func activeApplicationChanged(_ n: Notification) { @objc func activeApplicationChanged(_: Notification) {
updateActiveApp() updateActiveApp()
} }
func updateActiveApp() { func updateActiveApp() {
if self.blacklistAppIdentifiers.index(of: self.frontmostApplicationIdentifier!) != nil { if frontmostApplicationIdentifier != nil && blacklistAppIdentifiers.index(of: frontmostApplicationIdentifier!) != nil {
dismissTouchBar() dismissTouchBar()
} else { } else {
if self.showControlStripState { presentTouchBar()
updateControlStripPresence()
} else {
presentSystemModal(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
}
} }
} }
@@ -196,8 +193,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
} }
func createItems() { func createItems() {
for (identifier, definition) in self.itemDefinitions { for (identifier, definition) in itemDefinitions {
self.items[identifier] = self.createItem(forIdentifier: identifier, definition: definition) items[identifier] = createItem(forIdentifier: identifier, definition: definition)
} }
} }
@@ -214,7 +211,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
} }
@objc private func presentTouchBar() { @objc private func presentTouchBar() {
if self.showControlStripState { if showControlStripState {
updateControlStripPresence()
presentSystemModal(touchBar, systemTrayItemIdentifier: .controlStripItem) presentSystemModal(touchBar, systemTrayItemIdentifier: .controlStripItem)
} else { } else {
presentSystemModal(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem) presentSystemModal(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
@@ -231,9 +229,9 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
presentTouchBar() presentTouchBar()
} }
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? { func touchBar(_: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
if identifier == centerScrollArea { if identifier == centerScrollArea {
return self.scrollArea return scrollArea
} }
guard let item = self.items[identifier], guard let item = self.items[identifier],
@@ -245,45 +243,50 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
} }
func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? { func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? {
var barItem: NSTouchBarItem! var barItem: NSTouchBarItem!
switch item.type { switch item.type {
case .staticButton(title: let title): case let .staticButton(title: title):
barItem = CustomButtonTouchBarItem(identifier: identifier, title: title) barItem = CustomButtonTouchBarItem(identifier: identifier, title: title)
case .appleScriptTitledButton(source: let source, refreshInterval: let interval): case let .appleScriptTitledButton(source: source, refreshInterval: interval):
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval) barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval)
case .timeButton(formatTemplate: let template): case let .timeButton(formatTemplate: template, timeZone: timeZone):
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template) barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, timeZone: timeZone)
case .battery(): case .battery():
barItem = BatteryBarItem(identifier: identifier) barItem = BatteryBarItem(identifier: identifier)
case .dock: case .dock:
barItem = AppScrubberTouchBarItem(identifier: identifier) barItem = AppScrubberTouchBarItem(identifier: identifier)
case .volume: case .volume:
if case .image(let source)? = item.additionalParameters[.image] { if case let .image(source)? = item.additionalParameters[.image] {
barItem = VolumeViewController(identifier: identifier, image: source.image) barItem = VolumeViewController(identifier: identifier, image: source.image)
} else { } else {
barItem = VolumeViewController(identifier: identifier) barItem = VolumeViewController(identifier: identifier)
} }
case .brightness(refreshInterval: let interval): case let .brightness(refreshInterval: interval):
if case .image(let source)? = item.additionalParameters[.image] { if case let .image(source)? = item.additionalParameters[.image] {
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval, image: source.image) barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval, image: source.image)
} else { } else {
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval) barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
} }
case .weather(interval: let interval, units: let units, api_key: let api_key, icon_type: let icon_type): case let .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type):
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type) barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type)
case .currency(interval: let interval, from: let from, to: let to): case let .currency(interval: interval, from: from, to: to, full: full):
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to) barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, full: full)
case .inputsource(): case .inputsource():
barItem = InputSourceBarItem(identifier: identifier) barItem = InputSourceBarItem(identifier: identifier)
case .music(interval: let interval): case let .music(interval: interval):
barItem = MusicBarItem(identifier: identifier, interval: interval) barItem = MusicBarItem(identifier: identifier, interval: interval)
case .groupBar(items: let items): case let .groupBar(items: items):
barItem = GroupBarItem(identifier: identifier, items: items) barItem = GroupBarItem(identifier: identifier, items: items)
case .nightShift(): case .nightShift():
barItem = NightShiftBarItem(identifier: identifier) barItem = NightShiftBarItem(identifier: identifier)
case .dnd(): case .dnd():
barItem = DnDBarItem(identifier: identifier) barItem = DnDBarItem(identifier: identifier)
case let .pomodoro(workTime: workTime, restTime: restTime):
barItem = PomodoroBarItem(identifier: identifier, workTime: workTime, restTime: restTime)
case let .network(flip: flip):
barItem = NetworkBarItem(identifier: identifier, flip: flip)
case .darkMode():
barItem = DarkModeBarItem(identifier: identifier)
} }
if let action = self.action(forItem: item), let item = barItem as? CustomButtonTouchBarItem { if let action = self.action(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
@@ -292,19 +295,19 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
if let longAction = self.longAction(forItem: item), let item = barItem as? CustomButtonTouchBarItem { if let longAction = self.longAction(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
item.longTapClosure = longAction item.longTapClosure = longAction
} }
if case .bordered(let bordered)? = item.additionalParameters[.bordered], let item = barItem as? CustomButtonTouchBarItem { if case let .bordered(bordered)? = item.additionalParameters[.bordered], let item = barItem as? CustomButtonTouchBarItem {
item.isBordered = bordered item.isBordered = bordered
} }
if case .background(let color)? = item.additionalParameters[.background], let item = barItem as? CustomButtonTouchBarItem { if case let .background(color)? = item.additionalParameters[.background], let item = barItem as? CustomButtonTouchBarItem {
item.backgroundColor = color item.backgroundColor = color
} }
if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth { if case let .width(value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
widthBarItem.setWidth(value: value) widthBarItem.setWidth(value: value)
} }
if case .image(let source)? = item.additionalParameters[.image], let item = barItem as? CustomButtonTouchBarItem { if case let .image(source)? = item.additionalParameters[.image], let item = barItem as? CustomButtonTouchBarItem {
item.image = source.image item.image = source.image
} }
if case .title(let value)? = item.additionalParameters[.title] { if case let .title(value)? = item.additionalParameters[.title] {
if let item = barItem as? GroupBarItem { if let item = barItem as? GroupBarItem {
item.collapsedRepresentationLabel = value item.collapsedRepresentationLabel = value
} else if let item = barItem as? CustomButtonTouchBarItem { } else if let item = barItem as? CustomButtonTouchBarItem {
@@ -314,13 +317,13 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
return barItem return barItem
} }
func action(forItem item: BarItemDefinition) -> (()->())? { func action(forItem item: BarItemDefinition) -> (() -> Void)? {
switch item.action { switch item.action {
case .hidKey(keycode: let keycode): case let .hidKey(keycode: keycode):
return { HIDPostAuxKey(keycode) } return { HIDPostAuxKey(keycode) }
case .keyPress(keycode: let keycode): case let .keyPress(keycode: keycode):
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() } return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
case .appleScript(source: let source): case let .appleScript(source: source):
guard let appleScript = source.appleScript else { guard let appleScript = source.appleScript else {
print("cannot create apple script for item \(item)") print("cannot create apple script for item \(item)")
return {} return {}
@@ -334,38 +337,37 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
} }
} }
} }
case .shellScript(executable: let executable, parameters: let parameters): case let .shellScript(executable: executable, parameters: parameters):
return { return {
let task = Process() let task = Process()
task.launchPath = executable task.launchPath = executable
task.arguments = parameters task.arguments = parameters
task.launch() task.launch()
} }
case .openUrl(url: let url): case let .openUrl(url: url):
return { return {
if let url = URL(string: url), NSWorkspace.shared.open(url) { if let url = URL(string: url), NSWorkspace.shared.open(url) {
#if DEBUG #if DEBUG
print("URL was successfully opened") print("URL was successfully opened")
#endif #endif
} else { } else {
print("error", url) print("error", url)
} }
} }
case .custom(closure: let closure): case let .custom(closure: closure):
return closure return closure
case .none: case .none:
return nil return nil
} }
} }
func longAction(forItem item: BarItemDefinition) -> (() -> Void)? {
func longAction(forItem item: BarItemDefinition) -> (()->())? {
switch item.longAction { switch item.longAction {
case .hidKey(keycode: let keycode): case let .hidKey(keycode: keycode):
return { HIDPostAuxKey(keycode) } return { HIDPostAuxKey(keycode) }
case .keyPress(keycode: let keycode): case let .keyPress(keycode: keycode):
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() } return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
case .appleScript(source: let source): case let .appleScript(source: source):
guard let appleScript = source.appleScript else { guard let appleScript = source.appleScript else {
print("cannot create apple script for item \(item)") print("cannot create apple script for item \(item)")
return {} return {}
@@ -377,24 +379,24 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
print("error \(error) when handling \(item) ") print("error \(error) when handling \(item) ")
} }
} }
case .shellScript(executable: let executable, parameters: let parameters): case let .shellScript(executable: executable, parameters: parameters):
return { return {
let task = Process() let task = Process()
task.launchPath = executable task.launchPath = executable
task.arguments = parameters task.arguments = parameters
task.launch() task.launch()
} }
case .openUrl(url: let url): case let .openUrl(url: url):
return { return {
if let url = URL(string: url), NSWorkspace.shared.open(url) { if let url = URL(string: url), NSWorkspace.shared.open(url) {
#if DEBUG #if DEBUG
print("URL was successfully opened") print("URL was successfully opened")
#endif #endif
} else { } else {
print("error", url) print("error", url)
} }
} }
case .custom(closure: let closure): case let .custom(closure: closure):
return closure return closure
case .none: case .none:
return nil return nil
@@ -408,13 +410,13 @@ protocol CanSetWidth {
extension NSCustomTouchBarItem: CanSetWidth { extension NSCustomTouchBarItem: CanSetWidth {
func setWidth(value: CGFloat) { func setWidth(value: CGFloat) {
self.view.widthAnchor.constraint(equalToConstant: value).isActive = true view.widthAnchor.constraint(equalToConstant: value).isActive = true
} }
} }
extension BarItemDefinition { extension BarItemDefinition {
var align: Align { var align: Align {
if case .align(let result)? = self.additionalParameters[.align] { if case let .align(result)? = additionalParameters[.align] {
return result return result
} }
return .center return .center
+32 -36
View File
@@ -8,7 +8,6 @@
import Cocoa import Cocoa
class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrubberDataSource { class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrubberDataSource {
private var scrubber: NSScrubber! private var scrubber: NSScrubber!
private let hf: HapticFeedback = HapticFeedback() private let hf: HapticFeedback = HapticFeedback()
@@ -23,10 +22,8 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
private var runningAppsIdentifiers: [String] = [] private var runningAppsIdentifiers: [String] = []
private var frontmostApplicationIdentifier: String? { private var frontmostApplicationIdentifier: String? {
get { guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil }
guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil } return frontmostId
return frontmostId
}
} }
private var applications: [DockItem] = [] private var applications: [DockItem] = []
@@ -34,11 +31,11 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
override init(identifier: NSTouchBarItem.Identifier) { override init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier) super.init(identifier: identifier)
scrubber = NSScrubber(); scrubber = NSScrubber()
scrubber.delegate = self scrubber.delegate = self
scrubber.dataSource = self scrubber.dataSource = self
scrubber.mode = .free // .fixed scrubber.mode = .free // .fixed
let layout = NSScrubberFlowLayout(); let layout = NSScrubberFlowLayout()
layout.itemSize = NSSize(width: 36, height: 32) layout.itemSize = NSSize(width: 36, height: 32)
layout.itemSpacing = 2 layout.itemSpacing = 2
scrubber.scrubberLayout = layout scrubber.scrubberLayout = layout
@@ -54,21 +51,21 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didActivateApplicationNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didActivateApplicationNotification, object: nil)
if let persistent = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.dock.persistent") { if let persistent = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.dock.persistent") {
self.persistentAppIdentifiers = persistent persistentAppIdentifiers = persistent
} }
updateRunningApplication() updateRunningApplication()
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@objc func activeApplicationChanged(n: Notification) { @objc func activeApplicationChanged(n _: Notification) {
updateRunningApplication() updateRunningApplication()
} }
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { override func observeValue(forKeyPath _: String?, of _: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
updateRunningApplication() updateRunningApplication()
} }
@@ -86,7 +83,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
scrubber.selectedIndex = index ?? 0 scrubber.selectedIndex = index ?? 0
} }
public func numberOfItems(for scrubber: NSScrubber) -> Int { public func numberOfItems(for _: NSScrubber) -> Int {
return applications.count return applications.count
} }
@@ -105,7 +102,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
let dotView = NSView(frame: .zero) let dotView = NSView(frame: .zero)
dotView.wantsLayer = true dotView.wantsLayer = true
if self.runningAppsIdentifiers.contains(app.bundleIdentifier!) { if runningAppsIdentifiers.contains(app.bundleIdentifier!) {
dotView.layer?.backgroundColor = NSColor.white.cgColor dotView.layer?.backgroundColor = NSColor.white.cgColor
} else { } else {
dotView.layer?.backgroundColor = NSColor.black.cgColor dotView.layer?.backgroundColor = NSColor.black.cgColor
@@ -118,44 +115,44 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
return item return item
} }
public func didBeginInteracting(with scrubber: NSScrubber) { public func didBeginInteracting(with _: NSScrubber) {
stopTimer() stopTimer()
self.ticks = 0 ticks = 0
self.timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(checkTimer), userInfo: nil, repeats: true) timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(checkTimer), userInfo: nil, repeats: true)
} }
@objc private func checkTimer() { @objc private func checkTimer() {
self.ticks += 1 ticks += 1
if (self.ticks == minTicks) { if ticks == minTicks {
hf.tap(strong: 2) hf.tap(strong: 2)
} }
if (self.ticks > maxTicks) { if ticks > maxTicks {
stopTimer() stopTimer()
hf.tap(strong: 6) hf.tap(strong: 6)
} }
} }
private func stopTimer() { private func stopTimer() {
self.timer?.invalidate() timer?.invalidate()
self.timer = nil timer = nil
self.lastSelected = 0 lastSelected = 0
} }
public func didCancelInteracting(with scrubber: NSScrubber) { public func didCancelInteracting(with _: NSScrubber) {
stopTimer() stopTimer()
} }
public func didFinishInteracting(with scrubber: NSScrubber) { public func didFinishInteracting(with scrubber: NSScrubber) {
stopTimer() stopTimer()
if (ticks == 0) { if ticks == 0 {
return return
} }
if (self.ticks >= minTicks && scrubber.selectedIndex > 0) { if ticks >= minTicks && scrubber.selectedIndex > 0 {
self.longPress(selected: scrubber.selectedIndex) longPress(selected: scrubber.selectedIndex)
return return
} }
@@ -176,7 +173,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
private func longPress(selected: Int) { private func longPress(selected: Int) {
let bundleIdentifier = applications[selected].bundleIdentifier let bundleIdentifier = applications[selected].bundleIdentifier
if (self.ticks > maxTicks) { if ticks > maxTicks {
if let processIdentifier = applications[selected].pid { if let processIdentifier = applications[selected].pid {
if !(NSRunningApplication(processIdentifier: processIdentifier)?.terminate())! { if !(NSRunningApplication(processIdentifier: processIdentifier)?.terminate())! {
NSRunningApplication(processIdentifier: processIdentifier)?.forceTerminate() NSRunningApplication(processIdentifier: processIdentifier)?.forceTerminate()
@@ -185,26 +182,26 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
} else { } else {
hf.tap(strong: 6) hf.tap(strong: 6)
if let index = self.persistentAppIdentifiers.index(of: bundleIdentifier!) { if let index = self.persistentAppIdentifiers.index(of: bundleIdentifier!) {
self.persistentAppIdentifiers.remove(at: index) persistentAppIdentifiers.remove(at: index)
} else { } else {
self.persistentAppIdentifiers.append(bundleIdentifier!) persistentAppIdentifiers.append(bundleIdentifier!)
} }
UserDefaults.standard.set(self.persistentAppIdentifiers, forKey: "com.toxblh.mtmr.dock.persistent") UserDefaults.standard.set(persistentAppIdentifiers, forKey: "com.toxblh.mtmr.dock.persistent")
UserDefaults.standard.synchronize() UserDefaults.standard.synchronize()
} }
self.ticks = 0 ticks = 0
updateRunningApplication() updateRunningApplication()
} }
private func launchedApplications() -> [DockItem] { private func launchedApplications() -> [DockItem] {
self.runningAppsIdentifiers = [] runningAppsIdentifiers = []
var returnable: [DockItem] = [] var returnable: [DockItem] = []
for app in NSWorkspace.shared.runningApplications { for app in NSWorkspace.shared.runningApplications {
guard app.activationPolicy == NSApplication.ActivationPolicy.regular else { continue } guard app.activationPolicy == NSApplication.ActivationPolicy.regular else { continue }
guard let bundleIdentifier = app.bundleIdentifier else { continue } guard let bundleIdentifier = app.bundleIdentifier else { continue }
self.runningAppsIdentifiers.append(bundleIdentifier) runningAppsIdentifiers.append(bundleIdentifier)
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier), pid: app.processIdentifier) let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier), pid: app.processIdentifier)
returnable.append(dockItem) returnable.append(dockItem)
@@ -212,7 +209,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
return returnable return returnable
} }
public func getIcon(forBundleIdentifier bundleIdentifier: String? = nil, orPath path: String? = nil, orType type: String? = nil) -> NSImage { public func getIcon(forBundleIdentifier bundleIdentifier: String? = nil, orPath path: String? = nil, orType _: String? = nil) -> NSImage {
if bundleIdentifier != nil { if bundleIdentifier != nil {
if let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleIdentifier!) { if let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleIdentifier!) {
return NSWorkspace.shared.icon(forFile: appPath) return NSWorkspace.shared.icon(forFile: appPath)
@@ -227,12 +224,11 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
return genericIcon ?? NSImage(size: .zero) return genericIcon ?? NSImage(size: .zero)
} }
public func getDockPersistentAppsList() -> [DockItem] { public func getDockPersistentAppsList() -> [DockItem] {
var returnable: [DockItem] = [] var returnable: [DockItem] = []
for bundleIdentifier in persistentAppIdentifiers { for bundleIdentifier in persistentAppIdentifiers {
if !self.runningAppsIdentifiers.contains(bundleIdentifier) { if !runningAppsIdentifiers.contains(bundleIdentifier) {
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier)) let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier))
returnable.append(dockItem) returnable.append(dockItem)
} }
+23 -30
View File
@@ -6,8 +6,8 @@
// Copyright © 2018 Anton Palgunov. All rights reserved. // Copyright © 2018 Anton Palgunov. All rights reserved.
// //
import IOKit.ps
import Foundation import Foundation
import IOKit.ps
class BatteryBarItem: CustomButtonTouchBarItem { class BatteryBarItem: CustomButtonTouchBarItem {
private let batteryInfo = BatteryInfo() private let batteryInfo = BatteryInfo()
@@ -18,15 +18,15 @@ class BatteryBarItem: CustomButtonTouchBarItem {
batteryInfo.start { [weak self] in batteryInfo.start { [weak self] in
self?.refresh() self?.refresh()
} }
self.refresh() refresh()
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func refresh() { func refresh() {
self.attributedTitle = self.batteryInfo.formattedInfo() attributedTitle = batteryInfo.formattedInfo()
} }
deinit { deinit {
@@ -42,14 +42,14 @@ class BatteryInfo: NSObject {
var isCharging: Bool = false var isCharging: Bool = false
var ACPower: String = "" var ACPower: String = ""
var timeRemaining: String = "" var timeRemaining: String = ""
var notifyBlock: ()->() = {} var notifyBlock: () -> Void = {}
var loop:CFRunLoopSource? var loop: CFRunLoopSource?
func start(notifyBlock: @escaping ()->()) { func start(notifyBlock: @escaping () -> Void) {
self.notifyBlock = notifyBlock self.notifyBlock = notifyBlock
let opaque = Unmanaged.passRetained(self).toOpaque() let opaque = Unmanaged.passRetained(self).toOpaque()
let context = UnsafeMutableRawPointer(opaque) let context = UnsafeMutableRawPointer(opaque)
loop = IOPSNotificationCreateRunLoopSource({ (context) in loop = IOPSNotificationCreateRunLoopSource({ context in
guard let ctx = context else { guard let ctx = context else {
return return
} }
@@ -61,7 +61,7 @@ class BatteryInfo: NSObject {
} }
func stop() { func stop() {
self.notifyBlock = {} notifyBlock = {}
guard let loop = self.loop else { guard let loop = self.loop else {
return return
} }
@@ -75,41 +75,35 @@ class BatteryInfo: NSObject {
for ps in psList { for ps in psList {
if let psDesc = IOPSGetPowerSourceDescription(psInfo, ps).takeUnretainedValue() as? [String: Any] { if let psDesc = IOPSGetPowerSourceDescription(psInfo, ps).takeUnretainedValue() as? [String: Any] {
let current = psDesc[kIOPSCurrentCapacityKey] if let current = psDesc[kIOPSCurrentCapacityKey] as? Int {
if (current != nil) { self.current = current
self.current = current as! Int
} }
let timeToEmpty = psDesc[kIOPSTimeToEmptyKey] if let timeToEmpty = psDesc[kIOPSTimeToEmptyKey] as? Int {
if (timeToEmpty != nil) { self.timeToEmpty = timeToEmpty
self.timeToEmpty = timeToEmpty as! Int
} }
let timeToFull = psDesc[kIOPSTimeToFullChargeKey] if let timeToFull = psDesc[kIOPSTimeToFullChargeKey] as? Int {
if (timeToFull != nil) { self.timeToFull = timeToFull
self.timeToFull = timeToFull as! Int
} }
let isCharged = psDesc[kIOPSIsChargedKey] if let isCharged = psDesc[kIOPSIsChargedKey] as? Bool {
if (isCharged != nil) { self.isCharged = isCharged
self.isCharged = isCharged as! Bool
} }
let isCharging = psDesc[kIOPSIsChargingKey] if let isCharging = psDesc[kIOPSIsChargingKey] as? Bool {
if (isCharging != nil) { self.isCharging = isCharging
self.isCharging = isCharging as! Bool
} }
let ACPower = psDesc[kIOPSPowerSourceStateKey] if let ACPower = psDesc[kIOPSPowerSourceStateKey] as? String {
if (ACPower != nil) { self.ACPower = ACPower
self.ACPower = ACPower as! String
} }
} }
} }
} }
func getFormattedTime(time: Int) -> String { func getFormattedTime(time: Int) -> String {
if (time > 0) { if time > 0 {
let timeFormatted = NSString(format: " %d:%02d", time / 60, time % 60) as String let timeFormatted = NSString(format: " %d:%02d", time / 60, time % 60) as String
return timeFormatted return timeFormatted
} }
@@ -119,7 +113,7 @@ class BatteryInfo: NSObject {
public func formattedInfo() -> NSAttributedString { public func formattedInfo() -> NSAttributedString {
var title = "" var title = ""
self.getPSInfo() getPSInfo()
if ACPower == "AC Power" { if ACPower == "AC Power" {
if current < 100 { if current < 100 {
@@ -143,5 +137,4 @@ class BatteryInfo: NSObject {
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count)) newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
return newTitle return newTitle
} }
} }
+8 -9
View File
@@ -1,6 +1,6 @@
import Cocoa
import AppKit import AppKit
import AVFoundation import AVFoundation
import Cocoa
import CoreAudio import CoreAudio
class BrightnessViewController: NSCustomTouchBarItem { class BrightnessViewController: NSCustomTouchBarItem {
@@ -9,24 +9,24 @@ class BrightnessViewController: NSCustomTouchBarItem {
init(identifier: NSTouchBarItem.Identifier, refreshInterval: Double, image: NSImage? = nil) { init(identifier: NSTouchBarItem.Identifier, refreshInterval: Double, image: NSImage? = nil) {
super.init(identifier: identifier) super.init(identifier: identifier)
if (image == nil) { if image == nil {
sliderItem = CustomSlider() sliderItem = CustomSlider()
} else { } else {
sliderItem = CustomSlider(knob: image!) sliderItem = CustomSlider(knob: image!)
} }
sliderItem.target = self sliderItem.target = self
sliderItem.action = #selector(BrightnessViewController.sliderValueChanged(_:)) sliderItem.action = #selector(BrightnessViewController.sliderValueChanged(_:))
sliderItem.minValue = 0.0 sliderItem.minValue = 0.0
sliderItem.maxValue = 100.0 sliderItem.maxValue = 100.0
sliderItem.floatValue = getBrightness() * 100 sliderItem.floatValue = getBrightness() * 100
self.view = sliderItem view = sliderItem
let timer = Timer.scheduledTimer(timeInterval: refreshInterval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true) let timer = Timer.scheduledTimer(timeInterval: refreshInterval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true)
RunLoop.current.add(timer, forMode: RunLoop.Mode.common) RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@@ -42,13 +42,13 @@ class BrightnessViewController: NSCustomTouchBarItem {
@objc func sliderValueChanged(_ sender: Any) { @objc func sliderValueChanged(_ sender: Any) {
if let sliderItem = sender as? NSSlider { if let sliderItem = sender as? NSSlider {
setBrightness(level: Float32(sliderItem.intValue)/100.0) setBrightness(level: Float32(sliderItem.intValue) / 100.0)
} }
} }
private func getBrightness() -> Float32 { private func getBrightness() -> Float32 {
if #available(OSX 10.13, *) { if #available(OSX 10.13, *) {
return Float32(CoreDisplay_Display_GetUserBrightness(0)); return Float32(CoreDisplay_Display_GetUserBrightness(0))
} else { } else {
var level: Float32 = 0.5 var level: Float32 = 0.5
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect")) let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
@@ -60,13 +60,12 @@ class BrightnessViewController: NSCustomTouchBarItem {
private func setBrightness(level: Float) { private func setBrightness(level: Float) {
if #available(OSX 10.13, *) { if #available(OSX 10.13, *) {
CoreDisplay_Display_SetUserBrightness(0, Double(level)); CoreDisplay_Display_SetUserBrightness(0, Double(level))
} else { } else {
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect")) let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
IODisplaySetFloatParameter(service, 1, kIODisplayBrightnessKey as CFString, level) IODisplaySetFloatParameter(service, 1, kIODisplayBrightnessKey as CFString, level)
IOObjectRelease(service) IOObjectRelease(service)
} }
} }
} }
+26 -11
View File
@@ -12,9 +12,11 @@ import CoreLocation
class CurrencyBarItem: CustomButtonTouchBarItem { class CurrencyBarItem: CustomButtonTouchBarItem {
private let activity: NSBackgroundActivityScheduler private let activity: NSBackgroundActivityScheduler
private var prefix: String private var prefix: String
private var postfix: String
private var from: String private var from: String
private var to: String private var to: String
private var oldValue: Float32! private var oldValue: Float32!
private var full: Bool = false
private let currencies = [ private let currencies = [
"USD": "$", "USD": "$",
@@ -36,16 +38,23 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
"ETH": "Ξ", "ETH": "Ξ",
] ]
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String) { init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, full: Bool) {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck") activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval activity.interval = interval
self.from = from self.from = from
self.to = to self.to = to
self.full = full
if let prefix = currencies[from] { if let prefix = currencies[from] {
self.prefix = prefix self.prefix = prefix
} else { } else {
self.prefix = from prefix = from
}
if let postfix = currencies[to] {
self.postfix = postfix
} else {
postfix = to
} }
super.init(identifier: identifier, title: "⏳") super.init(identifier: identifier, title: "⏳")
@@ -59,21 +68,21 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
updateCurrency() updateCurrency()
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@objc func updateCurrency() { @objc func updateCurrency() {
let urlRequest = URLRequest(url: URL(string: "https://api.coinbase.com/v2/exchange-rates?currency=\(from)")!) let urlRequest = URLRequest(url: URL(string: "https://api.coinbase.com/v2/exchange-rates?currency=\(from)")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in let task = URLSession.shared.dataTask(with: urlRequest) { data, _, error in
if error == nil { if error == nil {
do { do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject] let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
var value: Float32! var value: Float32!
if let data_array = json["data"] as? [String : AnyObject] { if let data_array = json["data"] as? [String: AnyObject] {
if let rates = data_array["rates"] as? [String : AnyObject] { if let rates = data_array["rates"] as? [String: AnyObject] {
if let item = rates["\(self.to)"] as? String { if let item = rates["\(self.to)"] as? String {
value = Float32(item) value = Float32(item)
} }
@@ -103,13 +112,19 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
color = NSColor.red color = NSColor.red
} }
} }
self.oldValue = value
let title = String(format: "%@%.2f", self.prefix, value) oldValue = value
let regularFont = self.attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15) var title = ""
if full {
title = String(format: "%@‣%.2f%@", prefix, value, postfix)
} else {
title = String(format: "%@%.2f", prefix, value)
}
let regularFont = attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont]) let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont])
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count)) newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
self.attributedTitle = newTitle attributedTitle = newTitle
} }
} }
+57
View File
@@ -0,0 +1,57 @@
import Foundation
class DarkModeBarItem: CustomButtonTouchBarItem, Widget {
static var name: String = "darkmode"
static var identifier: String = "com.toxblh.mtmr.darkmode"
private var timer: Timer!
init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "")
isBordered = false
setWidth(value: 24)
tapClosure = { [weak self] in self?.DarkModeToggle() }
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
refresh()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func DarkModeToggle() {
DarkMode.isEnabled = !DarkMode.isEnabled
refresh()
}
@objc func refresh() {
image = DarkMode.isEnabled ? #imageLiteral(resourceName: "dark-mode-on") : #imageLiteral(resourceName: "dark-mode-off")
}
}
struct DarkMode {
private static let prefix = "tell application \"System Events\" to tell appearance preferences to"
static var isEnabled: Bool {
get {
return UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark"
}
set {
toggle(force: newValue)
}
}
static func toggle(force: Bool? = nil) {
let value = force.map(String.init) ?? "not dark mode"
runAppleScript("\(prefix) set dark mode to \(value)")
}
}
func runAppleScript(_ source: String) -> String? {
return NSAppleScript(source: source)?.executeAndReturnError(nil).stringValue
}
+8 -8
View File
@@ -8,22 +8,22 @@
import Foundation import Foundation
class DnDBarItem : CustomButtonTouchBarItem { class DnDBarItem: CustomButtonTouchBarItem {
private var timer: Timer! private var timer: Timer!
init(identifier: NSTouchBarItem.Identifier) { init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
self.isBordered = false isBordered = false
self.setWidth(value: 32) setWidth(value: 32)
self.tapClosure = { [weak self] in self?.DnDToggle() } tapClosure = { [weak self] in self?.DnDToggle() }
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true) timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
self.refresh() refresh()
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@@ -33,7 +33,7 @@ class DnDBarItem : CustomButtonTouchBarItem {
} }
@objc func refresh() { @objc func refresh() {
self.image = DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off") image = DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off")
} }
} }
@@ -67,7 +67,7 @@ public struct DoNotDisturb {
commitChanges() commitChanges()
} }
static var isEnabled:Bool { static var isEnabled: Bool {
get { get {
return CFPreferencesGetAppBooleanValue("doNotDisturb" as CFString, appId, nil) return CFPreferencesGetAppBooleanValue("doNotDisturb" as CFString, appId, nil)
} }
+20 -23
View File
@@ -8,7 +8,6 @@
import Cocoa import Cocoa
class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate { class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
var jsonItems: [BarItemDefinition] var jsonItems: [BarItemDefinition]
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:] var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
@@ -23,37 +22,35 @@ class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
init(identifier: NSTouchBarItem.Identifier, items: [BarItemDefinition]) { init(identifier: NSTouchBarItem.Identifier, items: [BarItemDefinition]) {
jsonItems = items jsonItems = items
super.init(identifier: identifier) super.init(identifier: identifier)
self.popoverTouchBar.delegate = self popoverTouchBar.delegate = self
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
deinit { deinit {}
} @objc override func showPopover(_: Any?) {
itemDefinitions = [:]
items = [:]
leftIdentifiers = []
centerItems = []
rightIdentifiers = []
@objc override func showPopover(_ sender: Any?) { loadItemDefinitions(jsonItems: jsonItems)
self.itemDefinitions = [:] createItems()
self.items = [:]
self.leftIdentifiers = []
self.centerItems = []
self.rightIdentifiers = []
self.loadItemDefinitions(jsonItems: jsonItems)
self.createItems()
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
return items[identifier] items[identifier]
}) })
self.centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString)) centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
self.scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems) scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
TouchBarController.shared.touchBar.delegate = self TouchBarController.shared.touchBar.delegate = self
TouchBarController.shared.touchBar.defaultItemIdentifiers = [] TouchBarController.shared.touchBar.defaultItemIdentifiers = []
TouchBarController.shared.touchBar.defaultItemIdentifiers = self.leftIdentifiers + [centerScrollArea] + self.rightIdentifiers TouchBarController.shared.touchBar.defaultItemIdentifiers = leftIdentifiers + [centerScrollArea] + rightIdentifiers
if TouchBarController.shared.showControlStripState { if TouchBarController.shared.showControlStripState {
presentSystemModal(TouchBarController.shared.touchBar, systemTrayItemIdentifier: .controlStripItem) presentSystemModal(TouchBarController.shared.touchBar, systemTrayItemIdentifier: .controlStripItem)
@@ -62,15 +59,15 @@ class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
} }
} }
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? { func touchBar(_: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
if identifier == centerScrollArea { if identifier == centerScrollArea {
return self.scrollArea return scrollArea
} }
guard let item = self.items[identifier], guard let item = self.items[identifier],
let definition = self.itemDefinitions[identifier], let definition = self.itemDefinitions[identifier],
definition.align != .center else { definition.align != .center else {
return nil return nil
} }
return item return item
} }
@@ -96,8 +93,8 @@ class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
} }
func createItems() { func createItems() {
for (identifier, definition) in self.itemDefinitions { for (identifier, definition) in itemDefinitions {
self.items[identifier] = TouchBarController.shared.createItem(forIdentifier: identifier, definition: definition) items[identifier] = TouchBarController.shared.createItem(forIdentifier: identifier, definition: definition)
} }
} }
} }
+13 -15
View File
@@ -9,33 +9,32 @@
import Cocoa import Cocoa
class InputSourceBarItem: CustomButtonTouchBarItem { class InputSourceBarItem: CustomButtonTouchBarItem {
fileprivate var notificationCenter: CFNotificationCenter fileprivate var notificationCenter: CFNotificationCenter
let buttonSize = NSSize(width: 21, height: 21) let buttonSize = NSSize(width: 21, height: 21)
init(identifier: NSTouchBarItem.Identifier) { init(identifier: NSTouchBarItem.Identifier) {
notificationCenter = CFNotificationCenterGetDistributedCenter(); notificationCenter = CFNotificationCenterGetDistributedCenter()
super.init(identifier: identifier, title: "⏳") super.init(identifier: identifier, title: "⏳")
observeIputSourceChangedNotification(); observeIputSourceChangedNotification()
textInputSourceDidChange() textInputSourceDidChange()
self.tapClosure = { [weak self] in tapClosure = { [weak self] in
self?.switchInputSource() self?.switchInputSource()
} }
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
deinit { deinit {
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())); CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()))
} }
@objc public func textInputSourceDidChange() { @objc public func textInputSourceDidChange() {
let currentSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue() let currentSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
var iconImage: NSImage? = nil var iconImage: NSImage?
if let imageURL = currentSource.iconImageURL, if let imageURL = currentSource.iconImageURL,
let image = NSImage(contentsOf: imageURL) { let image = NSImage(contentsOf: imageURL) {
@@ -46,10 +45,10 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
if let iconImage = iconImage { if let iconImage = iconImage {
iconImage.size = buttonSize iconImage.size = buttonSize
self.image = iconImage image = iconImage
self.title = "" title = ""
} else { } else {
self.title = currentSource.name title = currentSource.name
} }
} }
@@ -65,15 +64,15 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
}) })
for item in inputSources { for item in inputSources {
if (item.id != currentSource.id) { if item.id != currentSource.id {
TISSelectInputSource(item) TISSelectInputSource(item)
break break
} }
} }
} }
@objc public func observeIputSourceChangedNotification(){ @objc public func observeIputSourceChangedNotification() {
let callback: CFNotificationCallback = { center, observer, name, object, info in let callback: CFNotificationCallback = { _, observer, _, _, _ in
let mySelf = Unmanaged<InputSourceBarItem>.fromOpaque(observer!).takeUnretainedValue() let mySelf = Unmanaged<InputSourceBarItem>.fromOpaque(observer!).takeUnretainedValue()
mySelf.textInputSourceDidChange() mySelf.textInputSourceDidChange()
} }
@@ -96,7 +95,7 @@ extension TISInputSource {
private func getProperty(_ key: CFString) -> AnyObject? { private func getProperty(_ key: CFString) -> AnyObject? {
let cfType = TISGetInputSourceProperty(self, key) let cfType = TISGetInputSourceProperty(self, key)
if (cfType != nil) { if cfType != nil {
return Unmanaged<AnyObject>.fromOpaque(cfType!).takeUnretainedValue() return Unmanaged<AnyObject>.fromOpaque(cfType!).takeUnretainedValue()
} else { } else {
return nil return nil
@@ -131,4 +130,3 @@ extension TISInputSource {
return OpaquePointer(TISGetInputSourceProperty(self, kTISPropertyIconRef)) as IconRef? return OpaquePointer(TISGetInputSourceProperty(self, kTISPropertyIconRef)) as IconRef?
} }
} }
+59 -54
View File
@@ -20,51 +20,51 @@ class MusicBarItem: CustomButtonTouchBarItem {
"com.spotify.client", "com.spotify.client",
"com.coppertino.Vox", "com.coppertino.Vox",
"com.google.Chrome", "com.google.Chrome",
"com.apple.Safari" "com.apple.Safari",
] ]
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) { init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
self.interval = interval self.interval = interval
super.init(identifier: identifier, title: "⏳") super.init(identifier: identifier, title: "⏳")
self.isBordered = false isBordered = false
self.tapClosure = { [weak self] in self?.playPause() } tapClosure = { [weak self] in self?.playPause() }
self.longTapClosure = { [weak self] in self?.nextTrack() } longTapClosure = { [weak self] in self?.nextTrack() }
self.refreshAndSchedule() refreshAndSchedule()
} }
@objc func marquee(){ @objc func marquee() {
let str = self.title let str = title
if (str.count > 10) { if str.count > 10 {
let indexFirst = str.index(str.startIndex, offsetBy: 0) let indexFirst = str.index(str.startIndex, offsetBy: 0)
let indexSecond = str.index(str.startIndex, offsetBy: 1) let indexSecond = str.index(str.startIndex, offsetBy: 1)
self.title = String(str.suffix(from: indexSecond)) + String(str[indexFirst]) title = String(str.suffix(from: indexSecond)) + String(str[indexFirst])
} }
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@objc func playPause() { @objc func playPause() {
for ident in playerBundleIdentifiers { for ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident) { if let musicPlayer = SBApplication(bundleIdentifier: ident) {
if (musicPlayer.isRunning) { if musicPlayer.isRunning {
if (musicPlayer.className == "SpotifyApplication") { if musicPlayer.className == "SpotifyApplication" {
let mp = (musicPlayer as SpotifyApplication) let mp = (musicPlayer as SpotifyApplication)
mp.playpause!() mp.playpause!()
return return
} else if (musicPlayer.className == "ITunesApplication") { } else if musicPlayer.className == "ITunesApplication" {
let mp = (musicPlayer as iTunesApplication) let mp = (musicPlayer as iTunesApplication)
mp.playpause!() mp.playpause!()
return return
} else if (musicPlayer.className == "VOXApplication") { } else if musicPlayer.className == "VOXApplication" {
let mp = (musicPlayer as VoxApplication) let mp = (musicPlayer as VoxApplication)
mp.playpause!() mp.playpause!()
return return
} else if (musicPlayer.className == "SafariApplication") { } else if musicPlayer.className == "SafariApplication" {
// You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'. // You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'.
let safariApplication = musicPlayer as SafariApplication let safariApplication = musicPlayer as SafariApplication
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow }) let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
@@ -74,7 +74,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
if (tab.URL?.starts(with: "https://music.yandex.ru"))! { if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_play')[0].click()", in: tab) _ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_play')[0].click()", in: tab)
return return
} else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) { } else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('audio_page_player_play')[0].click()", in: tab) _ = safariApplication.doJavaScript!("document.getElementsByClassName('audio_page_player_play')[0].click()", in: tab)
return return
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! { } else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
@@ -112,23 +112,23 @@ class MusicBarItem: CustomButtonTouchBarItem {
@objc func nextTrack() { @objc func nextTrack() {
for ident in playerBundleIdentifiers { for ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident) { if let musicPlayer = SBApplication(bundleIdentifier: ident) {
if (musicPlayer.isRunning) { if musicPlayer.isRunning {
if (musicPlayer.className == "SpotifyApplication") { if musicPlayer.className == "SpotifyApplication" {
let mp = (musicPlayer as SpotifyApplication) let mp = (musicPlayer as SpotifyApplication)
mp.nextTrack!() mp.nextTrack!()
updatePlayer() updatePlayer()
return return
} else if (musicPlayer.className == "ITunesApplication") { } else if musicPlayer.className == "ITunesApplication" {
let mp = (musicPlayer as iTunesApplication) let mp = (musicPlayer as iTunesApplication)
mp.nextTrack!() mp.nextTrack!()
updatePlayer() updatePlayer()
return return
} else if (musicPlayer.className == "VOXApplication") { } else if musicPlayer.className == "VOXApplication" {
let mp = (musicPlayer as VoxApplication) let mp = (musicPlayer as VoxApplication)
mp.next!() mp.next!()
updatePlayer() updatePlayer()
return return
} else if (musicPlayer.className == "SafariApplication") { } else if musicPlayer.className == "SafariApplication" {
// You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'. // You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'.
let safariApplication = musicPlayer as SafariApplication let safariApplication = musicPlayer as SafariApplication
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow }) let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
@@ -139,7 +139,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_next')[0].click()", in: tab) _ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_next')[0].click()", in: tab)
updatePlayer() updatePlayer()
return return
} else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) { } else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('audio_page_player_next')[0].click()", in: tab) _ = safariApplication.doJavaScript!("document.getElementsByClassName('audio_page_player_next')[0].click()", in: tab)
updatePlayer() updatePlayer()
return return
@@ -171,15 +171,15 @@ class MusicBarItem: CustomButtonTouchBarItem {
for var ident in playerBundleIdentifiers { for var ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident) { if let musicPlayer = SBApplication(bundleIdentifier: ident) {
if (musicPlayer.isRunning) { if musicPlayer.isRunning {
var tempTitle = "" var tempTitle = ""
if (musicPlayer.className == "SpotifyApplication") { if musicPlayer.className == "SpotifyApplication" {
tempTitle = (musicPlayer as SpotifyApplication).title tempTitle = (musicPlayer as SpotifyApplication).title
} else if (musicPlayer.className == "ITunesApplication") { } else if musicPlayer.className == "ITunesApplication" {
tempTitle = (musicPlayer as iTunesApplication).title tempTitle = (musicPlayer as iTunesApplication).title
} else if (musicPlayer.className == "VOXApplication") { } else if musicPlayer.className == "VOXApplication" {
tempTitle = (musicPlayer as VoxApplication).title tempTitle = (musicPlayer as VoxApplication).title
} else if (musicPlayer.className == "SafariApplication") { } else if musicPlayer.className == "SafariApplication" {
let safariApplication = musicPlayer as SafariApplication let safariApplication = musicPlayer as SafariApplication
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow }) let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
for window in safariWindows! { for window in safariWindows! {
@@ -190,7 +190,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
tempTitle = (tab.name)! tempTitle = (tab.name)!
break break
// } // }
} else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) { } else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
tempTitle = (tab.name)! tempTitle = (tab.name)!
break break
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! { } else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
@@ -202,18 +202,18 @@ class MusicBarItem: CustomButtonTouchBarItem {
if tempTitle == "" { if tempTitle == "" {
ident = "" ident = ""
} }
} else if (musicPlayer.className == "GoogleChromeApplication") { } else if musicPlayer.className == "GoogleChromeApplication" {
let chromeApplication = musicPlayer as GoogleChromeApplication let chromeApplication = musicPlayer as GoogleChromeApplication
let chromeWindows = chromeApplication.windows?().compactMap({ $0 as? GoogleChromeWindow }) let chromeWindows = chromeApplication.windows?().compactMap({ $0 as? GoogleChromeWindow })
for window in chromeWindows! { for window in chromeWindows! {
for tab in window.tabs!() { for tab in window.tabs!() {
let tab = tab as! GoogleChromeTab let tab = tab as! GoogleChromeTab
if (tab.URL?.starts(with: "https://music.yandex.ru"))! { if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
if (!(tab.title?.hasSuffix("на Яндекс.Музыке"))!) { if !(tab.title?.hasSuffix("на Яндекс.Музыке"))! {
tempTitle = tab.title! tempTitle = tab.title!
break break
} }
} else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) { } else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
tempTitle = tab.title! tempTitle = tab.title!
break break
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! { } else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
@@ -227,7 +227,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
} }
} }
if (tempTitle == self.songTitle) { if tempTitle == self.songTitle {
return return
} else { } else {
self.songTitle = tempTitle self.songTitle = tempTitle
@@ -265,18 +265,20 @@ class MusicBarItem: CustomButtonTouchBarItem {
} }
@objc protocol SpotifyApplication { @objc protocol SpotifyApplication {
@objc optional var currentTrack: SpotifyTrack {get} @objc optional var currentTrack: SpotifyTrack { get }
@objc optional func nextTrack() @objc optional func nextTrack()
@objc optional func previousTrack() @objc optional func previousTrack()
@objc optional func playpause() @objc optional func playpause()
} }
extension SBApplication: SpotifyApplication{}
extension SBApplication: SpotifyApplication {}
@objc protocol SpotifyTrack { @objc protocol SpotifyTrack {
@objc optional var artist: String {get} @objc optional var artist: String { get }
@objc optional var name: String {get} @objc optional var name: String { get }
} }
extension SBObject: SpotifyTrack{}
extension SBObject: SpotifyTrack {}
extension SpotifyApplication { extension SpotifyApplication {
var title: String { var title: String {
@@ -285,20 +287,21 @@ extension SpotifyApplication {
} }
} }
@objc protocol iTunesApplication { @objc protocol iTunesApplication {
@objc optional var currentTrack: iTunesTrack {get} @objc optional var currentTrack: iTunesTrack { get }
@objc optional func playpause() @objc optional func playpause()
@objc optional func nextTrack() @objc optional func nextTrack()
@objc optional func previousTrack() @objc optional func previousTrack()
} }
extension SBApplication: iTunesApplication{}
extension SBApplication: iTunesApplication {}
@objc protocol iTunesTrack { @objc protocol iTunesTrack {
@objc optional var artist: String {get} @objc optional var artist: String { get }
@objc optional var name: String {get} @objc optional var name: String { get }
} }
extension SBObject: iTunesTrack{}
extension SBObject: iTunesTrack {}
extension iTunesApplication { extension iTunesApplication {
var title: String { var title: String {
@@ -307,16 +310,15 @@ extension iTunesApplication {
} }
} }
@objc protocol VoxApplication { @objc protocol VoxApplication {
@objc optional func playpause() @objc optional func playpause()
@objc optional func next() @objc optional func next()
@objc optional func previous() @objc optional func previous()
@objc optional var track: String {get} @objc optional var track: String { get }
@objc optional var artist: String {get} @objc optional var artist: String { get }
} }
extension SBApplication: VoxApplication{}
extension SBApplication: VoxApplication {}
extension VoxApplication { extension VoxApplication {
var title: String { var title: String {
@@ -324,7 +326,6 @@ extension VoxApplication {
} }
} }
@objc public protocol SBObjectProtocol: NSObjectProtocol { @objc public protocol SBObjectProtocol: NSObjectProtocol {
func get() -> Any! func get() -> Any!
} }
@@ -338,6 +339,7 @@ extension VoxApplication {
@objc optional func windows() -> SBElementArray @objc optional func windows() -> SBElementArray
@objc optional func doJavaScript(_ x: String!, in in_: Any!) -> Any // Applies a string of JavaScript code to a document. @objc optional func doJavaScript(_ x: String!, in in_: Any!) -> Any // Applies a string of JavaScript code to a document.
} }
extension SBApplication: SafariApplication {} extension SBApplication: SafariApplication {}
@objc public protocol SafariWindow: SBObjectProtocol { @objc public protocol SafariWindow: SBObjectProtocol {
@@ -346,36 +348,39 @@ extension SBApplication: SafariApplication {}
// @objc optional var document: SafariDocument { get } // The document whose contents are displayed in the window. // @objc optional var document: SafariDocument { get } // The document whose contents are displayed in the window.
// @objc optional func setCurrentTab(_ currentTab: SafariTab!) // The current tab. // @objc optional func setCurrentTab(_ currentTab: SafariTab!) // The current tab.
} }
extension SBObject: SafariWindow {} extension SBObject: SafariWindow {}
//@objc public protocol SafariDocument: SBObjectProtocol { // @objc public protocol SafariDocument: SBObjectProtocol {
// @objc optional var name: String { get } // Its name. // @objc optional var name: String { get } // Its name.
// @objc optional var URL: String { get } // The current URL of the document. // @objc optional var URL: String { get } // The current URL of the document.
//} // }
//extension SBObject: SafariDocument {} // extension SBObject: SafariDocument {}
@objc public protocol SafariTab: SBObjectProtocol { @objc public protocol SafariTab: SBObjectProtocol {
@objc optional var URL: String { get } // The current URL of the tab. @objc optional var URL: String { get } // The current URL of the tab.
@objc optional var name: String { get } // The name of the tab. @objc optional var name: String { get } // The name of the tab.
} }
extension SBObject: SafariTab {} extension SBObject: SafariTab {}
@objc public protocol GoogleChromeApplication: SBApplicationProtocol { @objc public protocol GoogleChromeApplication: SBApplicationProtocol {
@objc optional func windows() -> SBElementArray @objc optional func windows() -> SBElementArray
@objc optional func executeJavaScript(javascript: String!) -> Any // Applies a string of JavaScript code to a document. //, id: Any! @objc optional func executeJavaScript(javascript: String!) -> Any // Applies a string of JavaScript code to a document. //, id: Any!
} }
extension SBApplication: GoogleChromeApplication {} extension SBApplication: GoogleChromeApplication {}
@objc public protocol GoogleChromeWindow: SBObjectProtocol { @objc public protocol GoogleChromeWindow: SBObjectProtocol {
@objc optional var name: String { get } // The title of the window. @objc optional var name: String { get } // The title of the window.
@objc optional func tabs() -> SBElementArray @objc optional func tabs() -> SBElementArray
} }
extension SBObject: GoogleChromeWindow {} extension SBObject: GoogleChromeWindow {}
@objc public protocol GoogleChromeTab: SBObjectProtocol { @objc public protocol GoogleChromeTab: SBObjectProtocol {
@objc optional var URL: String { get } // The current URL of the tab. @objc optional var URL: String { get } // The current URL of the tab.
@objc optional var title: String { get } // The name of the tab. @objc optional var title: String { get } // The name of the tab.
} }
extension SBObject: GoogleChromeTab {} extension SBObject: GoogleChromeTab {}
+147
View File
@@ -0,0 +1,147 @@
//
// NetworkBarItem.swift
// MTMR
//
// Created by Anton Palgunov on 23/02/2019.
// Copyright © 2019 Anton Palgunov. All rights reserved.
//
import Foundation
class NetworkBarItem: CustomButtonTouchBarItem, Widget {
static var name: String = "network"
static var identifier: String = "com.toxblh.mtmr.network"
private let flip: Bool
init(identifier: NSTouchBarItem.Identifier, flip: Bool = false) {
self.flip = flip
super.init(identifier: identifier, title: " ")
startMonitoringProcess()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func startMonitoringProcess() {
var pipe: Pipe
var outputHandle: FileHandle
var bandwidthProcess: Process?
var dSpeed: UInt64?
var uSpeed: UInt64?
var curr: Array<Substring>?
var dataAvailable: NSObjectProtocol!
pipe = Pipe()
bandwidthProcess = Process()
bandwidthProcess?.launchPath = "/usr/bin/env"
bandwidthProcess?.arguments = ["netstat", "-w1", "-l", "en0"]
bandwidthProcess?.standardOutput = pipe
outputHandle = pipe.fileHandleForReading
outputHandle.waitForDataInBackgroundAndNotify(forModes: [RunLoop.Mode.common])
dataAvailable = NotificationCenter.default.addObserver(
forName: NSNotification.Name.NSFileHandleDataAvailable,
object: outputHandle,
queue: nil
) { _ -> Void in
let data = pipe.fileHandleForReading.availableData
if data.count > 0 {
if let str = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
curr = [""]
curr = str
.replacingOccurrences(of: " ", with: " ")
.split(separator: " ")
if curr == nil || (curr?.count)! < 6 {} else {
if Int64(curr![2]) == nil {} else {
dSpeed = UInt64(curr![2])
uSpeed = UInt64(curr![5])
self.setTitle(up: self.getHumanizeSize(speed: uSpeed!), down: self.getHumanizeSize(speed: dSpeed!))
}
}
}
outputHandle.waitForDataInBackgroundAndNotify()
} else {
NotificationCenter.default.removeObserver(dataAvailable)
}
}
var dataReady: NSObjectProtocol!
dataReady = NotificationCenter.default.addObserver(
forName: Process.didTerminateNotification,
object: outputHandle,
queue: nil
) { _ -> Void in
print("Task terminated!")
NotificationCenter.default.removeObserver(dataReady)
}
bandwidthProcess?.launch()
}
func getHumanizeSize(speed: UInt64) -> String {
let humanText: String
if speed < 1024 {
humanText = String(format: "%.0f", Double(speed)) + " B/s"
} else if speed < (1024 * 1024) {
humanText = String(format: "%.1f", Double(speed) / 1024) + " KB/s"
} else if speed < (1024 * 1024 * 1024) {
humanText = String(format: "%.1f", Double(speed) / (1024 * 1024)) + " MB/s"
} else {
humanText = String(format: "%.2f", Double(speed) / (1024 * 1024 * 1024)) + " GB/s"
}
return humanText
}
func appendUpSpeed(appendString: NSMutableAttributedString, up: String, titleFont: NSFont, newStr: Bool = false) {
appendString.append(NSMutableAttributedString(
string: newStr ? "\n↑" : "↑",
attributes: [
NSAttributedString.Key.foregroundColor: NSColor.blue,
NSAttributedString.Key.font: titleFont,
]))
appendString.append(NSMutableAttributedString(
string: up,
attributes: [
NSAttributedString.Key.font: titleFont,
]))
}
func appendDownSpeed(appendString: NSMutableAttributedString, down: String, titleFont: NSFont, newStr: Bool = false) {
appendString.append(NSMutableAttributedString(
string: newStr ? "\n↓" : "↓",
attributes: [
NSAttributedString.Key.foregroundColor: NSColor.red,
NSAttributedString.Key.font: titleFont,
]))
appendString.append(NSMutableAttributedString(
string: down,
attributes: [
NSAttributedString.Key.font: titleFont
]))
}
func setTitle(up: String, down: String) {
let titleFont = NSFont.monospacedDigitSystemFont(ofSize: 12, weight: NSFont.Weight.light)
let newTitle: NSMutableAttributedString = NSMutableAttributedString(string: "")
if (self.flip) {
appendUpSpeed(appendString: newTitle, up: up, titleFont: titleFont)
appendDownSpeed(appendString: newTitle, down: down, titleFont: titleFont, newStr: true)
} else {
appendDownSpeed(appendString: newTitle, down: down, titleFont: titleFont)
appendUpSpeed(appendString: newTitle, up: up, titleFont: titleFont, newStr: true)
}
self.attributedTitle = newTitle
}
}
+10 -10
View File
@@ -19,35 +19,35 @@ class NightShiftBarItem: CustomButtonTouchBarItem {
} }
private var isNightShiftEnabled: Bool { private var isNightShiftEnabled: Bool {
return self.blueLightStatus.enabled.boolValue return blueLightStatus.enabled.boolValue
} }
private func setNightShift(state: Bool) { private func setNightShift(state: Bool) {
self.nsclient.setEnabled(state) nsclient.setEnabled(state)
} }
init(identifier: NSTouchBarItem.Identifier) { init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
self.isBordered = false isBordered = false
self.setWidth(value: 28) setWidth(value: 28)
self.tapClosure = { [weak self] in self?.nightShiftAction() } tapClosure = { [weak self] in self?.nightShiftAction() }
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true) timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
self.refresh() refresh()
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func nightShiftAction() { func nightShiftAction() {
self.setNightShift(state: !self.isNightShiftEnabled) setNightShift(state: !isNightShiftEnabled)
self.refresh() refresh()
} }
@objc func refresh() { @objc func refresh() {
self.image = isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff") image = isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff")
} }
} }
+124
View File
@@ -0,0 +1,124 @@
//
// PomodoroBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 10.05.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
class PomodoroBarItem: CustomButtonTouchBarItem, Widget {
static let identifier = "com.toxblh.mtmr.pomodoro."
static let name = "pomodoro"
static let decoder: ParametersDecoder = { decoder in
enum CodingKeys: String, CodingKey {
case workTime
case restTime
}
let container = try decoder.container(keyedBy: CodingKeys.self)
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime)
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime)
return (
item: .pomodoro(workTime: workTime ?? 1500.00, restTime: restTime ?? 300),
action: .none,
longAction: .none,
parameters: [:]
)
}
private enum TimeTypes {
case work
case rest
case none
}
private let defaultTitle = "🍅"
private let workTime: TimeInterval
private let restTime: TimeInterval
private var typeTime: TimeTypes = .none
private var timer: DispatchSourceTimer?
private var timeLeft: Int = 0
private var timeLeftString: String {
return String(format: "%.2i:%.2i", timeLeft / 60, timeLeft % 60)
}
init(identifier: NSTouchBarItem.Identifier, workTime: TimeInterval, restTime: TimeInterval) {
self.workTime = workTime
self.restTime = restTime
super.init(identifier: identifier, title: defaultTitle)
tapClosure = { [weak self] in self?.startStopWork() }
longTapClosure = { [weak self] in self?.startStopRest() }
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
timer?.cancel()
timer = nil
}
@objc func startStopWork() {
typeTime = .work
startStopTimer()
}
@objc func startStopRest() {
typeTime = .rest
startStopTimer()
}
func startStopTimer() {
timer == nil ? start() : reset()
}
private func start() {
timeLeft = Int(typeTime == .work ? workTime : restTime)
let queue: DispatchQueue = DispatchQueue(label: "Timer")
timer = DispatchSource.makeTimerSource(flags: .strict, queue: queue)
timer?.schedule(deadline: .now(), repeating: .seconds(1), leeway: .never)
timer?.setEventHandler(handler: tick)
timer?.resume()
NSSound.beep()
}
private func finish() {
if typeTime != .none {
sendNotification()
}
reset()
}
private func reset() {
typeTime = .none
timer?.cancel()
timer = nil
title = defaultTitle
}
private func tick() {
timeLeft -= 1
DispatchQueue.main.async {
if self.timeLeft >= 0 {
self.title = self.defaultTitle + " " + self.timeLeftString
} else {
self.finish()
}
}
}
private func sendNotification() {
let notification: NSUserNotification = NSUserNotification()
notification.title = "Pomodoro"
notification.informativeText = typeTime == .work ? "it's time to rest your mind!" : "It's time to work!"
notification.soundName = "Submarine"
NSUserNotificationCenter.default.deliver(notification)
}
}
+8 -6
View File
@@ -4,20 +4,22 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
private let dateFormatter = DateFormatter() private let dateFormatter = DateFormatter()
private var timer: Timer! private var timer: Timer!
init(identifier: NSTouchBarItem.Identifier, formatTemplate: String) { init(identifier: NSTouchBarItem.Identifier, formatTemplate: String, timeZone: String? = nil) {
dateFormatter.setLocalizedDateFormatFromTemplate(formatTemplate) dateFormatter.dateFormat = formatTemplate
if let abbr = timeZone {
dateFormatter.timeZone = TimeZone(abbreviation: abbr)
}
super.init(identifier: identifier, title: " ") super.init(identifier: identifier, title: " ")
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true) timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
self.isBordered = false isBordered = false
updateTime() updateTime()
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@objc func updateTime() { @objc func updateTime() {
self.title = self.dateFormatter.string(from: Date()) title = dateFormatter.string(from: Date())
} }
} }
+13 -13
View File
@@ -1,6 +1,6 @@
import Cocoa
import AppKit import AppKit
import AVFoundation import AVFoundation
import Cocoa
import CoreAudio import CoreAudio
class VolumeViewController: NSCustomTouchBarItem { class VolumeViewController: NSCustomTouchBarItem {
@@ -12,31 +12,32 @@ class VolumeViewController: NSCustomTouchBarItem {
var forPropertyAddress = AudioObjectPropertyAddress( var forPropertyAddress = AudioObjectPropertyAddress(
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume, mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
mScope: kAudioDevicePropertyScopeOutput, mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster) mElement: kAudioObjectPropertyElementMaster
)
AudioObjectAddPropertyListenerBlock(defaultDeviceID, &forPropertyAddress, nil, audioObjectPropertyListenerBlock) AudioObjectAddPropertyListenerBlock(defaultDeviceID, &forPropertyAddress, nil, audioObjectPropertyListenerBlock)
if (image == nil) { if image == nil {
sliderItem = CustomSlider() sliderItem = CustomSlider()
} else { } else {
sliderItem = CustomSlider(knob: image!) sliderItem = CustomSlider(knob: image!)
} }
sliderItem.target = self sliderItem.target = self
sliderItem.action = #selector(VolumeViewController.sliderValueChanged(_:)) sliderItem.action = #selector(VolumeViewController.sliderValueChanged(_:))
sliderItem.minValue = 0.0 sliderItem.minValue = 0.0
sliderItem.maxValue = 100.0 sliderItem.maxValue = 100.0
sliderItem.floatValue = getInputGain()*100 sliderItem.floatValue = getInputGain() * 100
self.view = sliderItem view = sliderItem
} }
func audioObjectPropertyListenerBlock (numberAddresses: UInt32, addresses: UnsafePointer<AudioObjectPropertyAddress>) { func audioObjectPropertyListenerBlock(numberAddresses _: UInt32, addresses _: UnsafePointer<AudioObjectPropertyAddress>) {
DispatchQueue.main.async { DispatchQueue.main.async {
self.sliderItem.floatValue = self.getInputGain() * 100 self.sliderItem.floatValue = self.getInputGain() * 100
} }
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@@ -46,7 +47,7 @@ class VolumeViewController: NSCustomTouchBarItem {
@objc func sliderValueChanged(_ sender: Any) { @objc func sliderValueChanged(_ sender: Any) {
if let sliderItem = sender as? NSSlider { if let sliderItem = sender as? NSSlider {
_ = setInputGain(Float32(sliderItem.intValue)/100.0) _ = setInputGain(Float32(sliderItem.intValue) / 100.0)
} }
} }
@@ -76,9 +77,9 @@ class VolumeViewController: NSCustomTouchBarItem {
var inputVolume: Float32 = volume var inputVolume: Float32 = volume
if inputVolume == 0.0 { if inputVolume == 0.0 {
_ = setMute( mute: 1) _ = setMute(mute: 1)
} else { } else {
_ = setMute( mute: 0) _ = setMute(mute: 0)
} }
let size: UInt32 = UInt32(MemoryLayout.size(ofValue: inputVolume)) let size: UInt32 = UInt32(MemoryLayout.size(ofValue: inputVolume))
@@ -89,7 +90,7 @@ class VolumeViewController: NSCustomTouchBarItem {
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &inputVolume) return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &inputVolume)
} }
private func setMute( mute: Int) -> OSStatus { private func setMute(mute: Int) -> OSStatus {
var muteVal: Int = mute var muteVal: Int = mute
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress() var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
address.mSelector = AudioObjectPropertySelector(kAudioDevicePropertyMute) address.mSelector = AudioObjectPropertySelector(kAudioDevicePropertyMute)
@@ -99,4 +100,3 @@ class VolumeViewController: NSCustomTouchBarItem {
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &muteVal) return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &muteVal)
} }
} }
+16 -17
View File
@@ -16,11 +16,11 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
private var units_str = "°F" private var units_str = "°F"
private var prev_location: CLLocation! private var prev_location: CLLocation!
private var location: CLLocation! private var location: CLLocation!
private let iconsImages = ["01d": "☀️", "01n": "☀️", "02d": "⛅️", "02n": "⛅️", "03d": "☁️", "03n": "☁️", "04d": "☁️", "04n": "☁️", "09d": "⛅️", "09n": "⛅️", "10d": "🌦", "10n": "🌦", "11d": "🌩", "11n": "🌩", "13d": "❄️", "13n": "❄️", "50d": "🌫", "50n": "🌫"] private let iconsImages = ["01d": "☀️", "01n": "☀️", "02d": "⛅️", "02n": "⛅️", "03d": "☁️", "03n": "☁️", "04d": "☁️", "04n": "☁️", "09d": "⛅️", "09n": "⛅️", "10d": "🌦", "10n": "🌦", "11d": "🌩", "11n": "🌩", "13d": "❄️", "13n": "❄️", "50d": "🌫", "50n": "🌫"]
private let iconsText = ["01d": "☀", "01n": "☀", "02d": "☁", "02n": "☁", "03d": "☁", "03n": "☁", "04d": "☁", "04n": "☁", "09d": "☂", "09n": "☂", "10d": "☂", "10n": "☂", "11d": "☈", "11n": "☈", "13d": "☃", "13n": "☃", "50d": "♨", "50n": "♨"] private let iconsText = ["01d": "☀", "01n": "☀", "02d": "☁", "02n": "☁", "03d": "☁", "03n": "☁", "04d": "☁", "04n": "☁", "09d": "☂", "09n": "☂", "10d": "☂", "10n": "☂", "11d": "☈", "11n": "☈", "13d": "☃", "13n": "☃", "50d": "♨", "50n": "♨"]
private var iconsSource: Dictionary<String, String> private var iconsSource: Dictionary<String, String>
private var manager:CLLocationManager! private var manager: CLLocationManager!
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text") { init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text") {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck") activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
@@ -51,7 +51,7 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
} }
if !CLLocationManager.locationServicesEnabled() { if !CLLocationManager.locationServicesEnabled() {
print("Location services not enabled"); print("Location services not enabled")
return return
} }
@@ -69,24 +69,24 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
manager.startUpdatingLocation() manager.startUpdatingLocation()
} }
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@objc func updateWeather() { @objc func updateWeather() {
if self.location != nil { if location != nil {
let urlRequest = URLRequest(url: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)&units=\(self.units)&appid=\(self.api_key)")!) let urlRequest = URLRequest(url: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)&units=\(units)&appid=\(api_key)")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in let task = URLSession.shared.dataTask(with: urlRequest) { data, _, error in
if error == nil { if error == nil {
do { do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject] let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
// print(json) // print(json)
var temperature: Int! var temperature: Int!
var condition_icon = "" var condition_icon = ""
if let main = json["main"] as? [String : AnyObject] { if let main = json["main"] as? [String: AnyObject] {
if let temp = main["temp"] as? Double { if let temp = main["temp"] as? Double {
temperature = Int(temp) temperature = Int(temp)
} }
@@ -115,25 +115,24 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
} }
func setWeather(text: String) { func setWeather(text: String) {
self.title = text title = text
} }
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let lastLocation = locations.last! let lastLocation = locations.last!
self.location = lastLocation location = lastLocation
if prev_location == nil { if prev_location == nil {
updateWeather() updateWeather()
} }
prev_location = lastLocation prev_location = lastLocation
} }
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { func locationManager(_: CLLocationManager, didFailWithError error: Error) {
print(error); print(error)
} }
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { func locationManager(_: CLLocationManager, didChangeAuthorization _: CLAuthorizationStatus) {
// print("inside didChangeAuthorization "); // print("inside didChangeAuthorization ");
updateWeather() updateWeather()
} }
} }
+12
View File
@@ -0,0 +1,12 @@
//
// WidgetProtocol.swift
// MTMR
//
// Created by Anton Palgunov on 20/10/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
protocol Widget {
static var name: String { get }
static var identifier: String { get }
}
@@ -1,7 +1,6 @@
import XCTest import XCTest
class AppleScriptDefinitionTests: XCTestCase { class AppleScriptDefinitionTests: XCTestCase {
func testInline() { func testInline() {
let buttonNoActionFixture = """ let buttonNoActionFixture = """
[ { "type": "appleScriptTitledButton", "source": { "inline": "tell everything fine" } } ] [ { "type": "appleScriptTitledButton", "source": { "inline": "tell everything fine" } } ]
@@ -37,5 +36,4 @@ class AppleScriptDefinitionTests: XCTestCase {
return return
} }
} }
} }
+2 -4
View File
@@ -1,13 +1,12 @@
import XCTest import XCTest
class BackgroundColorTests: XCTestCase { class BackgroundColorTests: XCTestCase {
func testOpaque() { func testOpaque() {
let buttonNoActionFixture = """ let buttonNoActionFixture = """
[ { "type": "staticButton", "title": "Pew", "background": "#FF0000" } ] [ { "type": "staticButton", "title": "Pew", "background": "#FF0000" } ]
""".data(using: .utf8)! """.data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture) let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
guard case .background(let color)? = result?.first?.additionalParameters[.background] else { guard case let .background(color)? = result?.first?.additionalParameters[.background] else {
XCTFail() XCTFail()
return return
} }
@@ -19,11 +18,10 @@ class BackgroundColorTests: XCTestCase {
[ { "type": "staticButton", "title": "Pew", "background": "#FF000080" } ] [ { "type": "staticButton", "title": "Pew", "background": "#FF000080" } ]
""".data(using: .utf8)! """.data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture) let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
guard case .background(let color)? = result?.first?.additionalParameters[.background] else { guard case let .background(color)? = result?.first?.additionalParameters[.background] else {
XCTFail() XCTFail()
return return
} }
XCTAssertEqual(color.alphaComponent, 0.5, accuracy: 0.01) XCTAssertEqual(color.alphaComponent, 0.5, accuracy: 0.01)
} }
} }
-2
View File
@@ -1,7 +1,6 @@
import XCTest import XCTest
class ParseConfig: XCTestCase { class ParseConfig: XCTestCase {
func testButtonNoAction() { func testButtonNoAction() {
let buttonNoActionFixture = """ let buttonNoActionFixture = """
[ { "type": "staticButton", "title": "Pew" } ] [ { "type": "staticButton", "title": "Pew" } ]
@@ -65,5 +64,4 @@ class ParseConfig: XCTestCase {
return return
} }
} }
} }
+124 -61
View File
@@ -1,7 +1,4 @@
<p align="center"><img src="Resources/logo.png" width="120"></p>
<p align="center">
<img src="Resources/logo.png" width="120">
</p>
# My TouchBar. My rules # My TouchBar. My rules
@@ -15,59 +12,45 @@
</p> </p>
**MTMR** Community: **MTMR** Community:
[<img height="24px" src="https://github.com/discourse/DiscourseMobile/raw/master/icon.png" /> Discourse](https://forum.mtmr.app) [<img height="24px" src="https://camo.githubusercontent.com/88f53948f291c54736bf08f5fd7b037a848dfc62/68747470733a2f2f646973636f72646170702e636f6d2f6173736574732f30376463613830613130326434313439653937333664346231363263666636662e69636f"> Discord](https://discord.gg/CmNcDuQ)
[<img height="24px" src="https://telegram.org/img/t_logo.png" /> Telegram](https://t.me/joinchat/AmVYGg8vW38c13_3MxdE_g) [<img height="24px" src="https://telegram.org/img/t_logo.png" /> Telegram](https://t.me/joinchat/AmVYGg8vW38c13_3MxdE_g)
<a href="https://www.buymeacoffee.com/toxblh" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" height="32px" ></a> <a href="https://www.paypal.me/toxblh/10" title="Donate via Paypal"><img height="36px" src="Resources/support_paypal.svg" alt="PayPal donate button" /></a>
<a href="https://www.patreon.com/bePatron?u=9900748"><img height="32px" src="https://c5.patreon.com/external/logo/become_a_patron_button.png" srcset="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png 2x"></a> <a href="https://www.buymeacoffee.com/toxblh" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" height="36px" ></a>
[<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/854859-e299e139ab7f79855c0bc589f10b0ec6-medium_jpg.jpg?buster=1453074480" height="32px" /> Become a backer](https://opencollective.com/MTMR#backer) <a href="https://www.patreon.com/bePatron?u=9900748"><img height="36px" src="https://c5.patreon.com/external/logo/become_a_patron_button.png" srcset="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png 2x"></a>
[<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/854859-e299e139ab7f79855c0bc589f10b0ec6-medium_jpg.jpg?buster=1453074480" height="32px" /> Become a sponsor](https://opencollective.com/MTMR#sponsor) <a href="https://www.producthunt.com/posts/my-touchbar-my-rules-mtmr">
<img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=my-touchbar-my-rules-mtmr&theme=light" alt="My TouchBar My Rules (MTMR)" height="36px" style="max-width:100%">
</a>
My the idea is to create the program like a platform for plugins for customization TouchBar. I very like BTT and a full custom TouchBar (my [BTT preset](https://github.com/Toxblh/btt-touchbar-preset)). And I want to create it. And it's my the first Swift project for MacOS :) My idea is to create a platform for creating plugins to customize the TouchBar. I very much like BTT and having a full custom TouchBar (my [BTT preset](https://github.com/Toxblh/btt-touchbar-preset)), and I wanted to create it. It's my first Swift project for MacOS :)
### Roadmap
- [x] Create the first prototype with TouchBar in Storyboard
- [x] Put in stripe menu on startup the application
- [x] Find how to simulate real buttons like brightness, volume, night shift and etc.
- [x] Time in touchbar!
- [x] First the weather plugin
- [x] Find how to open full-screen TouchBar without the cross and stripe menu
- [x] Find how to add haptic feedback
- [x] Add icon and menu in StatusBar
- [x] Hide from Dock
- [x] Status menu: "preferences", "quit"
- [x] JSON or another approch for save preset, maybe in `~/Library/Application Support/MTMR/`
- [x] Custom buttons size, actions by click
- [x] Layout: [always left, NSSliderView for center, always right]
- [x] System for autoupdate (https://sparkle-project.org/)
- [ ] Overwrite default values from item types (e.g. title for brightness)
- [ ] Custom settings for paddings and margins for buttons
- [ ] XPC Service for scripts
- [ ] UI for settings
- [ ] Import config from BTT
Settings:
- [ ] Interface for plugins and export like presets
- [x] Startup at login
- [ ] Show on/off in Dock
- [ ] Show on/off in StatusBar
- [ ] On/off Haptic Feedback
Maybe:
- [ ] Refactoring the application on packages (AppleScript, JavaScript? and Swift?)
#### Share your presets [here](https://github.com/Toxblh/MTMR-presets)
## Installation ## Installation
- Download last [release](https://github.com/Toxblh/MTMR/releases) - Download lastest [release](https://github.com/Toxblh/MTMR/releases) from github
- Or via Homebrew `brew cask install mtmr` - Or via Homebrew `brew cask install mtmr`
## Preset > First open: you should allow access for MTMR in Accessibility for correct buttons work like <kbd>Esc</kbd> Volume/Brightness and another system keys
File for customize your preset for MTMR: `open ~/Library/Application\ Support/MTMR/items.json` ## Troubleshooting
If you can't open preferences:
- Opening another program which can't edit text
1. Open Terminal.app
2. Put `open -a TextEdit ~/Library/Application\ Support/MTMR/items.json` command and press <kbd>Enter</kbd>
Buttons or gestures doesn't work:
- "After the last update my mtmr is not working anymore!"
- "Buttons sometimes do not trigger action"
- "ESC don't work"
- "Gestures don't work"
Re-tick or check a tick for access 🍏→ System Preferences → Security and Privacy → tab Privacy → Accessibility → MTMR
<img width="450" alt="screenshot 2019-02-24 at 23 19 20" src="https://user-images.githubusercontent.com/2198153/53307057-2b078200-388c-11e9-8212-8c2b1aff0aa6.png">
## Built-in button types: ## Built-in button types:
> Buttons
- escape - escape
- exitTouchbar - exitTouchbar
- brightnessUp - brightnessUp
@@ -77,9 +60,6 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
- volumeDown - volumeDown
- volumeUp - volumeUp
- mute - mute
- dock (half-long click to open app, full-long click to kill app)
- nightShift
- dnd (Dont disturb)
> Native Plugins > Native Plugins
- battery - battery
@@ -87,6 +67,12 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
- weather - weather
- inputsource - inputsource
- music (tap for pause, longTap for next) - music (tap for pause, longTap for next)
- dock (half-long click to open app, full-long click to kill app)
- nightShift
- dnd (Don't disturb)
- darkMode
- pomodoro
- network
> Media Keys > Media Keys
- previous - previous
@@ -106,7 +92,7 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
- brightness - brightness
- volume - volume
### You can also make a custom buttons using these types ### You can also make custom buttons using these types
- `staticButton` - `staticButton`
```json ```json
"type": "staticButton", "type": "staticButton",
@@ -139,39 +125,78 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
"align": "center", "align": "center",
"bordered": true, "bordered": true,
"title": "stats", "title": "stats",
"items": [{ button }, {button}, ...] "items": [
{ "type": "play" }, { "type": "mute" }, ...]
} }
``` ```
To close a group, use the button:
```
{
"type": "close",
"width": 64
},
```
## Native plugins ## Native plugins
- `weather` #### `timeButton`
> Attention! Works not all: https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
```js
{
"type": "timeButton",
"formatTemplate": "dd HH:mm",
"timeZone": "UTC"
}
```
#### `weather`
> Provider: https://openweathermap.org Need allowance location service > Provider: https://openweathermap.org Need allowance location service
```js ```js
"type": "weather", "type": "weather",
"refreshInterval": 600, "refreshInterval": 600, // in seconds
"units": "metric", // or imperial "units": "metric", // or imperial
"icon_type": "text" // or images "icon_type": "text" // or images
"api_key": "" // you can get the key on openweather "api_key": "" // you can get the key on openweather
``` ```
- `currency` #### `currency`
> Provider: https://coinbase.com > Provider: https://coinbase.com
```js ```js
"type": "currency", "type": "currency",
"refreshInterval": 600, "refreshInterval": 600, // in seconds
"align": "right", "align": "right",
"from": "BTC", "from": "BTC",
"to": "USD", "to": "USD",
"full": true // £‣1.29$
``` ```
- `music` #### `music`
```js ```js
{ {
"type": "music", "type": "music",
"align": "center", "align": "center",
"width": 80, "width": 80,
"bordered": false, "bordered": false,
"refreshInterval": 2, "refreshInterval": 2, // in seconds
},
```
#### `pomodoro`
> Pomodoro plugin. One click to start the work timer, longclick to start the rest timer. Click in progress for reset.
```js
{
"type": "pomodoro",
"workTime": 1200, // set time work in seconds. Default 1500 (25 min)
"restTime": 600 // set time rest in seconds. Default 300 (5 min)
},
```
#### `network`
> Network plugin. The plugin to show usage a network
```js
{
"type": "network",
"flip": true
}, },
``` ```
@@ -213,7 +238,7 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
``` ```
## LongActions ## LongActions
This then you want to use longPress for some operations is will the same values like for Actions but different additional parameters, example: If you want to longPress for some operations, it is similar to the configuration for Actions but with additional parameters, for example:
```js ```js
"longAction": "hidKey", "longAction": "hidKey",
"longKeycode": 53, "longKeycode": 53,
@@ -228,14 +253,19 @@ This then you want to use longPress for some operations is will the same values
## Additional parameters: ## Additional parameters:
- `width` allow to restrict how much room a particular button will take - `width` restrict how much room a particular button will take
```json ```json
"width": 34 "width": 34
``` ```
- `align` can stick the item to the side. default is center - `align` can stick the item to the side. default is center
```js ```js
"align": "left" //or "right" or "center" "align": "left" // "left", "right" or "center"
```
- `bordered` you can do button without border
```js
"bordered": "false" // "true" or "false"
``` ```
## Example configuration: ## Example configuration:
@@ -296,17 +326,50 @@ This then you want to use longPress for some operations is will the same values
``` ```
### Author's presets ### Roadmap
- [x] Create the first prototype with TouchBar in Storyboard
- [x] Put in stripe menu on startup the application
- [x] Find how to simulate real buttons like brightness, volume, night shift and etc.
- [x] Time in touchbar!
- [x] First the weather plugin
- [x] Find how to open full-screen TouchBar without the cross and stripe menu
- [x] Find how to add haptic feedback
- [x] Add icon and menu in StatusBar
- [x] Hide from Dock
- [x] Status menu: "preferences", "quit"
- [x] JSON or another approch for save preset, maybe in `~/Library/Application Support/MTMR/`
- [x] Custom buttons size, actions by click
- [x] Layout: [always left, NSSliderView for center, always right]
- [x] System for autoupdate (https://sparkle-project.org/)
- [ ] Overwrite default values from item types (e.g. title for brightness)
- [ ] Custom settings for paddings and margins for buttons
- [ ] XPC Service for scripts
- [ ] UI for settings
- [ ] Import config from BTT
Settings:
- [ ] Interface for plugins and export like presets
- [x] Startup at login
- [ ] Show on/off in Dock
- [ ] Show on/off in StatusBar
- [ ] On/off Haptic Feedback
Maybe:
- [ ] Refactoring the application on packages (AppleScript, JavaScript? and Swift?)
### Author's presets
[@Toxblh preset](Resources/toxblh.json) [@Toxblh preset](Resources/toxblh.json)
[@ReDetection preset](Resources/ReDetection.json) [@ReDetection preset](Resources/ReDetection.json)
### User's presets ### User's presets
[@luongvo209 preset](Resources/luongvo209.json) [@luongvo209 preset](Resources/luongvo209.json)
![](Resources/luongvo209.png) ![](Resources/luongvo209.png)
[aadi_vs_anand preset](Resources/aadi_vs_anand.json)
![](Resources/aadi_vs_anand1.png)
![](Resources/aadi_vs_anand2.png)
## Credits ## Credits
Built by [@Toxblh](https://patreon.com/toxblh) and [@ReDetection](http://patreon.com/ReDetection). Built by [@Toxblh](https://patreon.com/toxblh) and [@ReDetection](http://patreon.com/ReDetection).
+129
View File
@@ -0,0 +1,129 @@
[
{ "type": "escape", "align": "left" },
// --- iTunes ---
{
"type": "appleScriptTitledButton",
"source": {
"inline":
"if application \"iTunes\" is running then\rtell application \"iTunes\"\rif player state is playing then\rreturn (get artist of current track) & \" – \" & (get name of current track)\relse\rreturn \"\"\rend if\rend tell\rend if\rreturn \"\"\r"
},
"action": "appleScript",
"actionAppleScript": {
"inline":
"if application \"iTunes\" is running then\rtell application \"iTunes\"\rif player state is playing then\rnext track\rend if\rend tell\rend if\r"
},
"refreshInterval": 1,
"image": {
"base64":
"iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADAUExURUdwTOVVZCzB+3qc0gkDBgEBAgcKEwAAAA4EBP5aVU2V95iJv7V3rtBOvH5W6jaOyclScKZGX3wuQCMuUqZN7+NQYXtDvFd9/sxYni2z6UhBhyhvnIp7sf38/PXz9ePm69/k6fHv8/j29+3q7/v6+ufq7uvu8fTw8+1egOFki/dbboVj/HNy/T+j/dNtnEul81vC8Vmf3OeRqOBVofK4xZfF7sDb7PLe6LxU1KKK79PL6vrW3fh4g5Zi4bi16daa0A3Qc90AAAAddFJOUwD3/v0uOlYNG/z+/v7998OYYztt/Le2/eDqi5jEo2rNTwAABMtJREFUWMPdl1tjqjoQhauC0Hqvta26JVxUQBQFRPBS/f//as8kISBqd/twXs5KjBDyrZkEFfP09D+WoigqFxz+mlbVXncweEZVBoNuD1x+E1vtMVZoURn01J/mob52KRNuj4k5mZjJcRueFotFpfv6EwdVRfy0tSdXSranheN0/zkRSB7x42Q6udExdBy3972Fog4o/kBg4X6bhPJaeX7eTqeT6b0MJtPpdOu6n49XQgX+dOQ8vpWEvYn/2AHiL052PpoROcsOtND17ztQ3rwTuCQz9O/moKiMf6BkG/puKBzurKQ64PmbU2bDzUxk3Uql4lZcl3Vpvt9VbxLoLZwjY7E1WcNZoB0XpbELie/3Sg6KVHG2jGPs1LTCE2UXFfgIgtBgyq8d/E/pehJq1zmZGc7kAPsMX4Ec932T25uX5vUklFcHJlDU1OT4wllkvOtn9lrSbF7dCUggNEtaMOXhQZq4WkpBcksJQBCOnyjvM4P8KqQgFW9BFJka2NMKB1gw+VMxvN9smnwI1EuzpxS+g9FWYySjsTpOtnq+H162iW01m/wyXLUPzT/5HKQoSjQmU8vE6TAElvWggbhuNRpScQa002bVtJmBCz9qNusWBkJmoyHmoHajC4yybVujhR26mJVha7lDo2FrhnA4N0aq+BpE24zjgsMoEsfU0AADaKCwemiIRZA+o6N9oygyMi/EAWk0DMNgFvCmN/5IwqCV3PCGzzIwbINrzgwykVq2iorUalm2UTZotXKWqVYz5uBjzDUoxrxWyzKQWy061LZsNIJ3PAMDIcbVauwdGmxrNblgYNnCgStN54ylBSsYoAxerwwgJsCWTS0sepym0Mdp1gYBw5lmwgDXIDEoaeHLYE36BafzuQWFQ9RASM/XQPpMD5YQ2gA/AwPArQJyZWDsgo64C+/pBRkDG4s31hdmwFNAGz1mBjPukBs8qdSgLDBA1LJm1lw/14IgWAdAQ5nhax4HY/FR7qfpHQMPUChzS0c6eFmv17MZo7HZBP3MQJHTNCnzxPMgOFUteAGtMwMmfRPIijBgc+AmNK6+9zw+9Iw05YsG8aaT/7Kro7eUcoSWw3n/1W57SxgGOZCYxl+VDDabcf6LpNTf3g6IQ4XY7TbiyyUBmhqsViswWK02cE7ITIdmtxnWlcJz6f1tPyMsha+2R4UGyJPdywotwGSHOKHdm+IMYA40BRhtkXOb42DAh8crppjw8CyB4nMBlvFtD/0WSfZebkDHguLNar2JdyTXptqRrx6OmMKZkISQc4Yv9yyDXHiiEx1qXL1OAFdhBJPAAQeRQDEiRZEm+kwnu2p1XHo64yQ8j47bL1kCZ87pDKWxuW4mQJ9O9ba31xE5Y/rnA4VoTCJQwvnyBNgk+pkDi8sSJjlKRPxhX7r3Lytz0LPMi1H1Qv7VuzwuAzh4h1ukKFi/YV9+9E8THZbne2Ezxd/xsNGQ6u+wgoeH4SH9Tl367t+2Ko/acA8Oj/DhWP7X/30Zkvj4WMYlj10MOISXf7DlkPvvH6g43u0oCzDS1U5f/sHWC3d7cn3UAQf4HeHfwxXQY4yu/HTDKNXro3Gngw4vw2FnPKrXJfUXu0fqIdeFZOnXm08FTRSxcf391pW7oNGT8vRf6i9jqljwYzAm6AAAAABJRU5ErkJggg=="
}
},
// --- VOX ---
// {
// "type": "appleScriptTitledButton",
// "source": {
// "inline":
// "if application \"VOX\" is running then\rtell application \"VOX\"\rif player state is 1 then\rreturn (get artist) & \" – \" & (get track)\relse\rreturn \"\"\rend if\rend tell\rend if\rreturn \"\"\r"
// },
// "action": "appleScript",
// "actionAppleScript": {
// "inline":
// "if application \"VOX\" is running then\rtell application \"VOX\"\rif player state is 1 then\rnext\rend if\rend tell\rend if\r"
// },
// "refreshInterval": 1,
// "image": {
// "base64":
// "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADAUExURUdwTHd7gqeqtKClrd/k7n+Di7e7xI6TnNHX4cPI0Xp9hJibpOfs9u/1/vX7/rq/yZiborK3wEpOUxgYFlFXYiIhHycpLD9ESjI0NgsLCj09O4SKk19lcbBkALhoAHJ3gT8rAzMvJJugqKhcAL5xAOSxB4I/AJ5SAGpweXuAiZJJAG8zAeXr9WNiYM+NANqeAMd9APHACfr9/e7JPrO5w6mrsA4bKC0gCHhtTyAYBFcpAZduKK2JP/fost/Eb5SUlFFfi9AAAAASdFJOUwDJE9FgWjb+/vyHppTXtbn9mDupvZ0AAAdoSURBVFjDlZcLQ6O6FoWrtgI+Owl5EGgp2DrEgnV6wD4c7f//V3ftAE7nzOj17NGxpayPtXfSZGcw+CjOh1c3N9eIm5ur4fngv8Xp8KZE+F2U5bq8GZ5+WU5qP6jzsRdTeF7e1AEoN8Ovya/LMsg9mxZJErpIksJarwnK8nr4FblfQ52EDOH0zL1KUuvVfnn9eTVOryCPoVZQuGj/FOApMAhx9Uktzq/L2ksT3FuQhIVVVVRVRa+SogA1KbzgExPD0s/xdBOmKdOqODR95IdEaZba0LAkbfzyg0rA/pgekxZKV4cmtxXTEmFUEufNodIKYJjLkcbf9UEM90mqZJU3qeJRxDkXAv/hlbJNXkmVJjCBNP5CGPq1TZSyiWB5XomIi+woRBSJKj8YUVgFQ4E//FMfQM8s40VTyIhDpLVRNIJKaWLwSNqm4mEMl/YPwrkP/9AbER+UkxtWbbcvLrbbkBlCcDa23BABHn4bi1PSM2a1PFgZwbwJty+TybSLyeRlyxQlIuNY6FgpDKd/PB+uXP2tFHEheCtvhW3Q65dtiEyQRixMrBjG4uo4gTpNFApvU9Kr6sWp77qY3BFj8lLBBJdpykOLwaqPkgioAEnFi1RCz7Z3uJ2U37sgCBB3W5ZJLm0VJQmVIXgfgSAvQpYK1ACDx7bTOT2dlA8uWsYEl0EQ3KDUKcOECvqRCGgGpApXf9M/PNx3QQwyMZ9uGWZWiGelsFAHvQFUMEx4WAku2HZC+k6+vF/ip0U4wmSrBJcFZgss9xZuYYAlWhaGC1PhMb1++R6EaAl3lRFcFUI5C7duDlAFwpCzSogsfCH93fdWvm+jRYCASs5fwkyIinGsVrBAc2FYxwVLlKhgQG3n8+kv/WwjhNhsZvveQ5+EqrhKWOHVlMNtgyUkhHkpdPjy1CZA+pl4bkOIfWcBQ/EEC1xWWmKxsi6HussA9WUwQBV0BmbPz3y32+x2nD8/z/okpvM5LAjGeEg51JiFtQeAEqHhWUIGJq0B0m9mLjYdgSxMnQVhGHIIkcP5YNjEAGjJJDfF5AnzzRnY/9K3hP3SjSUAk0oLzaQOwyJGEb41FgWVuOIymHYZCKfPGFaxzBHEcQ5cYrXD3LHNt8E3WkdDbpBY8vMdMHsW0Jui9v26MCDAggNMAfgZoghGdIDbPMXewY0RWfHzqQMsN2QgC/MAkVcZWZi1DuYAJJlQBlVM0vx2cDZOw4QAPEt/AcTzDgkkh3w8Hh8SPZvtKIc2haefKapo2mE4G5x5nQOuLQA0jwggCcAqF1SFnZQEwEwCwGZ0P+sAXQqa69gBJvgeLCUBhMS6arSWogV0NWwB2gHG5KAggDYO0OXQATghJP4QIPsFiJHCEYB2YgBE5gBzNxEyjVHYcOxKKDfquRE6u39wFXh6dADJaccG4HLsANIIEb89PnaEvSYLG2kYAG0G+3YMnh4fDxbfe4wkAS47gAAyShsA2iSWUgsiYHfJMCF2ZKDVA9AkET4WbhgvB6OxxeoiuZZRVT/2BFjQfOemMX53qNn+e5fA42OtAOCYiYXNR4PznL4LJoLA1G8rQjjCTCmxc9+GzU4oNXvonr9avTUZPc7QdyHH0j4mgIqQVZQHq1VvAgRKf7MRKISZPWAG0ONXq1Vg6WYahMIbYz24pJ6ECYIyf7EiBBGm3/da0c6KX/ifTnv9P74iu66G3iUAI4+KoCNpeNS8rha9ifnkfmlcLO+n816+WL3mEVVcY2ew4xEtqsgBjUWEQkWsfFssWhdgPM0xoBg8iKl2JF8s3kpngCtMo3jsNljkUDAmYQFVWC9+tAgHeQ93AR/8WKw9d6MMWWFdBn0OJsJsjERZLn4QomOsWmmrpk9Kn0eYxpGiMfBG7dZ0FrcW8B2N1PoV9zkEUehn1b2hy68lxg/F0owVaXzW7Y0j6kwZuiqhUIaW8M54F9Olcq0irF0R1vAkfTdAFmwaKhjDUgfCur39n+NwF9ZrFhnMWm5YiM737L0/GHkxtYD4RKIWILz1+jf86/Vv69I4fSTRTNojAxgIdPUgCE7VjXhzhHAEJ1/nUUSl4qTHKeDyuMnCycCzjiCwyUYsWK9fofzhxIC8rtcBi2SlqPeEnu7/rekGwcutwhTHhKoYHlWvEa8u6FVN17D/Or3Ncfu/Wu5R7I2bmNECRiYYRpuaYtKiBa+0u0j9K7aguMFZZvTvVvUCFuoce5ygSmA9xlcRDR8iyzaZqVq50Coc1zBw8WezfBEfmqBGX+8QXLs1HY9tV3aMMeQSp4A6aP6qdx4aNHyMWnxBHbpwzTIW9vadkGTf/1BPdYhxsqrR7zuGoD4/6pp+iKVkaY3zWGxHHx55TuJxXZa1RSmwIyAyeoGVlf4wG9CBLD755Nx1ehHHub9e+3lBpxUh+0DuuO4ef/H5AXR0gkpg+EofjQed1ej8VsTuEk6T8cno/54cRyc2zms3Bcr+4Esvazz9C/IOYWlESNseoWtU3n5V3tYCDEA8tAce5j3enFyc/scT/Pno4qSLi9HHhf8f8eOIRf9lFswAAAAASUVORK5CYII="
// }
// },
{ "type": "dock", "width": 200, "align": "left"},
//{ "type": "button", "title": "alfred"},
//{ "type": "volume", "width": 160, "align": "right"},
{
"type": "group",
"align": "right",
"bordered": false,
"title": "Media",
"items": [
{"type": "close", "bordered": false, "align": "left"},
{
"type": "brightnessDown",
"bordered": false,
"align": "left",
"width": 36
},
{
"type": "brightness",
"width": 200,
"align": "left",
"image": {
"base64":
"iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAGUExURffLOPfLNyaSVzUAAAACdFJOU/kBxOqnWgAAAbJJREFUSMfVljtyhDAQBVulQCFH4CgcDR1NR9ERFBKoeA5GfGddtkNvwFINFKP5tED22+Zxwviv6QVKfIEc/iNoF5gkpLIeYI8SUp4PsAUJiekADQntF6isQjvxCTrhAJlFqMMBeIH9BMsD7DAb2BhvYbIyNAOCZIWqYKGTpDZJFQu9EKVd44RxQRq3IrULWD62C8wSssWUZEsR0k6wcDOrJZmoBpMKI+s5qkBQCQOUJADVOECdOsDS0gDbsgHMfT4rVwHSrZQFIN5ABka8BgDgAeZ+BztBgvUEnSgVlhNsTFJjvoF5HAZorBpdYKAiSRbqNyBIUr6AjZMdPwO72R40MElS+wZUWA+wQ6LAYkFvdIhkmA+wQSDDdIAGAZ6A34H0x0fca11gBZZsIHSIfnE/5+NjCn/OuiuUB+/aunZwDeNayjXdTpDN0wlY+r1PfWu75nfj8RogN2JuCN2Y5qgMwTI0wGPUnQw6Qarx0sVNKA5Mn6VUL22lIbZoYitDbPmlvocc9Umfl2D7adz1reC3pF8av4m+DCenp/ndZuG3E7fhuC3pH2+vnz8V3MfE+bnxBTXuuIMTrLWHAAAAAElFTkSuQmCC"
}
},
{
"type": "brightnessUp",
"bordered": false,
"align": "left",
"width": 36
},
{
"type": "volumeDown",
"bordered": false,
"align": "left",
"width": 36,
},
{ "type": "volume", "width": 200, "align": "left"},
{
"type": "volumeUp",
"bordered": false,
"align": "left",
"width": 36
},
{"type": "previous", "bordered": false, "align": "center"},
//{"type": "play", "bordered": false, "align": "center"},
{
"type": "appleScriptTitledButton",
"align": "center",
"source": {
"inline":
"if application \"Spotify\" is running then\rtell application \"Spotify\"\rif player state is playing then\rreturn (get artist of current track) & \" – \" & (get name of current track)\relse\rreturn \"\"\rend if\rend tell\rend if\rreturn \"\"\r"
},
"action": "appleScript",
"actionAppleScript": {
"inline":
"if application \"Spotify\" is running then\rtell application \"Spotify\"\rif player state is playing then\rpause\rend if\rend tell\rend if\r"
},
"refreshInterval": 0.0001,
"image": {
"base64":
"iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAYUExURUdwTB3WXx3UXh3VXx7XYBkXFRpVLRyURmIaeAQAAAAEdFJOUwDDO3fSqUUkAAABbklEQVRIx61VbW6DMAztoAeYNA7ApB6gkzhAWS24wIAL0HABaK6/pHFNEhy8TXu/kPzkPD/8cTj8K7KPAqB+K5NhQPCUrABCXe7HOUYYZxgVRLiG8RfY4DUgFFtC7cffAfZTFBwBdhWEKfgEq4ocEjgj4ZQifO6/QG9kkETp1dDeVWfRKx3XYSW0LoqY5kCElXDrQkyeCCuh6WL0M4nIWQIyzqixdfKU1koFDKvyCA8NJMzU4xiD+b4kfHRpsIyKc6hBwjVptFHVY51EMAINNDFGJITKDNQcdpX74Hz0CQ3rY5qwMp4EIxrlafzrsYZ2Veb0DkRgfNCUok4Y1fqEijfyi2b8RE9beWqa48Y/uvCNMcH9btfUi+/CGLR1vhL6Zz9N/vBlaCU+7lwY/cmJ67Ryen/2tj23PLqJBodZH8vgj544vOL4pxfI5acrSFxi8hrkU9TSKr78ZpnL50A8KPJJEo+afBblwyqf5j/iGys5j6ScrST2AAAAAElFTkSuQmCC"
}
},
{"type": "next", "bordered": false, "align": "center"},
]
},
{ "type": "inputsource", "align": "right", },
{ "type": "mute", "bordered": false, "align": "right"},
{ "type": "dnd", "align": "right"},
{ "type": "nightShift", "align": "right"},
{ "type": "sleep", "align": "right", "bordered": false},
{ "type": "currency",
"refreshInterval": 600, // in seconds
"bordered": false,
"align": "right",
"from": "USD",
"to": "HKD",},
{
"type": "pomodoro",
"bordered": false,
"align": "right",
"workTime": 1800, // set time work in seconds. Default 1500 (25 min)
"restTime": 600, // set time rest in seconds. Default 300 (5 min)
},
{ "type": "weather", "refreshInterval": 60, "units": "metric", "align": "right", "bordered": false, "icon_type": "images", "api_key": "84645702688e83a35e2549ca77f73369"},
{ "type": "battery", "align": "right", "bordered": false },
{ "type": "timeButton", "align": "right", "formatTemplate": "E MMM d h:mm a", "bordered": false }
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+90
View File
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 250 49" style="enable-background:new 0 0 250 49;" xml:space="preserve">
<style type="text/css">
.st0{fill:#189BD7;}
.st1{enable-background:new ;}
.st2{fill:#FFFFFF;}
</style>
<title>support</title>
<path class="st0" d="M6.6,0h236.8c3.7,0,6.6,3,6.6,6.6v35.8c0,3.7-3,6.6-6.6,6.6c0,0,0,0,0,0H6.6C3,49,0,46,0,42.4c0,0,0,0,0,0V6.6
C0,3,3,0,6.6,0z"/>
<g class="st1">
<path class="st2" d="M25.4,18.8c-0.6-0.8-1.6-1.4-2.9-1.4c-1.4,0-3,0.8-3,2.6c0,1.8,1.5,2.2,3,2.7c2,0.7,4.1,1.3,4.1,4.1
c0,2.8-2.3,4-4.6,4c-1.7,0-3.4-0.7-4.4-2.1l1.2-0.9c0.6,1,1.7,1.8,3.2,1.8c1.4,0,3.1-0.9,3.1-2.7c0-2-1.6-2.4-3.4-3
c-1.9-0.6-3.8-1.4-3.8-3.9c0-2.7,2.4-3.9,4.5-3.9c1.9,0,3.3,0.8,3.9,1.7L25.4,18.8z"/>
<path class="st2" d="M36.4,30.6c0-0.5-0.1-1.2-0.1-1.6h0c-0.5,1.1-1.9,1.8-3.2,1.8c-2.3,0-3.5-1.5-3.5-3.8v-5.7h1.3v5.1
c0,1.9,0.6,3.2,2.5,3.2c1.4,0,2.8-1.1,2.8-3.6v-4.7h1.3v7.2c0,0.5,0,1.4,0.1,2H36.4z"/>
<path class="st2" d="M42.5,35.3h-1.3v-14h1.3v1.6h0.1c0.8-1.2,2.2-1.8,3.5-1.8c2.9,0,4.8,2.1,4.8,4.9c0,2.7-1.9,4.9-4.8,4.9
c-1.3,0-2.7-0.7-3.5-1.8h-0.1V35.3z M45.9,22.2c-2.1,0-3.6,1.6-3.6,3.7c0,2.1,1.5,3.7,3.6,3.7c2.2,0,3.5-1.7,3.5-3.7
C49.4,23.9,48.1,22.2,45.9,22.2z"/>
<path class="st2" d="M55,35.3h-1.3v-14H55v1.6h0.1c0.8-1.2,2.2-1.8,3.5-1.8c2.9,0,4.8,2.1,4.8,4.9c0,2.7-1.9,4.9-4.8,4.9
c-1.3,0-2.7-0.7-3.5-1.8H55V35.3z M58.4,22.2c-2.1,0-3.6,1.6-3.6,3.7c0,2.1,1.5,3.7,3.6,3.7c2.2,0,3.5-1.7,3.5-3.7
C61.9,23.9,60.6,22.2,58.4,22.2z"/>
<path class="st2" d="M70.4,30.8c-2.9,0-4.9-2.1-4.9-4.9c0-2.8,2.1-4.9,4.9-4.9c2.9,0,4.9,2.1,4.9,4.9
C75.4,28.7,73.3,30.8,70.4,30.8z M70.4,22.2c-2.1,0-3.5,1.7-3.5,3.7c0,2.1,1.4,3.7,3.5,3.7c2.2,0,3.5-1.6,3.5-3.7
C74,23.9,72.6,22.2,70.4,22.2z"/>
</g>
<g class="st1">
<path class="st2" d="M78.3,21.3h1.3c0,0.5,0.1,1.2,0.1,1.6h0c0.5-1.1,1.7-1.8,3-1.8c0.3,0,0.6,0,0.9,0.1l-0.2,1.3
c-0.2-0.1-0.6-0.1-0.9-0.1c-1.4,0-2.7,1-2.7,3.5v4.7h-1.3v-7.2C78.4,22.9,78.4,22,78.3,21.3z"/>
</g>
<g class="st1">
<path class="st2" d="M89.5,22.4h-2.6v5.4c0,1.4,0.6,1.7,1.4,1.7c0.4,0,0.8-0.1,1.2-0.3l0.1,1.2c-0.5,0.2-1,0.3-1.6,0.3
c-1,0-2.4-0.4-2.4-2.5v-5.8h-1.9v-1.1h1.9v-2.6h1.3v2.6h2.6V22.4z"/>
<path class="st2" d="M100.8,30.8c-2.9,0-4.9-2.1-4.9-4.9c0-2.8,2.1-4.9,4.9-4.9c2.9,0,4.9,2.1,4.9,4.9
C105.7,28.7,103.7,30.8,100.8,30.8z M100.8,22.2c-2.1,0-3.5,1.7-3.5,3.7c0,2.1,1.4,3.7,3.5,3.7c2.2,0,3.5-1.6,3.5-3.7
C104.3,23.9,102.9,22.2,100.8,22.2z"/>
<path class="st2" d="M109.8,21.3c0,0.5,0.1,1.2,0.1,1.6h0c0.5-1.1,1.9-1.8,3.2-1.8c2.3,0,3.5,1.5,3.5,3.8v5.7h-1.3v-5.1
c0-1.9-0.6-3.2-2.5-3.2c-1.4,0-2.8,1.1-2.8,3.6v4.7h-1.3v-7.2c0-0.5,0-1.4-0.1-2H109.8z"/>
</g>
<path class="st2" d="M166,18.3c0.1,0,0.2,0,0.3,0h4.9c1.1-0.1,2.2,0.1,3.2,0.4c0.8,0.2,1.5,0.7,2,1.4c0.5,0.8,0.7,1.8,0.6,2.8
c-0.1,0.9-0.3,1.7-0.6,2.5c-0.3,0.8-0.8,1.5-1.4,2.1c-0.9,0.7-2,1.2-3.1,1.4c-1.1,0.1-2.2,0.1-3.3,0.1c-0.3,0-0.5,0.1-0.6,0.4
c-0.1,0.2-0.2,0.4-0.2,0.6c-0.2,1.2-0.4,2.4-0.6,3.6c-0.1,0.4-0.4,0.7-0.8,0.7h-2.8c-0.3,0-0.5-0.2-0.5-0.5c0,0,0,0,0-0.1
c0.8-4.9,1.5-9.8,2.3-14.6C165.4,18.7,165.7,18.4,166,18.3z M169.1,21.5c-0.1,0.1-0.2,0.3-0.2,0.4c-0.2,1.2-0.4,2.5-0.6,3.7h0.9
c0.8,0.1,1.6-0.1,2.3-0.4c0.5-0.3,0.9-0.8,1-1.4c0.2-0.5,0.2-1.1,0-1.6c-0.2-0.4-0.5-0.6-0.9-0.8c-0.7-0.2-1.3-0.2-2-0.2
C169.5,21.4,169.3,21.4,169.1,21.5L169.1,21.5z"/>
<path class="st2" d="M179.3,24.1c1-0.6,2.1-0.9,3.2-0.8c0.9,0,1.7,0.2,2.5,0.6c0.3,0.2,0.5,0.5,0.7,0.8c0.1-0.3,0.1-0.5,0.1-0.8
c0.1-0.2,0.3-0.4,0.5-0.4h2.7c0.3,0,0.5,0.2,0.5,0.5c0,0,0,0.1,0,0.1c-0.5,3.1-1,6.2-1.5,9.4c-0.1,0.4-0.4,0.7-0.8,0.7h-2.5
c-0.2,0-0.4-0.1-0.5-0.3c0-0.1,0-0.3,0-0.4c0-0.2,0.1-0.4,0.1-0.6c-0.6,0.6-1.3,1-2.1,1.3c-0.9,0.3-1.9,0.3-2.9,0.1
c-0.7-0.1-1.4-0.5-2-1c-0.6-0.6-1.1-1.5-1.2-2.4c-0.2-1.1-0.1-2.1,0.2-3.2C176.9,26.2,177.9,24.9,179.3,24.1z M182.6,26.2
c-0.6,0.1-1.2,0.3-1.6,0.7c-0.6,0.5-1,1.1-1.1,1.9c-0.1,0.6,0,1.3,0.3,1.8c0.3,0.4,0.7,0.7,1.2,0.8c0.8,0.2,1.7,0.1,2.4-0.3
c1.1-0.7,1.7-1.9,1.5-3.2c-0.1-0.6-0.5-1.2-1.1-1.4C183.6,26.2,183.1,26.2,182.6,26.2z"/>
<path class="st2" d="M191,23.7c0.1-0.1,0.3-0.2,0.5-0.2h2.6c0.2,0,0.4,0.1,0.6,0.2c0.1,0.2,0.2,0.4,0.3,0.6l1.5,5.1
c1.2-1.7,2.3-3.5,3.5-5.2c0.1-0.2,0.3-0.4,0.4-0.6c0.2-0.1,0.4-0.2,0.6-0.2c0.9,0,1.8,0,2.7,0c0.3,0,0.5,0.3,0.4,0.5
c0,0.1,0,0.2-0.1,0.3l-9.2,13.2c-0.1,0.2-0.4,0.4-0.7,0.4h-2.7c-0.2,0-0.3,0-0.4-0.1c-0.2-0.2-0.2-0.5-0.1-0.7l2.8-3.9
c0-0.1,0-0.1,0-0.2l-3-8.8C190.8,24,190.9,23.8,191,23.7z"/>
<path class="st2" d="M206.9,18.3c0.1,0,0.2,0,0.4,0h5.1c1-0.1,2.1,0.1,3.1,0.4c0.8,0.3,1.4,0.8,1.9,1.5c0.5,0.9,0.7,1.8,0.5,2.8
c-0.1,1-0.4,2-0.9,2.9c-0.4,0.8-1,1.5-1.7,2c-0.8,0.5-1.7,0.9-2.7,1c-1.1,0.1-2.2,0.1-3.3,0.1c-0.3,0-0.5,0.2-0.6,0.4
c-0.1,0.2-0.1,0.4-0.2,0.6l-0.6,3.8c0,0.3-0.3,0.5-0.5,0.5h-3c-0.3,0-0.5-0.2-0.5-0.5c0,0,0,0,0-0.1c0.8-4.9,1.5-9.8,2.3-14.6
C206.3,18.6,206.6,18.4,206.9,18.3z M209.9,21.9c-0.2,1.3-0.4,2.5-0.6,3.8c0.9,0,1.8,0,2.7-0.2c0.5-0.1,0.9-0.4,1.1-0.7
c0.3-0.5,0.5-1,0.5-1.5c0.1-0.4-0.1-0.9-0.3-1.3c-0.3-0.3-0.7-0.5-1.1-0.5c-0.6-0.1-1.1-0.1-1.7-0.1
C210.1,21.4,209.9,21.6,209.9,21.9C209.9,21.8,209.9,21.8,209.9,21.9L209.9,21.9z"/>
<path class="st2" d="M233.6,18.3c0.1,0,0.3,0,0.4,0h2.4c0.3,0,0.5,0.2,0.5,0.5c0,0,0,0.1,0,0.1c-0.8,4.9-1.5,9.8-2.3,14.7
c-0.1,0.4-0.4,0.7-0.8,0.7h-2.4c-0.3,0-0.5-0.2-0.5-0.5c0,0,0-0.1,0-0.1c0.8-5,1.6-9.9,2.3-14.9C233.2,18.6,233.4,18.4,233.6,18.3z"
/>
<path class="st2" d="M152.5,19c0.8,0.4,1.5,1.2,1.8,2c0.3,1.1,0.4,2.2,0.1,3.3c-0.2,1.5-0.9,3-1.9,4.1c-0.6,0.7-1.4,1.3-2.3,1.6
c-1.3,0.5-2.7,0.7-4.1,0.7c-0.3,0-0.6,0-0.8,0.1c-0.3,0.2-0.5,0.5-0.6,0.8c-0.3,1.6-0.5,3.1-0.8,4.7c0,0.2-0.1,0.5-0.1,0.7
c-0.1,0.4-0.5,0.7-0.9,0.7H139c-0.2,0-0.5-0.1-0.6-0.3c-0.1-0.2-0.1-0.4,0-0.6c0.1-0.6,0.2-1.3,0.3-1.9c0-0.1,0-0.2,0.1-0.4
c0.3-2.3,0.7-4.6,1.1-6.8c0-0.2,0.1-0.4,0.2-0.7c0.2-0.2,0.4-0.4,0.7-0.5c0.3,0,0.6,0,0.9,0c1.2,0,2.4,0,3.6-0.1
c1.6-0.2,3.2-0.8,4.5-1.9c1.2-1.1,2-2.4,2.4-3.9C152.3,20.1,152.4,19.6,152.5,19z"/>
<path class="st2" d="M221.4,23.6c0.9-0.3,1.9-0.4,2.9-0.2c0.6,0.1,1.2,0.3,1.8,0.6c0.2,0.2,0.5,0.4,0.6,0.7c0-0.3,0.1-0.5,0.1-0.8
c0.1-0.2,0.3-0.4,0.5-0.3h2.7c0.3,0,0.5,0.2,0.5,0.5c0,0,0,0.1,0,0.1l-1.5,9.3c0,0.4-0.4,0.7-0.8,0.7h-2.5c-0.3,0-0.5-0.2-0.5-0.4
c0,0,0,0,0-0.1c0-0.3,0.1-0.5,0.1-0.8c-0.7,0.7-1.5,1.1-2.4,1.4c-0.9,0.2-1.8,0.2-2.7,0c-0.9-0.2-1.6-0.7-2.2-1.3
c-0.7-0.9-1-2-1-3.2c0-1.7,0.7-3.4,1.9-4.6C219.7,24.4,220.5,23.9,221.4,23.6z M223.5,26.2c-0.9,0.1-1.8,0.6-2.3,1.4
c-0.4,0.7-0.6,1.5-0.5,2.3c0.1,0.6,0.4,1.1,0.9,1.3c0.6,0.3,1.3,0.4,2,0.3c0.8-0.2,1.6-0.7,2-1.4c0.4-0.7,0.6-1.5,0.4-2.3
c-0.2-0.6-0.6-1.1-1.1-1.3C224.5,26.2,224.1,26.2,223.5,26.2L223.5,26.2z"/>
<path class="st2" d="M133.8,34.7c-0.2,0-0.3-0.1-0.3-0.3v-0.2c0.1-0.2,0.1-0.5,0.1-0.7l3.1-19.7c0-0.2,0.1-0.4,0.2-0.6
c0.1-0.2,0.3-0.3,0.6-0.3h8.5c1.4,0,2.7,0.3,4,0.9c0.9,0.5,1.7,1.3,2,2.3c0.3,0.9,0.3,1.8,0.1,2.8c-0.1,0.6-0.2,1.1-0.4,1.6
c-0.4,1.4-1.2,2.8-2.3,3.8c-1.2,1-2.7,1.6-4.3,1.8c-0.8,0.1-1.6,0.1-2.3,0.1h-1.7c-0.2,0-0.4,0-0.6,0c-0.4,0.1-0.7,0.3-0.9,0.7V27
v0.1c-0.1,0.2-0.1,0.5-0.2,0.8c-0.2,1-0.3,2-0.5,3.1c-0.2,1.3-0.4,2.5-0.6,3.8l0,0h-4.6L133.8,34.7z"/>
<path class="st0" d="M146,13.3c1.3,0,2.6,0.3,3.8,0.9c0.9,0.5,1.5,1.2,1.8,2.1c0.2,0.8,0.3,1.7,0.1,2.6l0,0l0,0
c-0.1,0.5-0.2,1-0.4,1.5c-0.4,1.4-1.1,2.6-2.2,3.6c-1.2,1-2.6,1.5-4.1,1.7c-0.8,0.1-1.5,0.1-2.3,0.1h-1.7c-0.2,0-0.4,0-0.6,0.1
c-0.5,0.1-0.9,0.4-1.1,0.8l-0.1,0.1V27c-0.1,0.3-0.1,0.5-0.2,0.8c-0.2,1-0.3,2-0.5,3c-0.2,1.2-0.4,2.3-0.6,3.5h-4.2
c0.1-0.2,0.1-0.5,0.1-0.7v-0.2l1.1-7.1l1.9-12.1c0-0.1,0-0.1,0-0.2c0-0.1,0-0.3,0.1-0.4c0.1-0.1,0.2-0.1,0.3-0.2L146,13.3 M146,12.5
h-8.6c-0.3,0-0.6,0.2-0.8,0.5c-0.2,0.3-0.2,0.6-0.3,0.9l-3,19.3c-0.1,0.3-0.1,0.6-0.2,1v0.3c0.1,0.3,0.3,0.5,0.6,0.6h4.4h0.5
c0-0.1,0-0.2,0.1-0.4c0.3-2.3,0.7-4.6,1.1-6.8c0-0.2,0.1-0.4,0.1-0.7c0.2-0.2,0.4-0.4,0.7-0.5c0.2,0,0.3,0,0.5,0h1.7
c0.8,0,1.6,0,2.4-0.1c1.6-0.2,3.2-0.8,4.5-1.9c1.2-1.1,2-2.4,2.4-3.9c0.2-0.5,0.3-1.1,0.4-1.7l0,0c0.2-1,0.1-2-0.1-2.9
c-0.4-1.1-1.2-2-2.2-2.6c-1.3-0.6-2.7-0.9-4.1-0.9l0,0L146,12.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 8.0 KiB

+9
View File
@@ -26,6 +26,7 @@ VERSION=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${NAME}.a
MINIMUM=`/usr/libexec/PlistBuddy -c "Print LSMinimumSystemVersion" ${NAME}.app/Contents/Info.plist` MINIMUM=`/usr/libexec/PlistBuddy -c "Print LSMinimumSystemVersion" ${NAME}.app/Contents/Info.plist`
SIZE=`stat -f%z MTMR\ ${VERSION}.dmg` SIZE=`stat -f%z MTMR\ ${VERSION}.dmg`
SIGN=`~/Sparkle/bin/sign_update MTMR\ ${VERSION}.dmg ~/Sparkle/bin/dsa_priv.pem | awk '{printf "%s",$0} END {print ""}'` SIGN=`~/Sparkle/bin/sign_update MTMR\ ${VERSION}.dmg ~/Sparkle/bin/dsa_priv.pem | awk '{printf "%s",$0} END {print ""}'`
SHA256=`shasum -a 256 MTMR\ ${VERSION}.dmg | awk '{print $1}'`
# ditto -c -k --sequesterRsrc --keepParent "${NAME}.app" "${NAME}v${VERSION}.zip" # ditto -c -k --sequesterRsrc --keepParent "${NAME}.app" "${NAME}v${VERSION}.zip"
@@ -57,5 +58,13 @@ echo "<?xml version=\"1.0\" standalone=\"yes\"?>
</channel> </channel>
</rss>" > appcast.xml </rss>" > appcast.xml
echo ""
echo "Homebrew https://github.com/Homebrew/homebrew-cask/edit/master/Casks/mtmr.rb"
echo ""
echo " version '${VERSION}'"
echo " sha256 '${SHA256}'"
echo ""
echo "Update MTMR v${VERSION}"
scp MTMR\ ${VERSION}.dmg do:/var/www/mtmr scp MTMR\ ${VERSION}.dmg do:/var/www/mtmr
scp appcast.xml do:/var/www/mtmr scp appcast.xml do:/var/www/mtmr
Executable
+30
View File
@@ -0,0 +1,30 @@
NAME='MTMR'
killall $NAME
rm -r Release 2>/dev/null
# xcodebuild \
# -workspace ./MTMR.xcodeproj/project.xcworkspace \
# -scheme MTMR \
# -configuration Release CONFIGURATION_BUILD_DIR=./build/Release
xcodebuild archive \
-scheme "$NAME" \
-archivePath Release/App.xcarchive
xcodebuild \
-exportArchive \
-archivePath Release/App.xcarchive \
-exportOptionsPlist export-options.plist \
-exportPath Release
cd Release
rm -r App.xcarchive
# Prerequisite: npm i -g create-dmg
NAME_DMG="${NAME}.app"
echo $NAME_DMG
create-dmg $NAME_DMG
open ./$NAME.app