Compare commits

..
104 Commits
Author SHA1 Message Date
toxblh 62a2aa14e1 v0.15 2018-05-14 10:33:17 +01:00
toxblh 1c0a2f064a upd Readme 2018-05-14 10:32:38 +01:00
Anton Palgunov 23357816b3 Merge pull request #74 from Toxblh/blackListedApps
+ toggle blacklisted app
2018-05-14 09:26:09 +01:00
Anton Palgunov 877dca7587 Merge pull request #64 from Toxblh/musicWidget
+ music widget
2018-05-14 08:13:59 +01:00
Anton Palgunov fd537bcdfe Merge pull request #75 from ReDetection/experimental-private-queue
experimental: run applescript on separate serial queue
2018-05-13 19:35:28 +01:00
toxblh e852ab58f8 change stip icon 2018-05-12 20:25:14 +01:00
Serg 5c7be625b6 fix warnings, switch track of only first player running 2018-05-13 00:11:47 +07:00
Serg 475b66c956 Merge branch 'master' into musicWidget and fix hidden property usage
# Conflicts:
#	MTMR/CustomButtonTouchBarItem.swift
#	MTMR/TouchBarController.swift
2018-05-12 21:47:13 +07:00
Serg 19f7ff0103 Merge branch 'master' into experimental-private-queue
# Conflicts:
#	MTMR/AppleScriptTouchBarItem.swift
2018-05-12 20:30:36 +07:00
Serg 3bc75b54c0 naive backport of compact map to old swift 2018-05-12 20:19:11 +07:00
Anton Palgunov 3c072923d2 Merge pull request #76 from ReDetection/hide-button
Code cleanup; Hide button property
2018-05-12 12:29:24 +02:00
Serg b29c74ccca Merge branch 'master' into musicWidget
# Conflicts:
#	MTMR/CustomButtonTouchBarItem.swift
2018-05-12 13:21:25 +07:00
Serg bbea465500 hide button property, cleanup nasty code 2018-05-12 12:02:45 +07:00
Serg f7eb49e5de public tap/longtap button action hanglers 2018-05-12 11:41:13 +07:00
Serg e9c69f6461 experimental: run applescript on separate serial queue 2018-05-12 11:11:53 +07:00
Serg 9e1fcb9a35 incapsulate button image handling 2018-05-12 10:49:16 +07:00
ad 7523605d7a + toggle blacklisted app 2018-05-11 16:31:13 +03:00
Anton Palgunov b73f8865a9 Merge pull request #73 from Toxblh/toggleControlStrip
+ toggle Control Strip
2018-05-11 13:51:30 +02:00
ad 852df9d6b6 + toggle Control Strip 2018-05-11 14:40:02 +03:00
toxblh 57e8af90dd close #10 2018-05-11 13:26:52 +02:00
Anton Palgunov 0e48566c74 Merge pull request #72 from ReDetection/fix-button-titles-2
fix button titles and images
2018-05-11 11:35:54 +02:00
Serg 55177a7f90 fix currency widget updating 2018-05-11 09:32:42 +07:00
Serg d46827832e another try to fix button titles and images 2018-05-11 00:29:05 +07:00
toxblh 9e10b2d3d1 upd. Build.sh 2018-05-10 10:11:50 +02:00
toxblh 33efbbebaa switch hidKeys to obj-c realesation 2018-05-10 09:56:04 +02:00
Anton Palgunov d048ec851e Merge pull request #65 from ReDetection/fix-width
Fix width for borderless buttons
2018-05-10 09:18:39 +02:00
Anton Palgunov 42ebb91e6b Merge pull request #66 from ReDetection/fix-taps
Fix taps for borderless buttons
2018-05-10 08:19:40 +02:00
Serg 1da4e2795f fix taps for borderless buttons 2018-05-10 09:31:37 +07:00
Serg 21bc525a04 fix setting width for borderless buttons 2018-05-10 09:02:30 +07:00
ad b9ef3cdc35 + music widget 2018-05-09 21:34:44 +03:00
Anton Palgunov 9a34d5bca0 Merge pull request #62 from ReDetection/fix-non-bordered-button
Fixes for borderless buttons
2018-05-09 12:22:01 +02:00
Anton Palgunov 4c9820a390 Merge pull request #63 from ReDetection/tests
Example on how to make tests + travis integration
2018-05-09 09:34:20 +02:00
Serg 634beeae8b add travis shield to readme 2018-05-09 14:09:40 +07:00
Serg 3ec2422181 add couple more tests 2018-05-09 14:07:37 +07:00
Serg a3c7fa7464 replace garbage in the app with garbage in the tests (until apple fix that), enable coverage 2018-05-09 13:34:32 +07:00
Serg 20ed827750 fix that borderless buttons in scrollable area were not centered, fix wrong text color for borderless colored buttons 2018-05-09 12:23:02 +07:00
Serg 85aa2c4f2b original key event type 2018-05-07 09:08:39 +07:00
Serg b761551384 fix imports 2018-05-07 09:04:33 +07:00
Serg 3854b455a6 workaround travis bug, add xcpretty 2018-05-07 08:42:24 +07:00
Serg 9c803e85cc ensure shared schemes, add travis config 2018-05-07 07:14:14 +07:00
Serg 651258b01f fix building test target, add one more test 2018-05-06 17:13:31 +07:00
Anton Palgunov 02baef80db Update README.md 2018-05-04 14:10:49 +01:00
toxblh cd75be6c26 v0.14 2018-04-30 22:17:04 +01:00
Anton Palgunov 7646c6e89d Merge pull request #58 from ReDetection/bordered-parameter
WIP: Bordered parameter, background parameter
2018-04-30 19:20:07 +01:00
toxblh 9c970aff63 if button have only image, change position to .imageonly 2018-04-30 19:19:33 +01:00
Serg 49d7333d5e implement highlight for buttons with no border 2018-04-30 23:18:59 +07:00
Serg cd69bc341c fix margin for some buttons 2018-04-30 22:54:15 +07:00
Serg 453895f395 fix wrong title while first execution 2018-04-30 18:53:32 +07:00
Serg 12796d6387 fix for colors in non-bordered buttons 2018-04-30 18:40:26 +07:00
Anton Palgunov f65e72faed Merge pull request #57 from Toxblh/revert-56-master
Revert "tapAction and longTapAction in config now have nested values"
2018-04-30 11:29:52 +01:00
Anton Palgunov df535b2893 Revert "tapAction and longTapAction in config now have nested values" 2018-04-30 11:29:42 +01:00
Anton Palgunov 574ca0fb84 Merge pull request #56 from ad/master
tapAction and longTapAction in config now have nested values
2018-04-30 11:08:28 +01:00
ad 4a03ba79ec + background option for button
# Conflicts:
#	MTMR/CustomButtonTouchBarItem.swift
#	MTMR/ItemsParsing.swift
2018-04-30 15:43:55 +07:00
ad 907b79965d + hexColor 2018-04-30 15:26:29 +07:00
Serg fbec803d47 introduce bordered parameter, restore 0.13 borders 2018-04-30 15:14:27 +07:00
ad 3f9574245a more constants from ev_keymap.h 2018-04-30 10:15:07 +03:00
ad 2bf6b90b04 * fix for brightness 2018-04-30 10:11:22 +03:00
ad 9d596c6adc Merge remote-tracking branch 'upstream/master' 2018-04-30 09:51:42 +03:00
ad 175e9edb8c Merge branch 'nestedActions' 2018-04-30 09:51:26 +03:00
ad 8e58bb179a tap and longTap 2018-04-30 09:50:59 +03:00
Anton Palgunov d37d9607d3 Merge pull request #55 from ad/master
deinit inputsource observer, button highlight on touch
2018-04-29 23:00:07 +01:00
ad 6b553c4d0d + deinit inputsource observer 2018-04-30 00:57:32 +03:00
ad 788927edb3 + highlighting button background on press 2018-04-30 00:57:09 +03:00
ad 9013779f71 Merge remote-tracking branch 'upstream/master' 2018-04-29 22:51:34 +03:00
toxblh a85507c544 upd Readme, upd Default 2018-04-29 19:44:58 +01:00
toxblh 59aa113995 build.sh upd 2018-04-29 19:27:01 +01:00
Anton Palgunov 9c5a19925b Merge pull request #54 from Toxblh/fix-leaked-items-and-timers
Fix leaking timers
2018-04-29 18:19:55 +01:00
Serg 2aa602d77d recreate touchbar to clear it's cache (items survive otherwise, still doing their job) 2018-04-29 23:37:09 +07:00
ad a1b3f13902 Merge remote-tracking branch 'upstream/master' 2018-04-29 14:59:13 +03:00
Anton Palgunov 60384561d2 Merge pull request #53 from Toxblh/fix-warnings
Fix compiler warnings
2018-04-29 12:55:00 +01:00
Serg 77938340d6 fix compiler warnings 2018-04-29 18:03:30 +07:00
ad 93d2f496d0 * first working 2018-04-29 01:18:05 +03:00
ad 0808f0617a * fix 2018-04-28 02:28:09 +03:00
ad 966a924183 Merge remote-tracking branch 'upstream/master' 2018-04-28 02:23:57 +03:00
ad a648402d04 Merge branch 'master' of https://github.com/ad/MTMR 2018-04-28 02:22:24 +03:00
ad 207af6468e * button highlight on touch 2018-04-28 02:22:05 +03:00
Anton Palgunov 6609a7c806 Merge pull request #49 from ad/master
* NSButtonCell
2018-04-27 23:39:02 +01:00
toxblh 479190d12b fix center line for apple script 2018-04-27 23:38:13 +01:00
ad 5ed1790601 * scrollView baseline fix 2018-04-27 21:53:27 +03:00
toxblh b95f5af95f lead position for image in image+title button 2018-04-27 19:13:55 +01:00
ad c76027ca9d * fix for button.image size 2018-04-27 20:18:22 +03:00
ad 55dc551516 * fix for longAction 2018-04-27 19:17:19 +03:00
ad cf5d8bf546 * staticButton image size 2018-04-27 19:03:58 +03:00
ad 8031108fc9 * font baseline like in original touchbar, styled battery widget (time showing as superscript) 2018-04-27 18:46:29 +03:00
ad d1a577868b * button background 2018-04-27 16:17:40 +03:00
ad 5f02bb2962 * font style 2018-04-27 14:32:07 +03:00
ad cc1332dd02 * button font color 2018-04-27 14:05:13 +03:00
ad a0dbfca828 * input source widget image size 2018-04-27 12:55:54 +03:00
ad 76bdf12745 * fixed switching input source 2018-04-26 22:50:38 +03:00
ad e1268d0d65 * NSButtonCell 2018-04-26 22:36:12 +03:00
toxblh 9c1e5f5032 Merge branch 'master' of https://github.com/Toxblh/MTMR 2018-04-26 14:54:36 +01:00
toxblh 88cde9256a v0.13.2 2018-04-26 14:54:24 +01:00
Anton Palgunov d974f92e3d brew instruction 2018-04-25 13:48:39 +01:00
toxblh 4b8ef1cd96 Merge remote-tracking branch 'origin/master' 2018-04-24 22:54:12 +01:00
toxblh e65c57dbaa If level a battery less 10% it will be red. 2018-04-24 22:53:56 +01:00
Anton Palgunov babc8451e0 Merge pull request #48 from jstart/patch-1
Spelling :)
2018-04-24 22:36:20 +01:00
Christopher Truman 7698ea1c8e Spelling :) 2018-04-24 14:10:18 -07:00
Anton Palgunov aa95d1a1bd Merge pull request #46 from ad/master
* trying to terminate app before forceTerminate (to prevent loosing of unsaved data)
2018-04-24 13:36:55 +01:00
ad 3fe6c22068 * trying to terminate app before forceTerminate (to prevent loosing of unsaved data) 2018-04-24 13:19:22 +03:00
toxblh 5ab19ee27b v0.13.1 2018-04-24 11:14:29 +01:00
Anton Palgunov ae4b0aa870 Merge pull request #45 from ad/master
Allow multiple instances of currency and weather widgets (different schedulers for one type widget)
2018-04-24 10:59:22 +01:00
ad 1e2043ebda * allow multiple instances of currency and weather widgets (different schedulers for one type widget) 2018-04-24 12:49:36 +03:00
Anton Palgunov 409b8d8b46 Merge pull request #44 from ad/master
* dock widget improvements
2018-04-24 10:47:34 +01:00
ad 8bccbf3834 * dock widget improvements 2018-04-23 20:09:05 +03:00
29 changed files with 1178 additions and 305 deletions
+6
View File
@@ -0,0 +1,6 @@
language: swift
xcode_project: MTMR.xcodeproj
xcode_scheme: UnitTests
osx_image: xcode9.3
install: gem install xcpretty
script: "xcodebuild test -project MTMR.xcodeproj -scheme 'UnitTests' | xcpretty -c && exit ${PIPESTATUS[0]}"
+20 -19
View File
@@ -7,7 +7,11 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
36300E83209F040900B31C71 /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; };
36300E8F20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */; };
36300E9120A2D2B200B31C71 /* BackgroundColorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36300E9020A2D2B200B31C71 /* BackgroundColorTests.swift */; };
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368EDDE620812A1D00E10953 /* ScrollViewItem.swift */; }; 368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368EDDE620812A1D00E10953 /* ScrollViewItem.swift */; };
36A778BE20A6C27100B38714 /* GeneralExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36A778BD20A6C27100B38714 /* GeneralExtensions.swift */; };
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */; }; 36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */; };
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */; }; 36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */; };
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; }; 36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
@@ -20,6 +24,7 @@
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */; }; 6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */; };
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */; }; 607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */; };
607EEA4D2087A8DA009DA5F0 /* CurrencyBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */; }; 607EEA4D2087A8DA009DA5F0 /* CurrencyBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */; };
60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60C44AFC20A373A100C0EC91 /* MusicBarItem.swift */; };
60F7D454208CC31400ABF5D2 /* InputSourceBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */; }; 60F7D454208CC31400ABF5D2 /* InputSourceBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */; };
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; }; B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; };
B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */; }; B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */; };
@@ -46,18 +51,12 @@
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 */
/* Begin PBXContainerItemProxy section */
B082B262205C7D8000BC04DC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B082B247205C7D8000BC04DC /* Project object */;
proxyType = 1;
remoteGlobalIDString = B082B24E205C7D8000BC04DC;
remoteInfo = MTMR;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
36300E85209FD16700B31C71 /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = SOURCE_ROOT; };
36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptDefinitionTests.swift; sourceTree = "<group>"; };
36300E9020A2D2B200B31C71 /* BackgroundColorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundColorTests.swift; sourceTree = "<group>"; };
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewItem.swift; sourceTree = "<group>"; }; 368EDDE620812A1D00E10953 /* ScrollViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewItem.swift; sourceTree = "<group>"; };
36A778BD20A6C27100B38714 /* GeneralExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralExtensions.swift; sourceTree = "<group>"; };
36BDC22F207CDA8600FCFEBE /* TECHNICAL_DEBT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TECHNICAL_DEBT.md; sourceTree = "<group>"; }; 36BDC22F207CDA8600FCFEBE /* TECHNICAL_DEBT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TECHNICAL_DEBT.md; sourceTree = "<group>"; };
36C2ECD2207B3B1D003CDA33 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; }; 36C2ECD2207B3B1D003CDA33 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeTouchBarItem.swift; sourceTree = "<group>"; }; 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeTouchBarItem.swift; sourceTree = "<group>"; };
@@ -72,6 +71,7 @@
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = DeprecatedCarbonAPI.c; sourceTree = "<group>"; }; 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = DeprecatedCarbonAPI.c; sourceTree = "<group>"; };
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherBarItem.swift; sourceTree = "<group>"; }; 607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherBarItem.swift; sourceTree = "<group>"; };
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyBarItem.swift; sourceTree = "<group>"; }; 607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyBarItem.swift; sourceTree = "<group>"; };
60C44AFC20A373A100C0EC91 /* MusicBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicBarItem.swift; sourceTree = "<group>"; };
60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputSourceBarItem.swift; sourceTree = "<group>"; }; 60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputSourceBarItem.swift; sourceTree = "<group>"; };
B0008E542080286C003AD4DD /* SupportHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportHelpers.swift; sourceTree = "<group>"; }; B0008E542080286C003AD4DD /* SupportHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportHelpers.swift; sourceTree = "<group>"; };
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryBarItem.swift; sourceTree = "<group>"; }; B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryBarItem.swift; sourceTree = "<group>"; };
@@ -177,6 +177,7 @@
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */, B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */,
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */, 368EDDE620812A1D00E10953 /* ScrollViewItem.swift */,
B05600D22083E9BB00EB218D /* CustomSlider.swift */, B05600D22083E9BB00EB218D /* CustomSlider.swift */,
36A778BD20A6C27100B38714 /* GeneralExtensions.swift */,
); );
path = MTMR; path = MTMR;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -184,8 +185,11 @@
B082B264205C7D8000BC04DC /* MTMRTests */ = { B082B264205C7D8000BC04DC /* MTMRTests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
36300E85209FD16700B31C71 /* .travis.yml */,
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */, 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */,
B082B267205C7D8000BC04DC /* Info.plist */, B082B267205C7D8000BC04DC /* Info.plist */,
36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */,
36300E9020A2D2B200B31C71 /* BackgroundColorTests.swift */,
); );
path = MTMRTests; path = MTMRTests;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -231,6 +235,7 @@
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */, 607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */,
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */, 6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */, 60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */,
60C44AFC20A373A100C0EC91 /* MusicBarItem.swift */,
); );
path = Widgets; path = Widgets;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -266,7 +271,6 @@
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
B082B263205C7D8000BC04DC /* PBXTargetDependency */,
); );
name = MTMRTests; name = MTMRTests;
productName = MTMRTests; productName = MTMRTests;
@@ -358,12 +362,14 @@
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 */,
60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */,
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */, B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */, 6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */, 607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */, 6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */, B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
60F7D454208CC31400ABF5D2 /* InputSourceBarItem.swift in Sources */, 60F7D454208CC31400ABF5D2 /* InputSourceBarItem.swift in Sources */,
36A778BE20A6C27100B38714 /* GeneralExtensions.swift in Sources */,
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */, 36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */,
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */, B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */,
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */, 36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */,
@@ -378,21 +384,16 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
36300E9120A2D2B200B31C71 /* BackgroundColorTests.swift in Sources */,
36300E8F20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift in Sources */,
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */, 36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */,
36300E83209F040900B31C71 /* SupportHelpers.swift in Sources */,
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */, 36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
B082B263205C7D8000BC04DC /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B082B24E205C7D8000BC04DC /* MTMR */;
targetProxy = B082B262205C7D8000BC04DC /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
B082B258205C7D8000BC04DC /* Main.storyboard */ = { B082B258205C7D8000BC04DC /* Main.storyboard */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
BuildableName = "MTMR.app"
BlueprintName = "MTMR"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B260205C7D8000BC04DC"
BuildableName = "MTMRTests.xctest"
BlueprintName = "MTMRTests"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
BuildableName = "MTMR.app"
BlueprintName = "MTMR"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
BuildableName = "MTMR.app"
BlueprintName = "MTMR"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
BuildableName = "MTMR.app"
BlueprintName = "MTMR"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B260205C7D8000BC04DC"
BuildableName = "MTMRTests.xctest"
BlueprintName = "MTMRTests"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+24
View File
@@ -41,6 +41,28 @@ class AppDelegate: NSObject, NSApplicationDelegate {
TouchBarController.shared.createAndUpdatePreset() TouchBarController.shared.createAndUpdatePreset()
} }
@objc func toggleControlStrip(_ sender: Any?) {
TouchBarController.shared.controlStripState = !TouchBarController.shared.controlStripState
createMenu()
TouchBarController.shared.resetControlStrip()
}
@objc func toggleBlackListedApp(_ sender: Any?) {
let appIdentifier = TouchBarController.shared.frontmostApplicationIdentifier
if appIdentifier != nil {
if let index = TouchBarController.shared.blacklistAppIdentifiers.index(of: appIdentifier!) {
TouchBarController.shared.blacklistAppIdentifiers.remove(at: index)
} else {
TouchBarController.shared.blacklistAppIdentifiers.append(appIdentifier!)
}
UserDefaults.standard.set(TouchBarController.shared.blacklistAppIdentifiers, forKey: "com.toxblh.mtmr.blackListedApps")
UserDefaults.standard.synchronize()
TouchBarController.shared.updateActiveApp()
}
}
@objc func openPreset(_ sender: Any?) { @objc func openPreset(_ sender: Any?) {
let dialog = NSOpenPanel(); let dialog = NSOpenPanel();
@@ -71,6 +93,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
menu.addItem(withTitle: "Preferences", action: #selector(openPrefereces(_:)), keyEquivalent: ",") menu.addItem(withTitle: "Preferences", action: #selector(openPrefereces(_:)), keyEquivalent: ",")
menu.addItem(withTitle: "Reload Preset", action: #selector(updatePreset(_:)), keyEquivalent: "r") menu.addItem(withTitle: "Reload Preset", action: #selector(updatePreset(_:)), keyEquivalent: "r")
menu.addItem(withTitle: "Open Preset", action: #selector(openPreset(_:)), keyEquivalent: "O") menu.addItem(withTitle: "Open Preset", action: #selector(openPreset(_:)), keyEquivalent: "O")
menu.addItem(withTitle: TouchBarController.shared.controlStripState ? "Hide Control Strip" : "Show Control Strip" , action: #selector(toggleControlStrip(_:)), keyEquivalent: "T")
menu.addItem(withTitle: "Toggle blackList current app" , action: #selector(toggleBlackListedApp(_:)), keyEquivalent: "B")
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")
statusItem.menu = menu statusItem.menu = menu
+16 -9
View File
@@ -5,24 +5,24 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
private let interval: TimeInterval private let interval: TimeInterval
private var forceHideConstraint: NSLayoutConstraint! private var forceHideConstraint: NSLayoutConstraint!
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, onTap: @escaping ()->(), onLongTap: @escaping ()->()) { init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval) {
self.interval = interval self.interval = interval
super.init(identifier: identifier, title: "⏳", onTap: onTap, onLongTap: onLongTap) super.init(identifier: identifier, title: "⏳")
self.forceHideConstraint = self.view.widthAnchor.constraint(equalToConstant: 0) self.forceHideConstraint = self.view.widthAnchor.constraint(equalToConstant: 0)
guard let script = source.appleScript else { guard let script = source.appleScript else {
button.title = "no script" self.title = "no script"
return return
} }
self.script = script self.script = script
button.bezelColor = .clear self.isBordered = false
DispatchQueue.main.async { DispatchQueue.appleScriptQueue.async {
var error: NSDictionary? var error: NSDictionary?
guard script.compileAndReturnError(&error) else { guard script.compileAndReturnError(&error) else {
#if DEBUG #if DEBUG
print(error?.description ?? "unknown error") print(error?.description ?? "unknown error")
#endif #endif
DispatchQueue.main.async { DispatchQueue.main.async {
self.button.title = "error" self.title = "error"
} }
return return
} }
@@ -36,14 +36,17 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
func refreshAndSchedule() { func refreshAndSchedule() {
#if DEBUG #if DEBUG
print("refresh happened") print("refresh happened (interval \(self.interval)), self \(self.identifier.rawValue))")
#endif #endif
let scriptResult = self.execute() let scriptResult = self.execute()
DispatchQueue.main.async { DispatchQueue.main.async {
self.button.title = scriptResult self.title = scriptResult
self.forceHideConstraint.isActive = scriptResult == "" self.forceHideConstraint.isActive = scriptResult == ""
#if DEBUG
print("did set new script result title \(scriptResult)")
#endif
} }
DispatchQueue.main.asyncAfter(deadline: .now() + self.interval) { [weak self] in DispatchQueue.appleScriptQueue.asyncAfter(deadline: .now() + self.interval) { [weak self] in
self?.refreshAndSchedule() self?.refreshAndSchedule()
} }
} }
@@ -66,3 +69,7 @@ extension SourceProtocol {
return NSAppleScript(source: source) return NSAppleScript(source: source)
} }
} }
extension DispatchQueue {
static let appleScriptQueue = DispatchQueue(label: "mtmr.applescript")
}
+1 -3
View File
@@ -10,8 +10,6 @@
@interface MediaKeys : NSObject @interface MediaKeys : NSObject
+ (void)decreaseVolume; + (void)HIDPostAuxKey:(UInt8)keyCode;
+ (void)increaseVolume;
+ (void)muteVolume;
@end @end
+4 -11
View File
@@ -7,6 +7,7 @@
// //
#import "TouchBarSupport.h" #import "TouchBarSupport.h"
#import <IOKit/hidsystem/ev_keymap.h>
@implementation MediaKeys @implementation MediaKeys
@@ -30,7 +31,7 @@ static io_connect_t get_event_driver(void)
} }
static void HIDPostAuxKey( const UInt8 auxKeyCode ) static void HIDReleaseAuxKey( const UInt8 auxKeyCode )
{ {
NXEventData event; NXEventData event;
kern_return_t kr; kern_return_t kr;
@@ -51,16 +52,8 @@ static void HIDPostAuxKey( const UInt8 auxKeyCode )
kr = IOHIDPostEvent( get_event_driver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE ); kr = IOHIDPostEvent( get_event_driver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE );
} }
+ (void)decreaseVolume { + (void)HIDPostAuxKey: (UInt8)keyCode {
HIDPostAuxKey(NX_KEYTYPE_SOUND_DOWN); HIDReleaseAuxKey(keyCode);
}
+ (void)increaseVolume {
HIDPostAuxKey(NX_KEYTYPE_SOUND_UP);
}
+ (void)muteVolume {
HIDPostAuxKey(NX_KEYTYPE_MUTE);
} }
@end @end
+119 -17
View File
@@ -9,22 +9,18 @@
import Cocoa import Cocoa
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate { class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
private let tapClosure: () -> ()? var tapClosure: (() -> ())?
private let longTapClosure: () -> ()? var longTapClosure: (() -> ())?
private(set) 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, onTap callback: @escaping () -> (), onLongTap callbackLong: @escaping () -> (), bezelColor: NSColor? = .clear) { init(identifier: NSTouchBarItem.Identifier, title: String) {
self.tapClosure = callback self.attributedTitle = title.defaultTouchbarAttributedString
self.longTapClosure = callbackLong
super.init(identifier: identifier) super.init(identifier: identifier)
button = NSButton(title: title, target: self, action: nil) button = CustomHeightButton(title: title, target: nil, action: nil)
button.bezelColor = bezelColor
button.title = title
self.view = button
longClick = NSPressGestureRecognizer(target: self, action: #selector(handleGestureLong)) longClick = NSPressGestureRecognizer(target: self, action: #selector(handleGestureLong))
longClick.allowedTouchTypes = .direct longClick.allowedTouchTypes = .direct
@@ -34,14 +30,71 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
singleClick.allowedTouchTypes = .direct singleClick.allowedTouchTypes = .direct
singleClick.delegate = self singleClick.delegate = self
self.view.addGestureRecognizer(longClick) reinstallButton()
self.view.addGestureRecognizer(singleClick) 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")
} }
var isBordered: Bool = true {
didSet {
reinstallButton()
}
}
var backgroundColor: NSColor? {
didSet {
reinstallButton()
}
}
var title: String {
get {
return self.attributedTitle.string
}
set {
self.attributedTitle = newValue.defaultTouchbarAttributedString
}
}
var attributedTitle: NSAttributedString {
didSet {
self.button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
self.button?.attributedTitle = attributedTitle
}
}
var image: NSImage? {
didSet {
button.image = image
}
}
private func reinstallButton() {
let title = button.attributedTitle
let image = button.image
let cell = CustomButtonCell(parentItem: self)
button.cell = cell
if let color = backgroundColor {
cell.isBordered = true
button.bezelColor = color
cell.backgroundColor = color
} else {
button.isBordered = isBordered
button.bezelStyle = isBordered ? .rounded : .inline
}
button.imageScaling = .scaleProportionallyDown
button.imageHugsTitle = true
button.attributedTitle = title
self.button?.imagePosition = title.length > 0 ? .imageLeading : .imageOnly
button.image = image
self.view = button
self.view.addGestureRecognizer(longClick)
self.view.addGestureRecognizer(singleClick)
}
func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool { func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool {
if gestureRecognizer == singleClick && otherGestureRecognizer == longClick { if gestureRecognizer == singleClick && otherGestureRecognizer == longClick {
@@ -55,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() self.tapClosure?()
break break
default: default:
break break
@@ -66,12 +119,12 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
let hf: HapticFeedback = HapticFeedback() let hf: HapticFeedback = HapticFeedback()
switch gr.state { switch gr.state {
case .began: case .began:
if self.longTapClosure != nil { if let closure = self.longTapClosure {
hf.tap(strong: 2) hf.tap(strong: 2)
self.tapClosure() closure()
} else { } else if let closure = self.tapClosure {
hf.tap(strong: 6) hf.tap(strong: 6)
self.longTapClosure() closure()
print("long click") print("long click")
} }
break break
@@ -82,3 +135,52 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
} }
} }
class CustomHeightButton : NSButton {
override var intrinsicContentSize: NSSize {
var size = super.intrinsicContentSize
size.height = 30
return size
}
}
class CustomButtonCell: NSButtonCell {
weak var parentItem: CustomButtonTouchBarItem?
init(parentItem: CustomButtonTouchBarItem) {
super.init(textCell: "")
self.parentItem = parentItem
}
override func highlight(_ flag: Bool, withFrame cellFrame: NSRect, in controlView: NSView) {
super.highlight(flag, withFrame: cellFrame, in: controlView)
if !self.isBordered {
if flag {
self.setAttributedTitle(self.attributedTitle, withColor: .lightGray)
} else if let parentItem = self.parentItem {
self.attributedTitle = parentItem.attributedTitle
}
}
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setAttributedTitle(_ title: NSAttributedString, withColor color: NSColor) {
let attrTitle = NSMutableAttributedString(attributedString: title)
attrTitle.addAttributes([.foregroundColor: color], range: NSRange(location: 0, length: attrTitle.length))
self.attributedTitle = attrTitle
}
}
extension String {
var defaultTouchbarAttributedString: NSAttributedString {
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))
return attrTitle
}
}
+8 -8
View File
@@ -49,19 +49,19 @@ class CustomSliderCell: NSSliderCell {
override func drawBar(inside aRect: NSRect, flipped: Bool) { override func drawBar(inside aRect: NSRect, flipped: Bool) {
_barRect = aRect _barRect = aRect
var rect = aRect
rect.size.height = CGFloat(4)
let barRadius = CGFloat(2) let barRadius = CGFloat(2)
let value = CGFloat((self.doubleValue - self.minValue) / (self.maxValue - self.minValue))
let finalWidth = CGFloat(value * (self.controlView!.frame.size.width - 12))
var leftRect = rect var bgRect = aRect
leftRect.size.width = finalWidth bgRect.size.height = CGFloat(4)
let bg = NSBezierPath(roundedRect: rect, xRadius: barRadius, yRadius: barRadius)
let bg = NSBezierPath(roundedRect: bgRect, xRadius: barRadius, yRadius: barRadius)
NSColor.lightGray.setFill() NSColor.lightGray.setFill()
bg.fill() bg.fill()
let active = NSBezierPath(roundedRect: leftRect, xRadius: barRadius, yRadius: barRadius) var activeRect = bgRect
activeRect.size.width = CGFloat((Double(bgRect.size.width) / (self.maxValue - self.minValue)) * self.doubleValue)
let active = NSBezierPath(roundedRect: activeRect, xRadius: barRadius, yRadius: barRadius)
NSColor.darkGray.setFill() NSColor.darkGray.setFill()
active.fill() active.fill()
} }
+19
View File
@@ -0,0 +1,19 @@
import Foundation
#if swift(>=4.1)
// compactMap supported
#else
extension Sequence {
func compactMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] {
return try flatMap(transform)
}
}
#endif
extension String {
var ifNotEmpty: String? {
return self.count > 0 ? self : nil
}
}
+6 -2
View File
@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.13</string> <string>0.15</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
@@ -26,8 +26,12 @@
<true/> <true/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright © 2018 Anton Palgunov. All rights reserved.</string> <string>Copyright © 2018 Anton Palgunov. All rights reserved.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Weather widget need your location for correct work</string>
<key>NSLocationUsageDescription</key> <key>NSLocationUsageDescription</key>
<string>...</string> <string>Weather widget need your location for correct work</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Weather widget need your location for correct work</string>
<key>NSMainStoryboardFile</key> <key>NSMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
+32 -61
View File
@@ -47,11 +47,11 @@ class SupportedTypesHolder {
"delete": { _ in return (item: .staticButton(title: "del"), action: .keyPress(keycode: 117), longAction: .none, parameters: [:])}, "delete": { _ in return (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: 113), 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: 107), longAction: .none, parameters: [.image: imageParameter]) return (item: .staticButton(title: ""), action: .keyPress(keycode: 145), longAction: .none, parameters: [.image: imageParameter])
}, },
"volumeDown": { _ in "volumeDown": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!) let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!)
@@ -125,6 +125,17 @@ class SupportedTypesHolder {
}, },
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), longAction: .none, parameters: [:]) }, "sleep": { _ in return (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 return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), longAction: .none, parameters: [:])},
"music": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval }
let container = try decoder.container(keyedBy: CodingKeys.self)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
return (
item: .music(interval: interval ?? 1800.00),
action: .none,
longAction: .none,
parameters: [:]
)
},
] ]
static let sharedInstance = SupportedTypesHolder() static let sharedInstance = SupportedTypesHolder()
@@ -157,6 +168,7 @@ enum ItemType: Decodable {
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)
case inputsource() case inputsource()
case music(interval: Double)
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case type case type
@@ -185,6 +197,7 @@ enum ItemType: Decodable {
case weather case weather
case currency case currency
case inputsource case inputsource
case music
} }
init(from decoder: Decoder) throws { init(from decoder: Decoder) throws {
@@ -223,13 +236,16 @@ enum ItemType: Decodable {
self = .currency(interval: interval, from: from, to: to) self = .currency(interval: interval, from: from, to: to)
case .inputsource: case .inputsource:
self = .inputsource() self = .inputsource()
case .music:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
self = .music(interval: interval)
} }
} }
} }
enum ActionType: Decodable { enum ActionType: Decodable {
case none case none
case hidKey(keycode: Int) case hidKey(keycode: Int32)
case keyPress(keycode: Int) case keyPress(keycode: Int)
case appleSctipt(source: SourceProtocol) case appleSctipt(source: SourceProtocol)
case shellScript(executable: String, parameters: [String]) case shellScript(executable: String, parameters: [String])
@@ -258,7 +274,7 @@ 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(Int.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)
@@ -282,7 +298,7 @@ enum ActionType: Decodable {
enum LongActionType: Decodable { enum LongActionType: Decodable {
case none case none
case hidKey(keycode: Int) case hidKey(keycode: Int32)
case keyPress(keycode: Int) case keyPress(keycode: Int)
case appleSctipt(source: SourceProtocol) case appleSctipt(source: SourceProtocol)
case shellScript(executable: String, parameters: [String]) case shellScript(executable: String, parameters: [String])
@@ -311,7 +327,7 @@ 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(Int.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)
@@ -332,62 +348,13 @@ enum LongActionType: Decodable {
} }
} }
extension ItemType: Equatable {}
func ==(lhs: ItemType, rhs: ItemType) -> Bool {
switch (lhs, rhs) {
case let (.staticButton(a), .staticButton(b)):
return a == b
case let (.appleScriptTitledButton(a, b), .appleScriptTitledButton(c, d)):
return a == c && b == d
default:
return false
}
}
extension ActionType: Equatable {}
func ==(lhs: ActionType, rhs: ActionType) -> Bool {
switch (lhs, rhs) {
case (.none, .none):
return true
case let (.hidKey(a), .hidKey(b)),
let (.keyPress(a), .keyPress(b)):
return a == b
case let (.appleSctipt(a), .appleSctipt(b)):
return a == b
case let (.shellScript(a, b), .shellScript(c, d)):
return a == c && b == d
case let (.openUrl(a), .openUrl(b)):
return a == b
default:
return false
}
}
extension LongActionType: Equatable {}
func ==(lhs: LongActionType, rhs: LongActionType) -> Bool {
switch (lhs, rhs) {
case (.none, .none):
return true
case let (.hidKey(a), .hidKey(b)),
let (.keyPress(a), .keyPress(b)):
return a == b
case let (.appleSctipt(a), .appleSctipt(b)):
return a == b
case let (.shellScript(a, b), .shellScript(c, d)):
return a == c && b == d
case let (.openUrl(a), .openUrl(b)):
return a == b
default:
return false
}
}
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 background(_:NSColor)
} }
struct GeneralParameters: Decodable { struct GeneralParameters: Decodable {
@@ -397,6 +364,8 @@ struct GeneralParameters: Decodable {
case width case width
case image case image
case align case align
case bordered
case background
} }
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)
@@ -409,6 +378,12 @@ struct GeneralParameters: Decodable {
} }
let align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center let align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center
result[.align] = .align(align) result[.align] = .align(align)
if let borderedFlag = try container.decodeIfPresent(Bool.self, forKey: .bordered) {
result[.bordered] = .bordered(borderedFlag)
}
if let backgroundColor = try container.decodeIfPresent(String.self, forKey: .background)?.hexColor {
result[.background] = .background(backgroundColor)
}
parameters = result parameters = result
} }
} }
@@ -458,10 +433,6 @@ extension NSImage: SourceProtocol {
return self return self
} }
} }
extension SourceProtocol where Self: Equatable {}
func ==(left: SourceProtocol, right: SourceProtocol) -> Bool {
return left.data == right.data
}
extension String { extension String {
var base64Data: Data? { var base64Data: Data? {
+3 -41
View File
@@ -29,45 +29,7 @@ extension KeyPress {
} }
} }
func doKey(_ key: Int, down: Bool) { func HIDPostAuxKey(_ key: Int32) {
let flags = NSEvent.ModifierFlags(rawValue: down ? 0xa00 : 0xb00) let key = UInt8(key)
let data1 = (key << 16) | ((down ? 0xa : 0xb) << 8) MediaKeys.hidPostAuxKey(key)
let ev = NSEvent.otherEvent(
with: NSEvent.EventType.systemDefined,
location: NSPoint(x:0.0, y:0.0),
modifierFlags: flags,
timestamp: TimeInterval(0),
windowNumber: 0,
context: nil,
// context: 0,
subtype: 8,
data1: data1,
data2: -1
)
let cev = ev!.cgEvent!
cev.post(tap: CGEventTapLocation(rawValue: 0)!)
} }
func HIDPostAuxKey(_ key: Int) {
doKey(key, down: true)
doKey(key, down: false)
}
// hidsystem/ev_keymap.h
let NX_KEYTYPE_SOUND_UP = 0
let NX_KEYTYPE_SOUND_DOWN = 1
let NX_KEYTYPE_MUTE = 7
let NX_KEYTYPE_BRIGHTNESS_UP = 2
let NX_KEYTYPE_BRIGHTNESS_DOWN = 3
let NX_KEYTYPE_PLAY = 16
let NX_KEYTYPE_NEXT = 17
let NX_KEYTYPE_PREVIOUS = 18
+19
View File
@@ -7,6 +7,7 @@
// //
import Foundation import Foundation
import AppKit
extension String { extension String {
func trim() -> String { func trim() -> String {
@@ -17,6 +18,24 @@ extension String {
// ((\s|,)\/\*[\s\S]*?\*\/)|(( |, ")\/\/.*) // ((\s|,)\/\*[\s\S]*?\*\/)|(( |, ")\/\/.*)
return self.replacingOccurrences(of: "((\\s|,)\\/\\*[\\s\\S]*?\\*\\/)|(( |, \\\")\\/\\/.*)", with: "", options: .regularExpression) return self.replacingOccurrences(of: "((\\s|,)\\/\\*[\\s\\S]*?\\*\\/)|(( |, \\\")\\/\\/.*)", with: "", options: .regularExpression)
} }
var hexColor: NSColor? {
let hex = trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int = UInt32()
Scanner(string: hex).scanHexInt32(&int)
let a, r, g, b: UInt32
switch hex.count {
case 3: // RGB (12-bit)
(r, g, b, a) = ((int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17, 255)
case 6: // RGB (24-bit)
(r, g, b, a) = (int >> 16, int >> 8 & 0xFF, int & 0xFF, 255)
case 8: // ARGB (32-bit)
(r, g, b, a) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
return nil
}
return NSColor(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
}
} }
extension NSImage { extension NSImage {
+93 -27
View File
@@ -37,6 +37,8 @@ extension ItemType {
return "com.toxblh.mtmr.currency" return "com.toxblh.mtmr.currency"
case .inputsource(): case .inputsource():
return "com.toxblh.mtmr.inputsource." return "com.toxblh.mtmr.inputsource."
case .music(interval: _):
return "com.toxblh.mtmr.music."
} }
} }
@@ -50,7 +52,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
static let shared = TouchBarController() static let shared = TouchBarController()
let touchBar = NSTouchBar() var touchBar: NSTouchBar!
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:] var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:] var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
@@ -61,14 +63,43 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
var scrollArea: NSCustomTouchBarItem? var scrollArea: NSCustomTouchBarItem?
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString)) var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
var controlStripState: Bool {
get {
return UserDefaults.standard.bool(forKey: "com.toxblh.mtmr.settings.showControlStrip")
}
set {
UserDefaults.standard.set(!controlStripState, forKey: "com.toxblh.mtmr.settings.showControlStrip")
}
}
var blacklistAppIdentifiers: [String] = []
var frontmostApplicationIdentifier: String? {
get {
guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil }
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)
if let blackListed = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.blackListedApps") {
self.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.didTerminateApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didActivateApplicationNotification, object: nil)
createAndUpdatePreset() createAndUpdatePreset()
} }
func createAndUpdatePreset(jsonItems: [BarItemDefinition]? = nil) { func createAndUpdatePreset(jsonItems: [BarItemDefinition]? = nil) {
if let oldBar = self.touchBar {
NSTouchBar.minimizeSystemModalFunctionBar(oldBar)
}
self.touchBar = NSTouchBar()
var jsonItems = jsonItems var jsonItems = jsonItems
self.itemDefinitions = [:] self.itemDefinitions = [:]
self.items = [:] self.items = [:]
@@ -92,7 +123,20 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
touchBar.delegate = self touchBar.delegate = self
touchBar.defaultItemIdentifiers = [] touchBar.defaultItemIdentifiers = []
touchBar.defaultItemIdentifiers = self.leftIdentifiers + [centerScrollArea] + self.rightIdentifiers touchBar.defaultItemIdentifiers = self.leftIdentifiers + [centerScrollArea] + self.rightIdentifiers
self.presentTouchBar()
self.updateActiveApp()
}
@objc func activeApplicationChanged(_ n: Notification) {
updateActiveApp()
}
func updateActiveApp() {
if self.blacklistAppIdentifiers.index(of: self.frontmostApplicationIdentifier!) != nil {
DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, false)
} else {
presentTouchBar()
}
} }
func readConfig() -> [BarItemDefinition]? { func readConfig() -> [BarItemDefinition]? {
@@ -110,8 +154,11 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
} }
func loadItemDefinitions(jsonItems: [BarItemDefinition]) { func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH-mm-ss"
let time = dateFormatter.string(from: Date())
for item in jsonItems { for item in jsonItems {
let identifierString = item.type.identifierBase.appending(UUID().uuidString) let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString)
let identifier = NSTouchBarItem.Identifier(identifierString) let identifier = NSTouchBarItem.Identifier(identifierString)
itemDefinitions[identifier] = item itemDefinitions[identifier] = item
if item.align == .left { if item.align == .left {
@@ -135,7 +182,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
@objc func setupControlStripPresence() { @objc func setupControlStripPresence() {
DFRSystemModalShowsCloseBoxWhenFrontMost(false) DFRSystemModalShowsCloseBoxWhenFrontMost(false)
let item = NSCustomTouchBarItem(identifier: .controlStripItem) let item = NSCustomTouchBarItem(identifier: .controlStripItem)
item.view = NSButton(image: #imageLiteral(resourceName: "Strip"), target: self, action: #selector(presentTouchBar)) item.view = NSButton(image: #imageLiteral(resourceName: "StatusImage"), target: self, action: #selector(presentTouchBar))
NSTouchBarItem.addSystemTrayItem(item) NSTouchBarItem.addSystemTrayItem(item)
DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, true) DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, true)
} }
@@ -145,7 +192,16 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
} }
@objc private func presentTouchBar() { @objc private func presentTouchBar() {
NSTouchBar.presentSystemModalFunctionBar(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem) if self.controlStripState {
NSTouchBar.presentSystemModalFunctionBar(touchBar, systemTrayItemIdentifier: .controlStripItem)
} else {
NSTouchBar.presentSystemModalFunctionBar(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
}
}
@objc func resetControlStrip() {
NSTouchBar.minimizeSystemModalFunctionBar(self.touchBar)
presentTouchBar()
} }
@objc private func dismissTouchBar() { @objc private func dismissTouchBar() {
@@ -166,19 +222,17 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
} }
func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? { func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? {
let action = self.action(forItem: item)
let longAction = self.longAction(forItem: item)
var barItem: NSTouchBarItem! var barItem: NSTouchBarItem!
switch item.type { switch item.type {
case .staticButton(title: let title): case .staticButton(title: let title):
barItem = CustomButtonTouchBarItem(identifier: identifier, title: title, onTap: action, onLongTap: longAction, bezelColor: NSColor.controlColor) barItem = CustomButtonTouchBarItem(identifier: identifier, title: title)
case .appleScriptTitledButton(source: let source, refreshInterval: let interval): case .appleScriptTitledButton(source: let source, refreshInterval: let interval):
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action, onLongTap: longAction) barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval)
case .timeButton(formatTemplate: let template): case .timeButton(formatTemplate: let template):
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, onTap: action, onLongTap: longAction) barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template)
case .battery(): case .battery():
barItem = BatteryBarItem(identifier: identifier, onTap: action, onLongTap: longAction) barItem = BatteryBarItem(identifier: identifier)
case .dock: case .dock:
barItem = AppScrubberTouchBarItem(identifier: identifier) barItem = AppScrubberTouchBarItem(identifier: identifier)
case .volume: case .volume:
@@ -194,27 +248,37 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
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 .weather(interval: let interval, units: let units, api_key: let api_key, icon_type: let icon_type):
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type, onTap: action, onLongTap: longAction) 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 .currency(interval: let interval, from: let from, to: let to):
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, onTap: action, onLongTap: longAction) barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to)
case .inputsource(): case .inputsource():
barItem = InputSourceBarItem(identifier: identifier, onTap: action, onLongTap: longAction) barItem = InputSourceBarItem(identifier: identifier)
case .music(interval: let interval):
barItem = MusicBarItem(identifier: identifier, interval: interval)
} }
if let action = self.action(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
item.tapClosure = action
}
if let longAction = self.longAction(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
item.longTapClosure = longAction
}
if case .bordered(let bordered)? = item.additionalParameters[.bordered], let item = barItem as? CustomButtonTouchBarItem {
item.isBordered = bordered
}
if case .background(let color)? = item.additionalParameters[.background], let item = barItem as? CustomButtonTouchBarItem {
item.backgroundColor = color
}
if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth { if case .width(let 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 .image(let source)? = item.additionalParameters[.image], let item = barItem as? CustomButtonTouchBarItem {
let button = item.button! item.image = source.image
button.image = source.image
button.imagePosition = .imageLeading
button.imageHugsTitle = true
button.bezelColor = .clear
} }
return barItem return barItem
} }
func action(forItem item: BarItemDefinition) -> ()->() { func action(forItem item: BarItemDefinition) -> (()->())? {
switch item.action { switch item.action {
case .hidKey(keycode: let keycode): case .hidKey(keycode: let keycode):
return { HIDPostAuxKey(keycode) } return { HIDPostAuxKey(keycode) }
@@ -226,10 +290,12 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
return {} return {}
} }
return { return {
var error: NSDictionary? DispatchQueue.appleScriptQueue.async {
appleScript.executeAndReturnError(&error) var error: NSDictionary?
if let error = error { appleScript.executeAndReturnError(&error)
print("error \(error) when handling \(item) ") if let error = error {
print("error \(error) when handling \(item) ")
}
} }
} }
case .shellScript(executable: let executable, parameters: let parameters): case .shellScript(executable: let executable, parameters: let parameters):
@@ -252,12 +318,12 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
case .custom(closure: let closure): case .custom(closure: let closure):
return closure return closure
case .none: case .none:
return {} return nil
} }
} }
func longAction(forItem item: BarItemDefinition) -> ()->() { func longAction(forItem item: BarItemDefinition) -> (()->())? {
switch item.longAction { switch item.longAction {
case .hidKey(keycode: let keycode): case .hidKey(keycode: let keycode):
return { HIDPostAuxKey(keycode) } return { HIDPostAuxKey(keycode) }
@@ -295,7 +361,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
case .custom(closure: let closure): case .custom(closure: let closure):
return closure return closure
case .none: case .none:
return {} return nil
} }
} }
} }
+53 -23
View File
@@ -13,6 +13,12 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
private let hf: HapticFeedback = HapticFeedback() private let hf: HapticFeedback = HapticFeedback()
private var timer: Timer!
private var ticks: Int = 0
private let minTicks: Int = 5
private let maxTicks: Int = 20
private var lastSelected: Int = 0
private var persistentAppIdentifiers: [String] = [] private var persistentAppIdentifiers: [String] = []
private var runningAppsIdentifiers: [String] = [] private var runningAppsIdentifiers: [String] = []
@@ -25,8 +31,6 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
private var applications: [DockItem] = [] private var applications: [DockItem] = []
private var timeAtPress: NSDate?
override init(identifier: NSTouchBarItem.Identifier) { override init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier) super.init(identifier: identifier)
@@ -35,7 +39,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
scrubber.dataSource = self scrubber.dataSource = self
scrubber.mode = .free // .fixed scrubber.mode = .free // .fixed
let layout = NSScrubberFlowLayout(); let layout = NSScrubberFlowLayout();
layout.itemSize = NSSize(width: 44, height: 30) layout.itemSize = NSSize(width: 36, height: 32)
layout.itemSpacing = 2 layout.itemSpacing = 2
scrubber.scrubberLayout = layout scrubber.scrubberLayout = layout
scrubber.selectionBackgroundStyle = .roundedBackground scrubber.selectionBackgroundStyle = .roundedBackground
@@ -94,6 +98,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
if let icon = app.icon { if let icon = app.icon {
item.image = icon item.image = icon
item.image.size = NSSize(width: 26, height: 26)
} }
item.removeFromSuperview() item.removeFromSuperview()
@@ -105,59 +110,82 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
} else { } else {
dotView.layer?.backgroundColor = NSColor.black.cgColor dotView.layer?.backgroundColor = NSColor.black.cgColor
} }
dotView.layer?.cornerRadius = 2 dotView.layer?.cornerRadius = 1.5
dotView.setFrameOrigin(NSPoint(x: 20, y: 0)) dotView.setFrameOrigin(NSPoint(x: 17, y: 1))
dotView.frame.size = NSSize(width: 4, height: 4) dotView.frame.size = NSSize(width: 3, height: 3)
item.addSubview(dotView) item.addSubview(dotView)
return item return item
} }
public func didBeginInteracting(with scrubber: NSScrubber) { public func didBeginInteracting(with scrubber: NSScrubber) {
timeAtPress = NSDate() stopTimer()
self.ticks = 0
self.timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(checkTimer), userInfo: nil, repeats: true)
}
@objc private func checkTimer() {
self.ticks += 1
if (self.ticks == minTicks) {
hf.tap(strong: 2)
}
if (self.ticks > maxTicks) {
stopTimer()
hf.tap(strong: 6)
}
}
private func stopTimer() {
self.timer?.invalidate()
self.timer = nil
self.lastSelected = 0
} }
public func didCancelInteracting(with scrubber: NSScrubber) { public func didCancelInteracting(with scrubber: NSScrubber) {
timeAtPress = nil stopTimer()
} }
public func didFinishInteracting(with scrubber: NSScrubber) { public func didFinishInteracting(with scrubber: NSScrubber) {
let timePressed = NSDate().timeIntervalSince(timeAtPress! as Date) stopTimer()
if (timePressed > 0.5 && scrubber.selectedIndex > 0) {
self.longPress(with: scrubber) if (ticks == 0) {
return return
} }
hf.tap(strong: 6) if (self.ticks >= minTicks && scrubber.selectedIndex > 0) {
self.longPress(selected: scrubber.selectedIndex)
return
}
let bundleIdentifier = applications[scrubber.selectedIndex].bundleIdentifier let bundleIdentifier = applications[scrubber.selectedIndex].bundleIdentifier
if bundleIdentifier!.contains("file://") { if bundleIdentifier!.contains("file://") {
NSWorkspace.shared.openFile(bundleIdentifier!.replacingOccurrences(of: "file://", with: "")) NSWorkspace.shared.openFile(bundleIdentifier!.replacingOccurrences(of: "file://", with: ""))
} else { } else {
NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIdentifier!, options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil) NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIdentifier!, options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil)
hf.tap(strong: 6)
} }
updateRunningApplication()
// NB: if you can't open app which on another space, try to check mark // NB: if you can't open app which on another space, try to check mark
// "When switching to an application, switch to a Space with open windows for the application" // "When switching to an application, switch to a Space with open windows for the application"
// in Mission control settings // in Mission control settings
} }
private func longPress(with scrubber: NSScrubber) { private func longPress(selected: Int) {
let timePressed = NSDate().timeIntervalSince(timeAtPress! as Date) let bundleIdentifier = applications[selected].bundleIdentifier
let bundleIdentifier = applications[scrubber.selectedIndex].bundleIdentifier if (self.ticks > maxTicks) {
if let processIdentifier = applications[selected].pid {
if (timePressed > 2.0) { if !(NSRunningApplication(processIdentifier: processIdentifier)?.terminate())! {
if let processIdentifier = applications[scrubber.selectedIndex].pid { NSRunningApplication(processIdentifier: processIdentifier)?.forceTerminate()
hf.tap(strong: 6) }
NSRunningApplication(processIdentifier: processIdentifier)?.forceTerminate()
} }
} 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) self.persistentAppIdentifiers.remove(at: index)
updateRunningApplication()
} else { } else {
self.persistentAppIdentifiers.append(bundleIdentifier!) self.persistentAppIdentifiers.append(bundleIdentifier!)
} }
@@ -165,6 +193,8 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
UserDefaults.standard.set(self.persistentAppIdentifiers, forKey: "com.toxblh.mtmr.dock.persistent") UserDefaults.standard.set(self.persistentAppIdentifiers, forKey: "com.toxblh.mtmr.dock.persistent")
UserDefaults.standard.synchronize() UserDefaults.standard.synchronize()
} }
self.ticks = 0
updateRunningApplication()
} }
private func launchedApplications() -> [DockItem] { private func launchedApplications() -> [DockItem] {
@@ -201,7 +231,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
public func getDockPersistentAppsList() -> [DockItem] { public func getDockPersistentAppsList() -> [DockItem] {
var returnable: [DockItem] = [] var returnable: [DockItem] = []
for (index, bundleIdentifier) in persistentAppIdentifiers.enumerated() { for bundleIdentifier in persistentAppIdentifiers {
if !self.runningAppsIdentifiers.contains(bundleIdentifier) { if !self.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)
+41 -29
View File
@@ -10,20 +10,28 @@ import IOKit.ps
import Foundation import Foundation
class BatteryBarItem: CustomButtonTouchBarItem { class BatteryBarItem: CustomButtonTouchBarItem {
private var timer: Timer! private let batteryInfo = BatteryInfo()
init(identifier: NSTouchBarItem.Identifier, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) { init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: " ", onTap: onTap, onLongTap: onLongTap) super.init(identifier: identifier, title: " ")
self.view = button
let batteryInfo = BatteryInfo(button: button) batteryInfo.start { [weak self] in
batteryInfo.start() self?.refresh()
batteryInfo.updateInfo() }
self.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() {
self.attributedTitle = self.batteryInfo.formattedInfo()
}
deinit {
batteryInfo.stop()
}
} }
class BatteryInfo: NSObject { class BatteryInfo: NSObject {
@@ -34,18 +42,11 @@ 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 button: NSButton?
var loop:CFRunLoopSource? var loop:CFRunLoopSource?
init(button: NSButton) { func start(notifyBlock: @escaping ()->()) {
super.init() self.notifyBlock = notifyBlock
self.button = button
self.start()
}
func start() {
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
@@ -54,16 +55,17 @@ class BatteryInfo: NSObject {
} }
let watcher = Unmanaged<BatteryInfo>.fromOpaque(ctx).takeUnretainedValue() let watcher = Unmanaged<BatteryInfo>.fromOpaque(ctx).takeUnretainedValue()
watcher.updateInfo() watcher.notifyBlock()
}, context).takeRetainedValue() as CFRunLoopSource }, context).takeRetainedValue() as CFRunLoopSource
CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode) CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
} }
func stop() { func stop() {
if !(self.loop != nil) { self.notifyBlock = {}
guard let loop = self.loop else {
return return
} }
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), self.loop, CFRunLoopMode.defaultMode) CFRunLoopRemoveSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
self.loop = nil self.loop = nil
} }
@@ -108,28 +110,38 @@ class BatteryInfo: NSObject {
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
} else if (time == 0) {
return ""
} }
return " (?)" return ""
} }
public func updateInfo() { public func formattedInfo() -> NSAttributedString {
var title = "" var title = ""
self.getPSInfo() self.getPSInfo()
if ACPower == "AC Power" { if ACPower == "AC Power" {
title += "⚡️" if current < 100 {
title += "⚡️"
}
timeRemaining = getFormattedTime(time: timeToFull) timeRemaining = getFormattedTime(time: timeToFull)
} else { } else {
timeRemaining = getFormattedTime(time: timeToEmpty) timeRemaining = getFormattedTime(time: timeToEmpty)
} }
title += String(current) + "%" + timeRemaining title += String(current) + "%"
button?.title = title
var color = NSColor.white
if current <= 10 && ACPower != "AC Power" {
color = NSColor.red
}
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: NSFont.systemFont(ofSize: 15), .baselineOffset: 1])
let newTitleSecond = NSMutableAttributedString(string: timeRemaining as String, attributes: [NSAttributedStringKey.foregroundColor: color, NSAttributedStringKey.font: NSFont.systemFont(ofSize: 8, weight: .regular), NSAttributedStringKey.baselineOffset: 7])
newTitle.append(newTitleSecond)
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
return newTitle
} }
} }
+9 -14
View File
@@ -10,7 +10,7 @@ import Cocoa
import CoreLocation import CoreLocation
class CurrencyBarItem: CustomButtonTouchBarItem { class CurrencyBarItem: CustomButtonTouchBarItem {
private let activity = NSBackgroundActivityScheduler(identifier: "com.toxblh.mtmr.currency.updatecheck") private let activity: NSBackgroundActivityScheduler
private var prefix: String private var prefix: String
private var from: String private var from: String
private var to: String private var to: String
@@ -36,7 +36,8 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
"ETH": "Ξ", "ETH": "Ξ",
] ]
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) { init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String) {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval activity.interval = interval
self.from = from self.from = from
self.to = to self.to = to
@@ -47,9 +48,7 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
self.prefix = from self.prefix = from
} }
super.init(identifier: identifier, title: "⏳", onTap: onTap, onLongTap: onLongTap) super.init(identifier: identifier, title: "⏳")
self.view = button
activity.repeats = true activity.repeats = true
activity.qualityOfService = .utility activity.qualityOfService = .utility
@@ -71,7 +70,6 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
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)
var value: Float32! var value: Float32!
if let data_array = json["data"] as? [String : AnyObject] { if let data_array = json["data"] as? [String : AnyObject] {
@@ -107,14 +105,11 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
} }
self.oldValue = value self.oldValue = value
button.title = String(format: "%@%.2f", self.prefix, value) let title = String(format: "%@%.2f", self.prefix, value)
let textRange = NSRange(location: 0, length: button.title.count) let regularFont = self.attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
let newTitle = NSMutableAttributedString(string: button.title) let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont])
newTitle.addAttribute(NSAttributedStringKey.foregroundColor, value: color, range: textRange) newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
newTitle.addAttribute(NSAttributedStringKey.font, value: button.font!, range: textRange) self.attributedTitle = newTitle
newTitle.setAlignment(.center, range: textRange)
button.attributedTitle = newTitle
} }
} }
+19 -14
View File
@@ -11,40 +11,45 @@ import Cocoa
class InputSourceBarItem: CustomButtonTouchBarItem { class InputSourceBarItem: CustomButtonTouchBarItem {
fileprivate var notificationCenter: CFNotificationCenter fileprivate var notificationCenter: CFNotificationCenter
let buttonSize = NSSize(width: 21, height: 21)
init(identifier: NSTouchBarItem.Identifier, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) { init(identifier: NSTouchBarItem.Identifier) {
notificationCenter = CFNotificationCenterGetDistributedCenter(); notificationCenter = CFNotificationCenterGetDistributedCenter();
super.init(identifier: identifier, title: "⏳", onTap: onTap, onLongTap: onLongTap) super.init(identifier: identifier, title: "⏳")
observeIputSourceChangedNotification(); observeIputSourceChangedNotification();
textInputSourceDidChange() textInputSourceDidChange()
self.tapClosure = { [weak self] in
self.button.action = #selector(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 {
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? = nil
if let imageURL = currentSource.iconImageURL { if let imageURL = currentSource.iconImageURL,
if let image = NSImage(contentsOf: imageURL) { let image = NSImage(contentsOf: imageURL) {
iconImage = image iconImage = image
} } else if let iconRef = currentSource.iconRef {
}
if iconImage == nil, let iconRef = currentSource.iconRef {
iconImage = NSImage(iconRef: iconRef) iconImage = NSImage(iconRef: iconRef)
} }
if (iconImage != nil) { if let iconImage = iconImage {
self.button.image = iconImage iconImage.size = buttonSize
self.image = iconImage
self.title = ""
} else { } else {
self.button.title = currentSource.name self.title = currentSource.name
} }
} }
+374
View File
@@ -0,0 +1,374 @@
//
// MusicBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 05.05.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
import ScriptingBridge
class MusicBarItem: CustomButtonTouchBarItem {
private let interval: TimeInterval
private var songTitle: String?
private var timer: Timer?
let buttonSize = NSSize(width: 21, height: 21)
let playerBundleIdentifiers = [
"com.apple.iTunes",
"com.spotify.client",
"com.coppertino.Vox",
"com.google.Chrome",
"com.apple.Safari"
]
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
self.interval = interval
super.init(identifier: identifier, title: "⏳")
self.isBordered = false
self.tapClosure = { [weak self] in self?.playPause() }
self.longTapClosure = { [weak self] in self?.nextTrack() }
DispatchQueue.main.async {
self.updatePlayer()
}
}
@objc func marquee(){
let str = self.title
if (str.count > 10) {
let indexFirst = str.index(str.startIndex, offsetBy: 0)
let indexSecond = str.index(str.startIndex, offsetBy: 1)
self.title = String(str.suffix(from: indexSecond)) + String(str[indexFirst])
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func playPause() {
for ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
if (musicPlayer.isRunning) {
if (musicPlayer.className == "SpotifyApplication") {
let mp = (musicPlayer as SpotifyApplication)
mp.playpause!()
return
} else if (musicPlayer.className == "ITunesApplication") {
let mp = (musicPlayer as iTunesApplication)
mp.playpause!()
return
} else if (musicPlayer.className == "VOXApplication") {
let mp = (musicPlayer as VoxApplication)
mp.playpause!()
return
} else if (musicPlayer.className == "SafariApplication") {
// 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 safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
for window in safariWindows! {
for tab in window.tabs!() {
let tab = tab as! SafariTab
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_play')[0].click()", in: tab)
return
} 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)
return
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
_ = safariApplication.doJavaScript!("document.getElementById('movie_player').click()", in: tab)
return
}
}
}
}
// else if (musicPlayer.className == "GoogleChromeApplication") {
// let chromeApplication = musicPlayer as GoogleChromeApplication
// let chromeWindows = chromeApplication.windows?().compactMap({ $0 as? GoogleChromeWindow })
// for window in chromeWindows! {
// for tab in window.tabs!() {
// let tab = tab as! GoogleChromeTab
// if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
// chromeApplication.executeJavaScript!(javascript: "document.getElementsByClassName('player-controls__btn_play')[0].click()")
// break
// } else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) {
// chromeApplication.executeJavaScript!(javascript: "document.getElementsByClassName('audio_page_player_ctrl')[0].click()")
// break
// } else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
// chromeApplication.executeJavaScript!(javascript: "alert(document.title)") // , id: tab
// break // document.getElementById('movie_player').click()
// }
// }
// }
// }
break
}
}
}
}
@objc func nextTrack() {
for ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
if (musicPlayer.isRunning) {
if (musicPlayer.className == "SpotifyApplication") {
let mp = (musicPlayer as SpotifyApplication)
mp.nextTrack!()
return
} else if (musicPlayer.className == "ITunesApplication") {
let mp = (musicPlayer as iTunesApplication)
mp.nextTrack!()
return
} else if (musicPlayer.className == "VOXApplication") {
let mp = (musicPlayer as VoxApplication)
mp.next!()
return
} else if (musicPlayer.className == "SafariApplication") {
// 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 safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
for window in safariWindows! {
for tab in window.tabs!() {
let tab = tab as! SafariTab
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_next')[0].click()", in: tab)
return
} 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)
return
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('ytp-next-button')[0].click()", in: tab)
return
}
}
}
}
}
}
}
}
func updatePlayer() {
var iconUpdated = false
var titleUpdated = false
for var ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
if (musicPlayer.isRunning) {
var tempTitle = ""
if (musicPlayer.className == "SpotifyApplication") {
tempTitle = (musicPlayer as SpotifyApplication).title
} else if (musicPlayer.className == "ITunesApplication") {
tempTitle = (musicPlayer as iTunesApplication).title
} else if (musicPlayer.className == "VOXApplication") {
tempTitle = (musicPlayer as VoxApplication).title
} else if (musicPlayer.className == "SafariApplication") {
let safariApplication = musicPlayer as SafariApplication
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
for window in safariWindows! {
for tab in window.tabs!() {
let tab = tab as! SafariTab
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
// if (!(tab.name?.hasSuffix("на Яндекс.Музыке"))!) {
tempTitle = (tab.name)!
break
// }
} else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) {
tempTitle = (tab.name)!
break
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
tempTitle = (tab.name)!
break
}
}
}
if tempTitle == "" {
ident = ""
}
} else if (musicPlayer.className == "GoogleChromeApplication") {
let chromeApplication = musicPlayer as GoogleChromeApplication
let chromeWindows = chromeApplication.windows?().compactMap({ $0 as? GoogleChromeWindow })
for window in chromeWindows! {
for tab in window.tabs!() {
let tab = tab as! GoogleChromeTab
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
if (!(tab.title?.hasSuffix("на Яндекс.Музыке"))!) {
tempTitle = tab.title!
break
}
} else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) {
tempTitle = tab.title!
break
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
tempTitle = tab.title!
break
}
}
}
if tempTitle == "" {
ident = ""
}
}
if (tempTitle == self.songTitle) {
DispatchQueue.main.asyncAfter(deadline: .now() + self.interval) { [weak self] in
self?.updatePlayer()
}
return
} else {
self.songTitle = tempTitle
}
if let songTitle = self.songTitle?.ifNotEmpty {
self.title = " " + songTitle + " "
titleUpdated = true
self.timer?.invalidate()
self.timer = nil
self.timer = Timer.scheduledTimer(timeInterval: 0.25, target: self, selector: #selector(self.marquee), userInfo: nil, repeats: true)
}
if let ident = ident.ifNotEmpty,
let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: ident) {
let image = NSWorkspace.shared.icon(forFile: appPath)
image.size = self.buttonSize
self.image = image
iconUpdated = true
}
break
}
}
}
DispatchQueue.main.async {
if !iconUpdated {
self.image = nil
}
if !titleUpdated {
self.title = ""
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + self.interval) { [weak self] in
self?.updatePlayer()
}
}
}
@objc protocol SpotifyApplication {
@objc optional var currentTrack: SpotifyTrack {get}
@objc optional func nextTrack()
@objc optional func previousTrack()
@objc optional func playpause()
}
extension SBApplication: SpotifyApplication{}
@objc protocol SpotifyTrack {
@objc optional var artist: String {get}
@objc optional var name: String {get}
}
extension SBObject: SpotifyTrack{}
extension SpotifyApplication {
var title: String {
guard let t = currentTrack else { return "" }
return (t.artist ?? "") + " — " + (t.name ?? "")
}
}
@objc protocol iTunesApplication {
@objc optional var currentTrack: iTunesTrack {get}
@objc optional func playpause()
@objc optional func nextTrack()
@objc optional func previousTrack()
}
extension SBApplication: iTunesApplication{}
@objc protocol iTunesTrack {
@objc optional var artist: String {get}
@objc optional var name: String {get}
}
extension SBObject: iTunesTrack{}
extension iTunesApplication {
var title: String {
guard let t = currentTrack else { return "" }
return (t.artist ?? "") + " — " + (t.name ?? "")
}
}
@objc protocol VoxApplication {
@objc optional func playpause()
@objc optional func next()
@objc optional func previous()
@objc optional var track: String {get}
@objc optional var artist: String {get}
}
extension SBApplication: VoxApplication{}
extension VoxApplication {
var title: String {
return (artist ?? "") + " — " + (track ?? "")
}
}
@objc public protocol SBObjectProtocol: NSObjectProtocol {
func get() -> Any!
}
@objc public protocol SBApplicationProtocol: SBObjectProtocol {
func activate()
var delegate: SBApplicationDelegate! { get set }
}
@objc public protocol SafariApplication: SBApplicationProtocol {
@objc optional func windows() -> SBElementArray
@objc optional func doJavaScript(_ x: String!, in in_: Any!) -> Any // Applies a string of JavaScript code to a document.
}
extension SBApplication: SafariApplication {}
@objc public protocol SafariWindow: SBObjectProtocol {
@objc optional var name: String { get } // The title of the window.
@objc optional func tabs() -> SBElementArray
// @objc optional var document: SafariDocument { get } // The document whose contents are displayed in the window.
// @objc optional func setCurrentTab(_ currentTab: SafariTab!) // The current tab.
}
extension SBObject: SafariWindow {}
//@objc public protocol SafariDocument: SBObjectProtocol {
// @objc optional var name: String { get } // Its name.
// @objc optional var URL: String { get } // The current URL of the document.
//}
//extension SBObject: SafariDocument {}
@objc public protocol SafariTab: SBObjectProtocol {
@objc optional var URL: String { get } // The current URL of the tab.
@objc optional var name: String { get } // The name of the tab.
}
extension SBObject: SafariTab {}
@objc public protocol GoogleChromeApplication: SBApplicationProtocol {
@objc optional func windows() -> SBElementArray
@objc optional func executeJavaScript(javascript: String!) -> Any // Applies a string of JavaScript code to a document. //, id: Any!
}
extension SBApplication: GoogleChromeApplication {}
@objc public protocol GoogleChromeWindow: SBObjectProtocol {
@objc optional var name: String { get } // The title of the window.
@objc optional func tabs() -> SBElementArray
}
extension SBObject: GoogleChromeWindow {}
@objc public protocol GoogleChromeTab: SBObjectProtocol {
@objc optional var URL: String { get } // The current URL of the tab.
@objc optional var title: String { get } // The name of the tab.
}
extension SBObject: GoogleChromeTab {}
+4 -5
View File
@@ -3,13 +3,12 @@ import Cocoa
class TimeTouchBarItem: CustomButtonTouchBarItem { class TimeTouchBarItem: CustomButtonTouchBarItem {
private let dateFormatter = DateFormatter() private let dateFormatter = DateFormatter()
private var timer: Timer! private var timer: Timer!
// private let button = NSButton(title: "", target: nil, action: nil)
init(identifier: NSTouchBarItem.Identifier, formatTemplate: String, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) { init(identifier: NSTouchBarItem.Identifier, formatTemplate: String) {
dateFormatter.setLocalizedDateFormatFromTemplate(formatTemplate) dateFormatter.setLocalizedDateFormatFromTemplate(formatTemplate)
super.init(identifier: identifier, title: " ", onTap: onTap, onLongTap: onLongTap) 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.view = button self.isBordered = false
updateTime() updateTime()
} }
@@ -18,7 +17,7 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
} }
@objc func updateTime() { @objc func updateTime() {
button.title = self.dateFormatter.string(from: Date()) self.title = self.dateFormatter.string(from: Date())
} }
} }
+5 -6
View File
@@ -10,7 +10,7 @@ import Cocoa
import CoreLocation import CoreLocation
class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate { class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
private let activity = NSBackgroundActivityScheduler(identifier: "com.toxblh.mtmr.weather.updatecheck") private let activity: NSBackgroundActivityScheduler
private var units: String private var units: String
private var api_key: String private var api_key: String
private var units_str = "°F" private var units_str = "°F"
@@ -22,7 +22,8 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
private var manager:CLLocationManager! private var manager:CLLocationManager!
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text", onTap: @escaping () -> (), onLongTap: @escaping () -> ()) { init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text") {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval activity.interval = interval
self.units = units self.units = units
self.api_key = api_key self.api_key = api_key
@@ -41,9 +42,7 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
iconsSource = iconsText iconsSource = iconsText
} }
super.init(identifier: identifier, title: "⏳", onTap: onTap, onLongTap: onLongTap) super.init(identifier: identifier, title: "⏳")
self.view = button
let status = CLLocationManager.authorizationStatus() let status = CLLocationManager.authorizationStatus()
if status == .restricted || status == .denied { if status == .restricted || status == .denied {
@@ -116,7 +115,7 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
} }
func setWeather(text: String) { func setWeather(text: String) {
button.title = text self.title = text
} }
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
+1
View File
@@ -73,6 +73,7 @@
{ "type": "volumeDown", "width": 34, "align": "right" }, { "type": "volumeDown", "width": 34, "align": "right" },
{ "type": "volume", "width": 60, "align": "right" }, { "type": "volume", "width": 60, "align": "right" },
{ "type": "volumeUp", "width": 34, "align": "right" }, { "type": "volumeUp", "width": 34, "align": "right" },
{ "type": "inputsource", "align": "right" },
{ "type": "battery", "align": "right" }, { "type": "battery", "align": "right" },
{ "type": "timeButton", "align": "right" } { "type": "timeButton", "align": "right" }
] ]
@@ -0,0 +1,41 @@
import XCTest
class AppleScriptDefinitionTests: XCTestCase {
func testInline() {
let buttonNoActionFixture = """
[ { "type": "appleScriptTitledButton", "source": { "inline": "tell everything fine" } } ]
""".data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
guard case .appleScriptTitledButton(let source, _)? = result?.first?.type else {
XCTFail()
return
}
XCTAssertEqual(source.string, "tell everything fine")
}
func testPath() {
let buttonNoActionFixture = """
[ { "type": "appleScriptTitledButton", "source": { "filePath": "/ololo/pew" } } ]
""".data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
guard case .appleScriptTitledButton(let source, _)? = result?.first?.type else {
XCTFail()
return
}
let sourceStruct = source as? Source
XCTAssertEqual(sourceStruct?.filePath, "/ololo/pew")
}
func testRefreshInterval() {
let buttonNoActionFixture = """
[ { "type": "appleScriptTitledButton", "source": { "inline": "tell everything fine" }, "refreshInterval": 305} ]
""".data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
guard case .appleScriptTitledButton(_, 305)? = result?.first?.type else {
XCTFail()
return
}
}
}
+29
View File
@@ -0,0 +1,29 @@
import XCTest
class BackgroundColorTests: XCTestCase {
func testOpaque() {
let buttonNoActionFixture = """
[ { "type": "staticButton", "title": "Pew", "background": "#FF0000" } ]
""".data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
guard case .background(let color)? = result?.first?.additionalParameters[.background] else {
XCTFail()
return
}
XCTAssertEqual(color, .red)
}
func testAlpha() {
let buttonNoActionFixture = """
[ { "type": "staticButton", "title": "Pew", "background": "#FF000080" } ]
""".data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
guard case .background(let color)? = result?.first?.additionalParameters[.background] else {
XCTFail()
return
}
XCTAssertEqual(color.alphaComponent, 0.5, accuracy: 0.01)
}
}
+44 -8
View File
@@ -1,5 +1,4 @@
import XCTest import XCTest
@testable import MTMR
class ParseConfig: XCTestCase { class ParseConfig: XCTestCase {
@@ -8,8 +7,14 @@ class ParseConfig: XCTestCase {
[ { "type": "staticButton", "title": "Pew" } ] [ { "type": "staticButton", "title": "Pew" } ]
""".data(using: .utf8)! """.data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture) let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
XCTAssertEqual(result?.first?.type, .staticButton(title: "Pew")) guard case .staticButton("Pew")? = result?.first?.type else {
XCTAssertEqual(result?.first?.action, .some(.none)) XCTFail()
return
}
guard case .none? = result?.first?.action else {
XCTFail()
return
}
} }
func testButtonKeyCodeAction() { func testButtonKeyCodeAction() {
@@ -17,17 +22,48 @@ class ParseConfig: XCTestCase {
[ { "type": "staticButton", "title": "Pew", "action": "hidKey", "keycode": 123} ] [ { "type": "staticButton", "title": "Pew", "action": "hidKey", "keycode": 123} ]
""".data(using: .utf8)! """.data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture) let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture)
XCTAssertEqual(result?.first?.type, .staticButton(title: "Pew")) guard case .staticButton("Pew")? = result?.first?.type else {
XCTAssertEqual(result?.first?.action, .hidKey(keycode: 123)) XCTFail()
return
}
guard case .hidKey(keycode: 123)? = result?.first?.action else {
XCTFail()
return
}
} }
func testPredefinedItem() { func testPredefinedItem() {
let buttonKeycodeFixture = """ let buttonKeycodeFixture = """
[ { "type": "brightnessUp" } ] [ { "type": "escape" } ]
""".data(using: .utf8)! """.data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture) let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture)
XCTAssertEqual(result?.first?.type, .staticButton(title: "🔆")) guard case .staticButton("esc")? = result?.first?.type else {
XCTAssertEqual(result?.first?.action, .keyPress(keycode: 113)) XCTFail()
return
}
guard case .keyPress(keycode: 53)? = result?.first?.action else {
XCTFail()
return
}
}
func testExtendedWidthForPredefinedItem() {
let buttonKeycodeFixture = """
[ { "type": "escape", "width": 110}, ]
""".data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture)
guard case .staticButton("esc")? = result?.first?.type else {
XCTFail()
return
}
guard case .keyPress(keycode: 53)? = result?.first?.action else {
XCTFail()
return
}
guard case .width(110)? = result?.first?.additionalParameters[.width] else {
XCTFail()
return
}
} }
} }
+20 -5
View File
@@ -5,6 +5,7 @@
# My TouchBar. My rules # My TouchBar. My rules
[![GitHub release](https://img.shields.io/github/release/toxblh/MTMR.svg)](https://github.com/Toxblh/MTMR/releases) [![GitHub release](https://img.shields.io/github/release/toxblh/MTMR.svg)](https://github.com/Toxblh/MTMR/releases)
[![license](https://img.shields.io/github/license/Toxblh/MTMR.svg)](https://github.com/Toxblh/MTMR/blob/master/LICENSE) [![Total downloads](https://img.shields.io/github/downloads/Toxblh/MTMR/total.svg)](https://github.com/Toxblh/MTMR/releases/latest) ![minimal system requirements](https://img.shields.io/badge/required-macOS%2010.12.2-blue.svg) ![travis](https://travis-ci.org/Toxblh/MTMR.svg?branch=master)
<p align="center"> <p align="center">
<img src="Resources/TouchBar-v0.8.1.png"> <img src="Resources/TouchBar-v0.8.1.png">
@@ -34,7 +35,7 @@ My the idea is to create the program like a platform for plugins for customizati
- [ ] System for autoupdate (maybe https://sparkle-project.org/) - [ ] System for autoupdate (maybe https://sparkle-project.org/)
Settings: Settings:
- [ ] Intarface for plugins and export like presets - [ ] Interface for plugins and export like presets
- [ ] Startup at login - [ ] Startup at login
- [ ] Show on/off in Dock - [ ] Show on/off in Dock
- [ ] Show on/off in StatusBar - [ ] Show on/off in StatusBar
@@ -45,9 +46,8 @@ Maybe:
## Installation ## Installation
1. Download last release [Releases](https://github.com/Toxblh/MTMR/releases) - Download last [release](https://github.com/Toxblh/MTMR/releases)
3. Open MTMR - Or via Homebrew `brew cask install mtmr`
4. Open preset `open ~/Library/Application\ Support/MTMR/items.json` and customize it. Restart MTMR to apply changes.
## Preset ## Preset
@@ -68,6 +68,8 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
- battery - battery
- currency - currency
- weather - weather
- inputsource
- music (tap for pause, longTap for next)
> Media Keys > Media Keys
- previous - previous
@@ -130,6 +132,17 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
"to": "USD", "to": "USD",
``` ```
- `music`
```js
{
"type": "music",
"align": "center",
"width": 80,
"bordered": false,
"refreshInterval": 2,
},
```
## Actions: ## Actions:
- `hidKey` - `hidKey`
> https://github.com/aosm/IOHIDFamily/blob/master/IOHIDSystem/IOKit/hidsystem/ev_keymap.h use only numbers > https://github.com/aosm/IOHIDFamily/blob/master/IOHIDSystem/IOKit/hidsystem/ev_keymap.h use only numbers
@@ -167,7 +180,7 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
"url": "https://google.com", "url": "https://google.com",
``` ```
## Additional paramaters: ## Additional parameters:
- `width` allow to restrict how much room a particular button will take - `width` allow to restrict how much room a particular button will take
```json ```json
@@ -245,4 +258,6 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
## Credits ## Credits
<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" style="height: auto !important;width: auto !important;" ></a>
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).
+5 -2
View File
@@ -16,8 +16,11 @@ cd Release
rm -r App.xcarchive rm -r App.xcarchive
# Prerequisite: npm i -g create-dmg # Prerequisite: npm i -g create-dmg
create-dmg "${NAME}.app" NAME_DMG="${NAME}.app"
echo $NAME_DMG
create-dmg $NAME_DMG
VERSION=`mdls -raw -name kMDItemVersion ${NAME}.app`
VERSION=`mdls -raw -name kMDItemVersion MTMR.app`
echo $VERSION echo $VERSION
zip -r "${NAME}v${VERSION}.zip" "${NAME}.app" zip -r "${NAME}v${VERSION}.zip" "${NAME}.app"