mirror of
https://github.com/Toxblh/MTMR.git
synced 2026-11-01 11:08:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62a2aa14e1 | ||
|
|
1c0a2f064a | ||
|
|
23357816b3 | ||
|
|
877dca7587 | ||
|
|
fd537bcdfe | ||
|
|
e852ab58f8 | ||
|
|
5c7be625b6 | ||
|
|
475b66c956 | ||
|
|
19f7ff0103 | ||
|
|
3bc75b54c0 | ||
|
|
3c072923d2 | ||
|
|
b29c74ccca | ||
|
|
bbea465500 | ||
|
|
f7eb49e5de | ||
|
|
e9c69f6461 | ||
|
|
9e1fcb9a35 | ||
|
|
7523605d7a | ||
|
|
b73f8865a9 | ||
|
|
852df9d6b6 | ||
|
|
57e8af90dd | ||
|
|
0e48566c74 | ||
|
|
55177a7f90 | ||
|
|
d46827832e | ||
|
|
9e10b2d3d1 | ||
|
|
33efbbebaa | ||
|
|
d048ec851e | ||
|
|
42ebb91e6b | ||
|
|
1da4e2795f | ||
|
|
21bc525a04 | ||
|
|
b9ef3cdc35 | ||
|
|
9a34d5bca0 | ||
|
|
4c9820a390 | ||
|
|
634beeae8b | ||
|
|
3ec2422181 | ||
|
|
a3c7fa7464 | ||
|
|
20ed827750 | ||
|
|
85aa2c4f2b | ||
|
|
b761551384 | ||
|
|
3854b455a6 | ||
|
|
9c803e85cc | ||
|
|
651258b01f | ||
|
|
02baef80db | ||
|
|
cd75be6c26 | ||
|
|
7646c6e89d | ||
|
|
9c970aff63 | ||
|
|
49d7333d5e | ||
|
|
cd69bc341c | ||
|
|
453895f395 | ||
|
|
12796d6387 | ||
|
|
f65e72faed | ||
|
|
df535b2893 | ||
|
|
574ca0fb84 | ||
|
|
4a03ba79ec | ||
|
|
907b79965d | ||
|
|
fbec803d47 | ||
|
|
3f9574245a | ||
|
|
2bf6b90b04 | ||
|
|
9d596c6adc | ||
|
|
175e9edb8c | ||
|
|
8e58bb179a | ||
|
|
d37d9607d3 | ||
|
|
6b553c4d0d | ||
|
|
788927edb3 | ||
|
|
9013779f71 | ||
|
|
a85507c544 | ||
|
|
59aa113995 | ||
|
|
9c5a19925b | ||
|
|
2aa602d77d | ||
|
|
a1b3f13902 | ||
|
|
60384561d2 | ||
|
|
77938340d6 | ||
|
|
93d2f496d0 | ||
|
|
0808f0617a | ||
|
|
966a924183 | ||
|
|
a648402d04 | ||
|
|
207af6468e | ||
|
|
6609a7c806 | ||
|
|
479190d12b | ||
|
|
5ed1790601 | ||
|
|
b95f5af95f | ||
|
|
c76027ca9d | ||
|
|
55dc551516 | ||
|
|
cf5d8bf546 | ||
|
|
8031108fc9 | ||
|
|
d1a577868b | ||
|
|
5f02bb2962 | ||
|
|
cc1332dd02 | ||
|
|
a0dbfca828 | ||
|
|
76bdf12745 | ||
|
|
e1268d0d65 | ||
|
|
9c1e5f5032 | ||
|
|
88cde9256a | ||
|
|
d974f92e3d | ||
|
|
4b8ef1cd96 | ||
|
|
e65c57dbaa | ||
|
|
babc8451e0 | ||
|
|
7698ea1c8e | ||
|
|
aa95d1a1bd | ||
|
|
3fe6c22068 | ||
|
|
5ab19ee27b | ||
|
|
ae4b0aa870 | ||
|
|
1e2043ebda | ||
|
|
409b8d8b46 | ||
|
|
8bccbf3834 |
@@ -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]}"
|
||||||
@@ -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>
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
@interface MediaKeys : NSObject
|
@interface MediaKeys : NSObject
|
||||||
|
|
||||||
+ (void)decreaseVolume;
|
+ (void)HIDPostAuxKey:(UInt8)keyCode;
|
||||||
+ (void)increaseVolume;
|
|
||||||
+ (void)muteVolume;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 bgRect = aRect
|
||||||
|
bgRect.size.height = CGFloat(4)
|
||||||
|
|
||||||
var leftRect = rect
|
let bg = NSBezierPath(roundedRect: bgRect, xRadius: barRadius, yRadius: barRadius)
|
||||||
leftRect.size.width = finalWidth
|
|
||||||
let bg = NSBezierPath(roundedRect: rect, 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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
private var scrubber: NSScrubber!
|
private var scrubber: NSScrubber!
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {}
|
||||||
@@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
# My TouchBar. My rules
|
# My TouchBar. My rules
|
||||||
[](https://github.com/Toxblh/MTMR/releases)
|
[](https://github.com/Toxblh/MTMR/releases)
|
||||||
|
[](https://github.com/Toxblh/MTMR/blob/master/LICENSE) [](https://github.com/Toxblh/MTMR/releases/latest)  
|
||||||
|
|
||||||
<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).
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user