Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffc101b455 | ||
|
|
bbe9a1f10e | ||
|
|
71051d7ad0 | ||
|
|
e5136d5991 | ||
|
|
09d9f9618f | ||
|
|
534dbc2284 | ||
|
|
9e0f103ba6 | ||
|
|
c6b6f7bbd5 | ||
|
|
eee4082a1a | ||
|
|
10d94d65e2 | ||
|
|
7de87ad029 | ||
|
|
ad27d26c86 | ||
|
|
ecbd2beb48 | ||
|
|
0aed9a8680 | ||
|
|
1f764d539a | ||
|
|
e95c707009 | ||
|
|
1914dbb08f | ||
|
|
34074a1b9b | ||
|
|
e07ee1e263 | ||
|
|
9228eaffc8 | ||
|
|
f89871d294 | ||
|
|
8e35d38b59 | ||
|
|
17e023d2d2 | ||
|
|
dc94857ac4 | ||
|
|
ec228fddde | ||
|
|
958f93f440 | ||
|
|
d03b16eb89 | ||
|
|
ea573882a0 | ||
|
|
7fb936bd1d | ||
|
|
f97a3be6d3 | ||
|
|
3bc26eae82 | ||
|
|
f84463dbb2 | ||
|
|
46c86a71de | ||
|
|
ad70c034da | ||
|
|
221565f604 | ||
|
|
6ca7f11a2b | ||
|
|
63795b3d41 | ||
|
|
8a7de99bce | ||
|
|
7f13422805 | ||
|
|
56d64155ae | ||
|
|
088f141da3 | ||
|
|
091208e536 | ||
|
|
c6f6fbc5cd | ||
|
|
3cdf44903b | ||
|
|
e13e8598a7 | ||
|
|
d82c837902 | ||
|
|
d5483cdd03 | ||
|
|
9f2dd277fb | ||
|
|
e7c949209d | ||
|
|
42d3d29e0d | ||
|
|
6674b5aef7 | ||
|
|
f83f331a99 | ||
|
|
cd16c09dbd | ||
|
|
4ba9d11494 | ||
|
|
62a2aa14e1 | ||
|
|
1c0a2f064a | ||
|
|
23357816b3 | ||
|
|
877dca7587 | ||
|
|
fd537bcdfe | ||
|
|
e852ab58f8 | ||
|
|
5c7be625b6 | ||
|
|
475b66c956 | ||
|
|
19f7ff0103 | ||
|
|
3bc75b54c0 | ||
|
|
3c072923d2 | ||
|
|
b29c74ccca | ||
|
|
bbea465500 | ||
|
|
f7eb49e5de | ||
|
|
e9c69f6461 | ||
|
|
9e1fcb9a35 | ||
|
|
7523605d7a | ||
|
|
b9ef3cdc35 |
@@ -11,18 +11,22 @@
|
|||||||
36300E8F20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */; };
|
36300E8F20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */; };
|
||||||
36300E9120A2D2B200B31C71 /* BackgroundColorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36300E9020A2D2B200B31C71 /* BackgroundColorTests.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 */; };
|
||||||
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */; };
|
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */; };
|
||||||
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
|
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
|
||||||
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */ = {isa = PBXBuildFile; fileRef = 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */; };
|
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */ = {isa = PBXBuildFile; fileRef = 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */; };
|
||||||
|
60173D3E20C0031B002C305F /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 60173D3D20C0031B002C305F /* LaunchAtLoginController.m */; };
|
||||||
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */; };
|
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */; };
|
||||||
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
|
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
|
||||||
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */; };
|
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */; };
|
||||||
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */; };
|
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */; };
|
||||||
|
60669B4320AD8FA80074E817 /* GroupBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60669B4220AD8FA80074E817 /* GroupBarItem.swift */; };
|
||||||
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 */; };
|
||||||
@@ -30,6 +34,9 @@
|
|||||||
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D621205E03F5006E6B86 /* TouchBarController.swift */; };
|
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D621205E03F5006E6B86 /* TouchBarController.swift */; };
|
||||||
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; };
|
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; };
|
||||||
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; };
|
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; };
|
||||||
|
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08173262135F02B005D4908 /* NightShiftBarItem.swift */; };
|
||||||
|
B081732A2135F354005D4908 /* CoreBrightness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B08173292135F354005D4908 /* CoreBrightness.framework */; };
|
||||||
|
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B081732B213739FE005D4908 /* DnDBarItem.swift */; };
|
||||||
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B252205C7D8000BC04DC /* AppDelegate.swift */; };
|
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B252205C7D8000BC04DC /* AppDelegate.swift */; };
|
||||||
B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B082B256205C7D8000BC04DC /* Assets.xcassets */; };
|
B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B082B256205C7D8000BC04DC /* Assets.xcassets */; };
|
||||||
B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B082B258205C7D8000BC04DC /* Main.storyboard */; };
|
B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B082B258205C7D8000BC04DC /* Main.storyboard */; };
|
||||||
@@ -46,6 +53,7 @@
|
|||||||
B0B17438207D6B590004B740 /* Vox.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742E207D6B590004B740 /* Vox.next.scpt */; };
|
B0B17438207D6B590004B740 /* Vox.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742E207D6B590004B740 /* Vox.next.scpt */; };
|
||||||
B0B17439207D6B590004B740 /* Vox.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */; };
|
B0B17439207D6B590004B740 /* Vox.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */; };
|
||||||
B0B1743A207D6B590004B740 /* iTunes.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */; };
|
B0B1743A207D6B590004B740 /* iTunes.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */; };
|
||||||
|
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */; };
|
||||||
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 */
|
||||||
|
|
||||||
@@ -54,6 +62,7 @@
|
|||||||
36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptDefinitionTests.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
||||||
@@ -61,13 +70,17 @@
|
|||||||
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsParsing.swift; sourceTree = "<group>"; };
|
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsParsing.swift; sourceTree = "<group>"; };
|
||||||
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigTests.swift; sourceTree = "<group>"; };
|
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigTests.swift; sourceTree = "<group>"; };
|
||||||
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = defaultPreset.json; sourceTree = "<group>"; };
|
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = defaultPreset.json; sourceTree = "<group>"; };
|
||||||
|
60173D3C20C0031B002C305F /* LaunchAtLoginController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LaunchAtLoginController.h; sourceTree = "<group>"; };
|
||||||
|
60173D3D20C0031B002C305F /* LaunchAtLoginController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = "<group>"; };
|
||||||
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrightnessViewController.swift; sourceTree = "<group>"; };
|
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrightnessViewController.swift; sourceTree = "<group>"; };
|
||||||
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VolumeViewController.swift; sourceTree = "<group>"; };
|
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VolumeViewController.swift; sourceTree = "<group>"; };
|
||||||
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScrubberTouchBarItem.swift; sourceTree = "<group>"; };
|
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScrubberTouchBarItem.swift; sourceTree = "<group>"; };
|
||||||
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeprecatedCarbonAPI.h; sourceTree = "<group>"; };
|
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeprecatedCarbonAPI.h; sourceTree = "<group>"; };
|
||||||
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>"; };
|
||||||
|
60669B4220AD8FA80074E817 /* GroupBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupBarItem.swift; 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>"; };
|
||||||
@@ -77,6 +90,10 @@
|
|||||||
B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarPrivateApi.h; sourceTree = "<group>"; };
|
B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarPrivateApi.h; sourceTree = "<group>"; };
|
||||||
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TouchBarPrivateApi-Bridging.h"; sourceTree = "<group>"; };
|
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TouchBarPrivateApi-Bridging.h"; sourceTree = "<group>"; };
|
||||||
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DFRFoundation.framework; path = ../../../../../System/Library/PrivateFrameworks/DFRFoundation.framework; sourceTree = "<group>"; };
|
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DFRFoundation.framework; path = ../../../../../System/Library/PrivateFrameworks/DFRFoundation.framework; sourceTree = "<group>"; };
|
||||||
|
B08173262135F02B005D4908 /* NightShiftBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightShiftBarItem.swift; sourceTree = "<group>"; };
|
||||||
|
B08173282135F128005D4908 /* CBBlueLightClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CBBlueLightClient.h; sourceTree = "<group>"; };
|
||||||
|
B08173292135F354005D4908 /* CoreBrightness.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBrightness.framework; path = ../../../../../System/Library/PrivateFrameworks/CoreBrightness.framework; sourceTree = "<group>"; };
|
||||||
|
B081732B213739FE005D4908 /* DnDBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DnDBarItem.swift; sourceTree = "<group>"; };
|
||||||
B082B24F205C7D8000BC04DC /* MTMR.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MTMR.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
B082B24F205C7D8000BC04DC /* MTMR.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MTMR.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
B082B252205C7D8000BC04DC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
B082B252205C7D8000BC04DC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
B082B256205C7D8000BC04DC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
B082B256205C7D8000BC04DC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@@ -98,6 +115,7 @@
|
|||||||
B0B1742E207D6B590004B740 /* Vox.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Vox.next.scpt; sourceTree = "<group>"; };
|
B0B1742E207D6B590004B740 /* Vox.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Vox.next.scpt; sourceTree = "<group>"; };
|
||||||
B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Vox.nowPlaying.scpt; sourceTree = "<group>"; };
|
B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Vox.nowPlaying.scpt; sourceTree = "<group>"; };
|
||||||
B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iTunes.nowPlaying.scpt; sourceTree = "<group>"; };
|
B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iTunes.nowPlaying.scpt; sourceTree = "<group>"; };
|
||||||
|
B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportNSTouchBar.swift; sourceTree = "<group>"; };
|
||||||
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TouchBarSupport.m; sourceTree = "<group>"; };
|
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TouchBarSupport.m; sourceTree = "<group>"; };
|
||||||
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarSupport.h; sourceTree = "<group>"; };
|
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarSupport.h; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@@ -107,6 +125,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
B081732A2135F354005D4908 /* CoreBrightness.framework in Frameworks */,
|
||||||
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */,
|
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */,
|
||||||
B09EB1E6207C0F8E00D5C1E0 /* MultitouchSupport.framework in Frameworks */,
|
B09EB1E6207C0F8E00D5C1E0 /* MultitouchSupport.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
@@ -125,6 +144,7 @@
|
|||||||
B059D62B205F11E8006E6B86 /* Frameworks */ = {
|
B059D62B205F11E8006E6B86 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
B08173292135F354005D4908 /* CoreBrightness.framework */,
|
||||||
B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */,
|
B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */,
|
||||||
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */,
|
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */,
|
||||||
);
|
);
|
||||||
@@ -173,6 +193,8 @@
|
|||||||
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */,
|
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */,
|
||||||
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */,
|
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */,
|
||||||
B05600D22083E9BB00EB218D /* CustomSlider.swift */,
|
B05600D22083E9BB00EB218D /* CustomSlider.swift */,
|
||||||
|
36A778BD20A6C27100B38714 /* GeneralExtensions.swift */,
|
||||||
|
B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */,
|
||||||
);
|
);
|
||||||
path = MTMR;
|
path = MTMR;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -215,6 +237,9 @@
|
|||||||
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */,
|
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */,
|
||||||
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */,
|
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */,
|
||||||
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */,
|
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */,
|
||||||
|
60173D3C20C0031B002C305F /* LaunchAtLoginController.h */,
|
||||||
|
60173D3D20C0031B002C305F /* LaunchAtLoginController.m */,
|
||||||
|
B08173282135F128005D4908 /* CBBlueLightClient.h */,
|
||||||
);
|
);
|
||||||
path = CBridge;
|
path = CBridge;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -230,6 +255,10 @@
|
|||||||
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */,
|
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */,
|
||||||
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
|
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
|
||||||
60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */,
|
60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */,
|
||||||
|
60C44AFC20A373A100C0EC91 /* MusicBarItem.swift */,
|
||||||
|
60669B4220AD8FA80074E817 /* GroupBarItem.swift */,
|
||||||
|
B08173262135F02B005D4908 /* NightShiftBarItem.swift */,
|
||||||
|
B081732B213739FE005D4908 /* DnDBarItem.swift */,
|
||||||
);
|
);
|
||||||
path = Widgets;
|
path = Widgets;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -356,12 +385,19 @@
|
|||||||
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 */,
|
||||||
|
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */,
|
||||||
|
60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */,
|
||||||
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
|
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
|
||||||
|
60173D3E20C0031B002C305F /* LaunchAtLoginController.m in Sources */,
|
||||||
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
|
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
|
||||||
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
|
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
|
||||||
|
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */,
|
||||||
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
|
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
|
||||||
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
|
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
|
||||||
|
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */,
|
||||||
60F7D454208CC31400ABF5D2 /* InputSourceBarItem.swift in Sources */,
|
60F7D454208CC31400ABF5D2 /* InputSourceBarItem.swift in Sources */,
|
||||||
|
36A778BE20A6C27100B38714 /* GeneralExtensions.swift in Sources */,
|
||||||
|
60669B4320AD8FA80074E817 /* GroupBarItem.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 */,
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
private var fileSystemSource: DispatchSourceFileSystemObject?
|
private var fileSystemSource: DispatchSourceFileSystemObject?
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
|
let _ = AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true] as NSDictionary)
|
||||||
|
|
||||||
TouchBarController.shared.setupControlStripPresence()
|
TouchBarController.shared.setupControlStripPresence()
|
||||||
|
|
||||||
if let button = statusItem.button {
|
if let button = statusItem.button {
|
||||||
@@ -28,7 +30,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openPrefereces(_ sender: Any?) {
|
@objc func openPreferences(_ sender: Any?) {
|
||||||
let task = Process()
|
let task = Process()
|
||||||
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
||||||
let presetPath = appSupportDirectory.appending("/items.json")
|
let presetPath = appSupportDirectory.appending("/items.json")
|
||||||
@@ -37,16 +39,28 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
task.launch()
|
task.launch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func updatePreset(_ sender: Any?) {
|
|
||||||
TouchBarController.shared.createAndUpdatePreset()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func toggleControlStrip(_ sender: Any?) {
|
@objc func toggleControlStrip(_ sender: Any?) {
|
||||||
TouchBarController.shared.controlStripState = !TouchBarController.shared.controlStripState
|
TouchBarController.shared.controlStripState = !TouchBarController.shared.controlStripState
|
||||||
createMenu()
|
createMenu()
|
||||||
TouchBarController.shared.resetControlStrip()
|
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();
|
||||||
|
|
||||||
@@ -59,32 +73,42 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
dialog.allowedFileTypes = ["json"]
|
dialog.allowedFileTypes = ["json"]
|
||||||
dialog.directoryURL = NSURL.fileURL(withPath: NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR"), isDirectory: true)
|
dialog.directoryURL = NSURL.fileURL(withPath: NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR"), isDirectory: true)
|
||||||
|
|
||||||
if (dialog.runModal() == NSApplication.ModalResponse.OK) {
|
if dialog.runModal() == .OK, let path = dialog.url?.path {
|
||||||
let result = dialog.url
|
TouchBarController.shared.reloadPreset(path: path)
|
||||||
|
|
||||||
if (result != nil) {
|
|
||||||
let path = result!.path
|
|
||||||
let jsonData = path.fileData
|
|
||||||
let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
|
|
||||||
|
|
||||||
TouchBarController.shared.createAndUpdatePreset(jsonItems: jsonItems)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func toggleStartAtLogin(_ sender: Any?) {
|
||||||
|
LaunchAtLoginController().setLaunchAtLogin(!LaunchAtLoginController().launchAtLogin, for: NSURL.fileURL(withPath: Bundle.main.bundlePath))
|
||||||
|
createMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMenu() {
|
func createMenu() {
|
||||||
let menu = NSMenu()
|
let menu = NSMenu()
|
||||||
menu.addItem(withTitle: "Preferences", action: #selector(openPrefereces(_:)), keyEquivalent: ",")
|
|
||||||
menu.addItem(withTitle: "Reload Preset", action: #selector(updatePreset(_:)), keyEquivalent: "r")
|
let startAtLogin = NSMenuItem(title: "Start at login", action: #selector(toggleStartAtLogin(_:)), keyEquivalent: "L")
|
||||||
menu.addItem(withTitle: "Open Preset", action: #selector(openPreset(_:)), keyEquivalent: "O")
|
startAtLogin.state = LaunchAtLoginController().launchAtLogin ? .on : .off
|
||||||
menu.addItem(withTitle: TouchBarController.shared.controlStripState ? "Hide Control Strip" : "Show Control Strip" , action: #selector(toggleControlStrip(_:)), keyEquivalent: "T")
|
|
||||||
|
let hideControlStrip = NSMenuItem(title: "Hide Control Strip", action: #selector(toggleControlStrip(_:)), keyEquivalent: "T")
|
||||||
|
hideControlStrip.state = TouchBarController.shared.controlStripState ? .on : .off
|
||||||
|
|
||||||
|
let settingSeparator = NSMenuItem(title: "Settings", action: nil, keyEquivalent: "")
|
||||||
|
settingSeparator.isEnabled = false
|
||||||
|
|
||||||
|
menu.addItem(withTitle: "Preferences", action: #selector(openPreferences(_:)), keyEquivalent: ",")
|
||||||
|
menu.addItem(withTitle: "Open preset", action: #selector(openPreset(_:)), keyEquivalent: "O")
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
menu.addItem(settingSeparator)
|
||||||
|
menu.addItem(hideControlStrip)
|
||||||
|
menu.addItem(withTitle: "Toggle current app in blacklist" , action: #selector(toggleBlackListedApp(_:)), keyEquivalent: "B")
|
||||||
|
menu.addItem(startAtLogin)
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
|
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
|
||||||
statusItem.menu = menu
|
statusItem.menu = menu
|
||||||
}
|
}
|
||||||
|
|
||||||
func reloadOnDefaultConfigChanged() {
|
func reloadOnDefaultConfigChanged() {
|
||||||
let file = NSURL.fileURL(withPath: NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR/items.json"))
|
let file = NSURL.fileURL(withPath: standardConfigPath)
|
||||||
|
|
||||||
let fd = open(file.path, O_EVTONLY)
|
let fd = open(file.path, O_EVTONLY)
|
||||||
|
|
||||||
@@ -93,7 +117,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
self.fileSystemSource?.setEventHandler(handler: {
|
self.fileSystemSource?.setEventHandler(handler: {
|
||||||
print("Config changed, reloading...")
|
print("Config changed, reloading...")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
TouchBarController.shared.createAndUpdatePreset()
|
TouchBarController.shared.reloadPreset(path: file.path)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ 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 {
|
||||||
self.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
|
||||||
@@ -42,8 +42,11 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,9 +63,6 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SourceProtocol {
|
extension DispatchQueue {
|
||||||
var appleScript: NSAppleScript? {
|
static let appleScriptQueue = DispatchQueue(label: "mtmr.applescript")
|
||||||
guard let source = self.string else { return nil }
|
|
||||||
return NSAppleScript(source: source)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "dnd-off.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "dnd-on.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 7.8 KiB |
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "KeyboardBrightDown.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "KeyboardBrightUp.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "NightShift.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "NightShiftEnabled.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// CBBlueLightClient.h
|
||||||
|
// MTMR
|
||||||
|
//
|
||||||
|
// Created by Anton Palgunov on 28/08/2018.
|
||||||
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int hour;
|
||||||
|
int minute;
|
||||||
|
} Time;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Time fromTime;
|
||||||
|
Time toTime;
|
||||||
|
} Schedule;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BOOL active;
|
||||||
|
BOOL enabled;
|
||||||
|
BOOL sunSchedulePermitted;
|
||||||
|
int mode;
|
||||||
|
Schedule schedule;
|
||||||
|
unsigned long long disableFlags;
|
||||||
|
} Status;
|
||||||
|
|
||||||
|
@interface CBBlueLightClient: NSObject
|
||||||
|
- (BOOL) setEnabled:(BOOL)enabled;
|
||||||
|
- (BOOL) setMode:(int)mode;
|
||||||
|
- (void) getBlueLightStatus: (Status *)status;
|
||||||
|
@end
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// LaunchAtLoginController.h
|
||||||
|
//
|
||||||
|
// Copyright 2011 Tomáš Znamenáček
|
||||||
|
// Copyright 2010 Ben Clark-Robinson
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the ‘Software’),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
@import Foundation;
|
||||||
|
@import CoreServices;
|
||||||
|
|
||||||
|
@interface LaunchAtLoginController : NSObject {}
|
||||||
|
|
||||||
|
@property(assign) BOOL launchAtLogin;
|
||||||
|
|
||||||
|
- (BOOL) willLaunchAtLogin: (NSURL*) itemURL;
|
||||||
|
- (void) setLaunchAtLogin: (BOOL) enabled forURL: (NSURL*) itemURL;
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
//
|
||||||
|
// LaunchAtLoginController.m
|
||||||
|
//
|
||||||
|
// Copyright 2011 Tomáš Znamenáček
|
||||||
|
// Copyright 2010 Ben Clark-Robinson
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the ‘Software’),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#import "LaunchAtLoginController.h"
|
||||||
|
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
|
||||||
|
static NSString *const StartAtLoginKey = @"launchAtLogin";
|
||||||
|
|
||||||
|
@interface LaunchAtLoginController ()
|
||||||
|
@property(assign) LSSharedFileListRef loginItems;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation LaunchAtLoginController
|
||||||
|
@synthesize loginItems;
|
||||||
|
|
||||||
|
#pragma mark Change Observing
|
||||||
|
|
||||||
|
void sharedFileListDidChange(LSSharedFileListRef inList, void *context)
|
||||||
|
{
|
||||||
|
LaunchAtLoginController *self = (__bridge id) context;
|
||||||
|
[self willChangeValueForKey:StartAtLoginKey];
|
||||||
|
[self didChangeValueForKey:StartAtLoginKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark Initialization
|
||||||
|
|
||||||
|
- (id) init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
|
||||||
|
LSSharedFileListAddObserver(loginItems, CFRunLoopGetMain(),
|
||||||
|
(CFStringRef)NSDefaultRunLoopMode, sharedFileListDidChange, (voidPtr)CFBridgingRetain(self));
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) dealloc
|
||||||
|
{
|
||||||
|
LSSharedFileListRemoveObserver(loginItems, CFRunLoopGetMain(),
|
||||||
|
(CFStringRef)NSDefaultRunLoopMode, sharedFileListDidChange, (__bridge void *)(self));
|
||||||
|
CFRelease(loginItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark Launch List Control
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
LSSharedFileListItemRef copyItemWithURLinFileList(NSURL* wantedURL, LSSharedFileListRef fileList) {
|
||||||
|
if (wantedURL == NULL || fileList == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
NSArray *listSnapshot = (__bridge_transfer NSArray *)LSSharedFileListCopySnapshot(fileList, NULL);
|
||||||
|
for(NSUInteger i = 0; i< [listSnapshot count]; i++) {
|
||||||
|
LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)[listSnapshot objectAtIndex:i];
|
||||||
|
UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
|
||||||
|
CFURLRef currentItemURL = NULL;
|
||||||
|
LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL);
|
||||||
|
if (currentItemURL && [(__bridge_transfer NSURL*)currentItemURL isEqual:wantedURL]) {
|
||||||
|
CFRetain(item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
- (BOOL) willLaunchAtLogin: (NSURL*) itemURL
|
||||||
|
{
|
||||||
|
return !!copyItemWithURLinFileList(itemURL, loginItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setLaunchAtLogin: (BOOL) enabled forURL: (NSURL*) itemURL
|
||||||
|
{
|
||||||
|
LSSharedFileListItemRef appItem = copyItemWithURLinFileList(itemURL, loginItems);
|
||||||
|
if (enabled && !appItem) {
|
||||||
|
LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst,
|
||||||
|
NULL, NULL, (__bridge CFURLRef)itemURL, NULL, NULL);
|
||||||
|
} else if (!enabled && appItem) {
|
||||||
|
LSSharedFileListItemRemove(loginItems, appItem);
|
||||||
|
}
|
||||||
|
if (appItem) {
|
||||||
|
CFRelease(appItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark Basic Interface
|
||||||
|
|
||||||
|
- (NSURL*) appURL
|
||||||
|
{
|
||||||
|
return [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setLaunchAtLogin: (BOOL) enabled
|
||||||
|
{
|
||||||
|
[self willChangeValueForKey:StartAtLoginKey];
|
||||||
|
[self setLaunchAtLogin:enabled forURL:[self appURL]];
|
||||||
|
[self didChangeValueForKey:StartAtLoginKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) launchAtLogin
|
||||||
|
{
|
||||||
|
return [self willLaunchAtLogin:[self appURL]];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
#pragma clang diagnostic pop
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
#import "TouchBarPrivateApi.h"
|
#import "TouchBarPrivateApi.h"
|
||||||
#import "TouchBarSupport.h"
|
#import "TouchBarSupport.h"
|
||||||
#import "DeprecatedCarbonAPI.h"
|
#import "DeprecatedCarbonAPI.h"
|
||||||
|
#import "CBBlueLightClient.h"
|
||||||
|
#import "LaunchAtLoginController.h"
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,16 @@ extern void DFRSystemModalShowsCloseBoxWhenFrontMost(BOOL);
|
|||||||
|
|
||||||
@interface NSTouchBar (PrivateMethods)
|
@interface NSTouchBar (PrivateMethods)
|
||||||
|
|
||||||
// presentSystemModalFunctionBar:placement:systemTrayItemIdentifier:
|
// macOS 10.14
|
||||||
|
+ (void)presentSystemModalTouchBar:(NSTouchBar *)touchBar placement:(long long)placement systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier;
|
||||||
|
+ (void)presentSystemModalTouchBar:(NSTouchBar *)touchBar systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier;
|
||||||
|
+ (void)dismissSystemModalTouchBar:(NSTouchBar *)touchBar;
|
||||||
|
+ (void)minimizeSystemModalTouchBar:(NSTouchBar *)touchBar;
|
||||||
|
|
||||||
|
// macOS 10.13
|
||||||
+ (void)presentSystemModalFunctionBar:(NSTouchBar *)touchBar placement:(long long)placement systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier;
|
+ (void)presentSystemModalFunctionBar:(NSTouchBar *)touchBar placement:(long long)placement systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier;
|
||||||
|
|
||||||
+ (void)presentSystemModalFunctionBar:(NSTouchBar *)touchBar systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier;
|
+ (void)presentSystemModalFunctionBar:(NSTouchBar *)touchBar systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier;
|
||||||
|
|
||||||
+ (void)dismissSystemModalFunctionBar:(NSTouchBar *)touchBar;
|
+ (void)dismissSystemModalFunctionBar:(NSTouchBar *)touchBar;
|
||||||
|
|
||||||
+ (void)minimizeSystemModalFunctionBar:(NSTouchBar *)touchBar;
|
+ (void)minimizeSystemModalFunctionBar:(NSTouchBar *)touchBar;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,16 +9,14 @@
|
|||||||
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! //todo hide completely
|
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.longTapClosure = callbackLong
|
|
||||||
self.attributedTitle = title.defaultTouchbarAttributedString
|
self.attributedTitle = title.defaultTouchbarAttributedString
|
||||||
|
|
||||||
super.init(identifier: identifier)
|
super.init(identifier: identifier)
|
||||||
@@ -63,10 +61,17 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
|||||||
|
|
||||||
var attributedTitle: NSAttributedString {
|
var attributedTitle: NSAttributedString {
|
||||||
didSet {
|
didSet {
|
||||||
|
self.button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
|
||||||
self.button?.attributedTitle = attributedTitle
|
self.button?.attributedTitle = attributedTitle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var image: NSImage? {
|
||||||
|
didSet {
|
||||||
|
button.image = image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func reinstallButton() {
|
private func reinstallButton() {
|
||||||
let title = button.attributedTitle
|
let title = button.attributedTitle
|
||||||
let image = button.image
|
let image = button.image
|
||||||
@@ -80,7 +85,10 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
|||||||
button.isBordered = isBordered
|
button.isBordered = isBordered
|
||||||
button.bezelStyle = isBordered ? .rounded : .inline
|
button.bezelStyle = isBordered ? .rounded : .inline
|
||||||
}
|
}
|
||||||
|
button.imageScaling = .scaleProportionallyDown
|
||||||
|
button.imageHugsTitle = true
|
||||||
button.attributedTitle = title
|
button.attributedTitle = title
|
||||||
|
self.button?.imagePosition = title.length > 0 ? .imageLeading : .imageOnly
|
||||||
button.image = image
|
button.image = image
|
||||||
self.view = button
|
self.view = button
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.14</string>
|
<string>0.18</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ struct BarItemDefinition: Decodable {
|
|||||||
let type = try container.decode(String.self, forKey: .type)
|
let type = try container.decode(String.self, forKey: .type)
|
||||||
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type)
|
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type)
|
||||||
var additionalParameters = try GeneralParameters(from: decoder).parameters
|
var additionalParameters = try GeneralParameters(from: decoder).parameters
|
||||||
|
|
||||||
if let result = try? parametersDecoder(decoder),
|
if let result = try? parametersDecoder(decoder),
|
||||||
case let (itemType, action, longAction, parameters) = result {
|
case let (itemType, action, longAction, parameters) = result {
|
||||||
parameters.forEach { additionalParameters[$0] = $1 }
|
parameters.forEach { additionalParameters[$0] = $1 }
|
||||||
@@ -44,39 +45,59 @@ class SupportedTypesHolder {
|
|||||||
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, longAction: LongActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter])
|
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, longAction: LongActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter])
|
||||||
private var supportedTypes: [String: ParametersDecoder] = [
|
private var supportedTypes: [String: ParametersDecoder] = [
|
||||||
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), longAction: .none, parameters: [.align: .align(.left)]) },
|
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), longAction: .none, parameters: [.align: .align(.left)]) },
|
||||||
|
|
||||||
"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: 144), longAction: .none, parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .keyPress(keycode: 144), longAction: .none, parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
|
|
||||||
"brightnessDown": { _ in
|
"brightnessDown": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
|
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
|
||||||
return (item: .staticButton(title: ""), action: .keyPress(keycode: 145), longAction: .none, parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .keyPress(keycode: 145), longAction: .none, parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"illuminationUp": { _ in
|
||||||
|
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_up"))
|
||||||
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_UP), longAction: .none, parameters: [.image: imageParameter])
|
||||||
|
},
|
||||||
|
|
||||||
|
"illuminationDown": { _ in
|
||||||
|
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_down"))
|
||||||
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_DOWN), longAction: .none, parameters: [.image: imageParameter])
|
||||||
|
},
|
||||||
|
|
||||||
"volumeDown": { _ in
|
"volumeDown": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), longAction: .none, parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), longAction: .none, parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
|
|
||||||
"volumeUp": { _ in
|
"volumeUp": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeUpTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeUpTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), longAction: .none, parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), longAction: .none, parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
|
|
||||||
"mute": { _ in
|
"mute": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarAudioOutputMuteTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarAudioOutputMuteTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), longAction: .none, parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), longAction: .none, parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
|
|
||||||
"previous": { _ in
|
"previous": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarRewindTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarRewindTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), longAction: .none, parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), longAction: .none, parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
|
|
||||||
"play": { _ in
|
"play": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarPlayPauseTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarPlayPauseTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), longAction: .none, parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), longAction: .none, parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
|
|
||||||
"next": { _ in
|
"next": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarFastForwardTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarFastForwardTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), longAction: .none, parameters: [.image: imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), longAction: .none, parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
|
|
||||||
"weather": { decoder in
|
"weather": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key ; case icon_type }
|
enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key ; case icon_type }
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
@@ -88,6 +109,7 @@ class SupportedTypesHolder {
|
|||||||
let longAction = try LongActionType(from: decoder)
|
let longAction = try LongActionType(from: decoder)
|
||||||
return (item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"), action: action, longAction: longAction, parameters: [:])
|
return (item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"), action: action, longAction: longAction, parameters: [:])
|
||||||
},
|
},
|
||||||
|
|
||||||
"currency": { decoder in
|
"currency": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to }
|
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to }
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
@@ -98,12 +120,15 @@ class SupportedTypesHolder {
|
|||||||
let longAction = try LongActionType(from: decoder)
|
let longAction = try LongActionType(from: decoder)
|
||||||
return (item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), action: action, longAction: longAction, parameters: [:])
|
return (item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), action: action, longAction: longAction, parameters: [:])
|
||||||
},
|
},
|
||||||
|
|
||||||
"dock": { decoder in
|
"dock": { decoder in
|
||||||
return (item: .dock(), action: .none, longAction: .none, parameters: [:])
|
return (item: .dock(), action: .none, longAction: .none, parameters: [:])
|
||||||
},
|
},
|
||||||
|
|
||||||
"inputsource": { decoder in
|
"inputsource": { decoder in
|
||||||
return (item: .inputsource(), action: .none, longAction: .none, parameters: [:])
|
return (item: .inputsource(), action: .none, longAction: .none, parameters: [:])
|
||||||
},
|
},
|
||||||
|
|
||||||
"volume": { decoder in
|
"volume": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case image }
|
enum CodingKeys: String, CodingKey { case image }
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
@@ -113,6 +138,7 @@ class SupportedTypesHolder {
|
|||||||
return (item: .volume(), action: .none, longAction: .none, parameters: [:])
|
return (item: .volume(), action: .none, longAction: .none, parameters: [:])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"brightness": { decoder in
|
"brightness": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval; case image }
|
enum CodingKeys: String, CodingKey { case refreshInterval; case image }
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
@@ -123,8 +149,53 @@ class SupportedTypesHolder {
|
|||||||
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [:])
|
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [:])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), longAction: .none, parameters: [:]) },
|
"sleep": { _ in 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: [:]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
"group": { decoder in
|
||||||
|
enum CodingKeys: CodingKey { case items }
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
let items = try container.decode([BarItemDefinition].self, forKey: .items)
|
||||||
|
return (
|
||||||
|
item: .groupBar(items: items),
|
||||||
|
action: .none,
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
"nightShift": {_ in
|
||||||
|
return (
|
||||||
|
item: .nightShift(),
|
||||||
|
action: .none,
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
"dnd": { _ in
|
||||||
|
return (
|
||||||
|
item: .dnd(),
|
||||||
|
action: .none,
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
static let sharedInstance = SupportedTypesHolder()
|
static let sharedInstance = SupportedTypesHolder()
|
||||||
@@ -157,6 +228,10 @@ 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)
|
||||||
|
case groupBar(items: [BarItemDefinition])
|
||||||
|
case nightShift()
|
||||||
|
case dnd()
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case type
|
case type
|
||||||
@@ -172,6 +247,7 @@ enum ItemType: Decodable {
|
|||||||
case image
|
case image
|
||||||
case url
|
case url
|
||||||
case longUrl
|
case longUrl
|
||||||
|
case items
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ItemTypeRaw: String, Decodable {
|
enum ItemTypeRaw: String, Decodable {
|
||||||
@@ -185,44 +261,72 @@ enum ItemType: Decodable {
|
|||||||
case weather
|
case weather
|
||||||
case currency
|
case currency
|
||||||
case inputsource
|
case inputsource
|
||||||
|
case music
|
||||||
|
case groupBar
|
||||||
|
case nightShift
|
||||||
|
case dnd
|
||||||
}
|
}
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
let type = try container.decode(ItemTypeRaw.self, forKey: .type)
|
let type = try container.decode(ItemTypeRaw.self, forKey: .type)
|
||||||
switch type {
|
switch type {
|
||||||
|
|
||||||
case .appleScriptTitledButton:
|
case .appleScriptTitledButton:
|
||||||
let source = try container.decode(Source.self, forKey: .source)
|
let source = try container.decode(Source.self, forKey: .source)
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||||
self = .appleScriptTitledButton(source: source, refreshInterval: interval)
|
self = .appleScriptTitledButton(source: source, refreshInterval: interval)
|
||||||
|
|
||||||
case .staticButton:
|
case .staticButton:
|
||||||
let title = try container.decode(String.self, forKey: .title)
|
let title = try container.decode(String.self, forKey: .title)
|
||||||
self = .staticButton(title: title)
|
self = .staticButton(title: title)
|
||||||
|
|
||||||
case .timeButton:
|
case .timeButton:
|
||||||
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
|
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
|
||||||
self = .timeButton(formatTemplate: template)
|
self = .timeButton(formatTemplate: template)
|
||||||
|
|
||||||
case .battery:
|
case .battery:
|
||||||
self = .battery()
|
self = .battery()
|
||||||
|
|
||||||
case .dock:
|
case .dock:
|
||||||
self = .dock()
|
self = .dock()
|
||||||
|
|
||||||
case .volume:
|
case .volume:
|
||||||
self = .volume()
|
self = .volume()
|
||||||
|
|
||||||
case .brightness:
|
case .brightness:
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
|
||||||
self = .brightness(refreshInterval: interval)
|
self = .brightness(refreshInterval: interval)
|
||||||
|
|
||||||
case .weather:
|
case .weather:
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||||
let units = try container.decodeIfPresent(String.self, forKey: .units) ?? "metric"
|
let units = try container.decodeIfPresent(String.self, forKey: .units) ?? "metric"
|
||||||
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
|
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
|
||||||
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
|
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
|
||||||
self = .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type)
|
self = .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type)
|
||||||
|
|
||||||
case .currency:
|
case .currency:
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
|
||||||
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
|
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
|
||||||
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
|
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
|
||||||
self = .currency(interval: interval, from: from, to: to)
|
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)
|
||||||
|
|
||||||
|
case .groupBar:
|
||||||
|
let items = try container.decode([BarItemDefinition].self, forKey: .items)
|
||||||
|
self = .groupBar(items: items)
|
||||||
|
|
||||||
|
case .nightShift:
|
||||||
|
self = .nightShift()
|
||||||
|
|
||||||
|
case .dnd:
|
||||||
|
self = .dnd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,7 +335,7 @@ enum ActionType: Decodable {
|
|||||||
case none
|
case none
|
||||||
case hidKey(keycode: Int32)
|
case hidKey(keycode: Int32)
|
||||||
case keyPress(keycode: Int)
|
case keyPress(keycode: Int)
|
||||||
case appleSctipt(source: SourceProtocol)
|
case appleScript(source: SourceProtocol)
|
||||||
case shellScript(executable: String, parameters: [String])
|
case shellScript(executable: String, parameters: [String])
|
||||||
case custom(closure: ()->())
|
case custom(closure: ()->())
|
||||||
case openUrl(url: String)
|
case openUrl(url: String)
|
||||||
@@ -256,23 +360,29 @@ enum ActionType: Decodable {
|
|||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
|
let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
|
||||||
|
|
||||||
switch type {
|
switch type {
|
||||||
case .some(.hidKey):
|
case .some(.hidKey):
|
||||||
let keycode = try container.decode(Int32.self, forKey: .keycode)
|
let keycode = try container.decode(Int32.self, forKey: .keycode)
|
||||||
self = .hidKey(keycode: keycode)
|
self = .hidKey(keycode: keycode)
|
||||||
|
|
||||||
case .some(.keyPress):
|
case .some(.keyPress):
|
||||||
let keycode = try container.decode(Int.self, forKey: .keycode)
|
let keycode = try container.decode(Int.self, forKey: .keycode)
|
||||||
self = .keyPress(keycode: keycode)
|
self = .keyPress(keycode: keycode)
|
||||||
|
|
||||||
case .some(.appleScript):
|
case .some(.appleScript):
|
||||||
let source = try container.decode(Source.self, forKey: .actionAppleScript)
|
let source = try container.decode(Source.self, forKey: .actionAppleScript)
|
||||||
self = .appleSctipt(source: source)
|
self = .appleScript(source: source)
|
||||||
|
|
||||||
case .some(.shellScript):
|
case .some(.shellScript):
|
||||||
let executable = try container.decode(String.self, forKey: .executablePath)
|
let executable = try container.decode(String.self, forKey: .executablePath)
|
||||||
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
|
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
|
||||||
self = .shellScript(executable: executable, parameters: parameters)
|
self = .shellScript(executable: executable, parameters: parameters)
|
||||||
|
|
||||||
case .some(.openUrl):
|
case .some(.openUrl):
|
||||||
let url = try container.decode(String.self, forKey: .url)
|
let url = try container.decode(String.self, forKey: .url)
|
||||||
self = .openUrl(url: url)
|
self = .openUrl(url: url)
|
||||||
|
|
||||||
case .none:
|
case .none:
|
||||||
self = .none
|
self = .none
|
||||||
}
|
}
|
||||||
@@ -284,17 +394,17 @@ enum LongActionType: Decodable {
|
|||||||
case none
|
case none
|
||||||
case hidKey(keycode: Int32)
|
case hidKey(keycode: Int32)
|
||||||
case keyPress(keycode: Int)
|
case keyPress(keycode: Int)
|
||||||
case appleSctipt(source: SourceProtocol)
|
case appleScript(source: SourceProtocol)
|
||||||
case shellScript(executable: String, parameters: [String])
|
case shellScript(executable: String, parameters: [String])
|
||||||
case custom(closure: ()->())
|
case custom(closure: ()->())
|
||||||
case openUrl(url: String)
|
case openUrl(url: String)
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case longAction
|
case longAction
|
||||||
case keycode
|
case longKeycode
|
||||||
case actionAppleScript
|
case longActionAppleScript
|
||||||
case executablePath
|
case longExecutablePath
|
||||||
case shellArguments
|
case longShellArguments
|
||||||
case longUrl
|
case longUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,23 +419,29 @@ enum LongActionType: Decodable {
|
|||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
let longType = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction)
|
let longType = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction)
|
||||||
|
|
||||||
switch longType {
|
switch longType {
|
||||||
case .some(.hidKey):
|
case .some(.hidKey):
|
||||||
let keycode = try container.decode(Int32.self, forKey: .keycode)
|
let keycode = try container.decode(Int32.self, forKey: .longKeycode)
|
||||||
self = .hidKey(keycode: keycode)
|
self = .hidKey(keycode: keycode)
|
||||||
|
|
||||||
case .some(.keyPress):
|
case .some(.keyPress):
|
||||||
let keycode = try container.decode(Int.self, forKey: .keycode)
|
let keycode = try container.decode(Int.self, forKey: .longKeycode)
|
||||||
self = .keyPress(keycode: keycode)
|
self = .keyPress(keycode: keycode)
|
||||||
|
|
||||||
case .some(.appleScript):
|
case .some(.appleScript):
|
||||||
let source = try container.decode(Source.self, forKey: .actionAppleScript)
|
let source = try container.decode(Source.self, forKey: .longActionAppleScript)
|
||||||
self = .appleSctipt(source: source)
|
self = .appleScript(source: source)
|
||||||
|
|
||||||
case .some(.shellScript):
|
case .some(.shellScript):
|
||||||
let executable = try container.decode(String.self, forKey: .executablePath)
|
let executable = try container.decode(String.self, forKey: .longExecutablePath)
|
||||||
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
|
let parameters = try container.decodeIfPresent([String].self, forKey: .longShellArguments) ?? []
|
||||||
self = .shellScript(executable: executable, parameters: parameters)
|
self = .shellScript(executable: executable, parameters: parameters)
|
||||||
|
|
||||||
case .some(.openUrl):
|
case .some(.openUrl):
|
||||||
let longUrl = try container.decode(String.self, forKey: .longUrl)
|
let longUrl = try container.decode(String.self, forKey: .longUrl)
|
||||||
self = .openUrl(url: longUrl)
|
self = .openUrl(url: longUrl)
|
||||||
|
|
||||||
case .none:
|
case .none:
|
||||||
self = .none
|
self = .none
|
||||||
}
|
}
|
||||||
@@ -339,6 +455,7 @@ enum GeneralParameter {
|
|||||||
case align(_: Align)
|
case align(_: Align)
|
||||||
case bordered(_: Bool)
|
case bordered(_: Bool)
|
||||||
case background(_:NSColor)
|
case background(_:NSColor)
|
||||||
|
case title(_:String)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GeneralParameters: Decodable {
|
struct GeneralParameters: Decodable {
|
||||||
@@ -350,32 +467,47 @@ struct GeneralParameters: Decodable {
|
|||||||
case align
|
case align
|
||||||
case bordered
|
case bordered
|
||||||
case background
|
case background
|
||||||
|
case title
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
var result: [GeneralParameters.CodingKeys: GeneralParameter] = [:]
|
var result: [GeneralParameters.CodingKeys: GeneralParameter] = [:]
|
||||||
|
|
||||||
if let value = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
|
if let value = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
|
||||||
result[.width] = .width(value)
|
result[.width] = .width(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let imageSource = try container.decodeIfPresent(Source.self, forKey: .image) {
|
if let imageSource = try container.decodeIfPresent(Source.self, forKey: .image) {
|
||||||
result[.image] = .image(source: imageSource)
|
result[.image] = .image(source: imageSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
if let borderedFlag = try container.decodeIfPresent(Bool.self, forKey: .bordered) {
|
||||||
result[.bordered] = .bordered(borderedFlag)
|
result[.bordered] = .bordered(borderedFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let backgroundColor = try container.decodeIfPresent(String.self, forKey: .background)?.hexColor {
|
if let backgroundColor = try container.decodeIfPresent(String.self, forKey: .background)?.hexColor {
|
||||||
result[.background] = .background(backgroundColor)
|
result[.background] = .background(backgroundColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let title = try container.decodeIfPresent(String.self, forKey: .title) {
|
||||||
|
result[.title] = .title(title)
|
||||||
|
}
|
||||||
|
|
||||||
parameters = result
|
parameters = result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol SourceProtocol {
|
protocol SourceProtocol {
|
||||||
var data: Data? { get }
|
var data: Data? { get }
|
||||||
var string: String? { get }
|
var string: String? { get }
|
||||||
var image: NSImage? { get }
|
var image: NSImage? { get }
|
||||||
|
var appleScript: NSAppleScript? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Source: Decodable, SourceProtocol {
|
struct Source: Decodable, SourceProtocol {
|
||||||
let filePath: String?
|
let filePath: String?
|
||||||
let base64: String?
|
let base64: String?
|
||||||
@@ -390,46 +522,69 @@ struct Source: Decodable, SourceProtocol {
|
|||||||
var data: Data? {
|
var data: Data? {
|
||||||
return base64?.base64Data ?? inline?.data(using: .utf8) ?? filePath?.fileData
|
return base64?.base64Data ?? inline?.data(using: .utf8) ?? filePath?.fileData
|
||||||
}
|
}
|
||||||
|
|
||||||
var string: String? {
|
var string: String? {
|
||||||
return inline ?? self.data?.utf8string
|
return inline ?? filePath?.fileString
|
||||||
}
|
}
|
||||||
|
|
||||||
var image: NSImage? {
|
var image: NSImage? {
|
||||||
return data?.image
|
return data?.image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var appleScript: NSAppleScript? {
|
||||||
|
return filePath?.fileURL.appleScript ?? self.string?.appleScript
|
||||||
|
}
|
||||||
|
|
||||||
private init(filePath: String?, base64: String?, inline: String?) {
|
private init(filePath: String?, base64: String?, inline: String?) {
|
||||||
self.filePath = filePath
|
self.filePath = filePath
|
||||||
self.base64 = base64
|
self.base64 = base64
|
||||||
self.inline = inline
|
self.inline = inline
|
||||||
}
|
}
|
||||||
|
|
||||||
init(filePath: String) {
|
init(filePath: String) {
|
||||||
self.init(filePath: filePath, base64: nil, inline: nil)
|
self.init(filePath: filePath, base64: nil, inline: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NSImage: SourceProtocol {
|
extension NSImage: SourceProtocol {
|
||||||
var data: Data? {
|
var data: Data? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var string: String? {
|
var string: String? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var image: NSImage? {
|
var image: NSImage? {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var appleScript: NSAppleScript? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
var base64Data: Data? {
|
var base64Data: Data? {
|
||||||
return Data(base64Encoded: self)
|
return Data(base64Encoded: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileData: Data? {
|
var fileData: Data? {
|
||||||
return try? Data(contentsOf: URL(fileURLWithPath: self))
|
return try? Data(contentsOf: URL(fileURLWithPath: self))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fileString: String? {
|
||||||
|
var encoding: String.Encoding = .utf8
|
||||||
|
return try? String(contentsOfFile: self, usedEncoding: &encoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extension Data {
|
extension Data {
|
||||||
var utf8string: String? {
|
var utf8string: String? {
|
||||||
return String(data: self, encoding: .utf8)
|
return String(data: self, encoding: .utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
var image: NSImage? {
|
var image: NSImage? {
|
||||||
return NSImage(data: self)?.resize(maxSize: NSSize(width: 24, height: 24))
|
return NSImage(data: self)?.resize(maxSize: NSSize(width: 24, height: 24))
|
||||||
}
|
}
|
||||||
@@ -440,3 +595,20 @@ enum Align: String, Decodable {
|
|||||||
case center
|
case center
|
||||||
case right
|
case right
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
var fileURL: URL {
|
||||||
|
return URL(fileURLWithPath: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
var appleScript: NSAppleScript? {
|
||||||
|
return NSAppleScript(source: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension URL {
|
||||||
|
var appleScript: NSAppleScript? {
|
||||||
|
guard FileManager.default.fileExists(atPath: self.path) else { return nil }
|
||||||
|
return NSAppleScript(contentsOf: self, error: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class ScrollViewItem: NSCustomTouchBarItem {
|
class ScrollViewItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
||||||
|
var twofingersPrev: CGFloat = 0.0
|
||||||
|
var threefingersPrev: CGFloat = 0.0
|
||||||
|
|
||||||
init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem]) {
|
init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem]) {
|
||||||
super.init(identifier: identifier)
|
super.init(identifier: identifier)
|
||||||
@@ -10,12 +12,64 @@ class ScrollViewItem: NSCustomTouchBarItem {
|
|||||||
stackView.orientation = .horizontal
|
stackView.orientation = .horizontal
|
||||||
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
|
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
|
||||||
scrollView.documentView = stackView
|
scrollView.documentView = stackView
|
||||||
// scrollView.documentView?.bounds.origin = CGPoint(x: 0.0, y: -2.5)
|
|
||||||
self.view = scrollView
|
self.view = scrollView
|
||||||
|
|
||||||
|
let twofingers = NSPanGestureRecognizer(target: self, action: #selector(twofingersHandler(_:)))
|
||||||
|
twofingers.allowedTouchTypes = .direct
|
||||||
|
twofingers.numberOfTouchesRequired = 2
|
||||||
|
self.view.addGestureRecognizer(twofingers)
|
||||||
|
|
||||||
|
let threefingers = NSPanGestureRecognizer(target: self, action: #selector(threefingersHandler(_:)))
|
||||||
|
threefingers.allowedTouchTypes = .direct
|
||||||
|
threefingers.numberOfTouchesRequired = 3
|
||||||
|
self.view.addGestureRecognizer(threefingers)
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func twofingersHandler(_ sender: NSGestureRecognizer?) { // Volume
|
||||||
|
let position = (sender?.location(in: sender?.view).x)!
|
||||||
|
|
||||||
|
switch sender!.state {
|
||||||
|
case .began:
|
||||||
|
twofingersPrev = position
|
||||||
|
case .changed:
|
||||||
|
if (((position-twofingersPrev) > 10) || ((twofingersPrev-position) > 10)) {
|
||||||
|
if position > twofingersPrev {
|
||||||
|
HIDPostAuxKey(NX_KEYTYPE_SOUND_UP)
|
||||||
|
} else if position < twofingersPrev {
|
||||||
|
HIDPostAuxKey(NX_KEYTYPE_SOUND_DOWN)
|
||||||
|
}
|
||||||
|
twofingersPrev = position
|
||||||
|
}
|
||||||
|
case .ended:
|
||||||
|
twofingersPrev = 0.0
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func threefingersHandler(_ sender: NSGestureRecognizer?) { // Brightness
|
||||||
|
let position = (sender?.location(in: sender?.view).x)!
|
||||||
|
|
||||||
|
switch sender!.state {
|
||||||
|
case .began:
|
||||||
|
threefingersPrev = position
|
||||||
|
case .changed:
|
||||||
|
if (((position-threefingersPrev) > 15) || ((threefingersPrev-position) > 15)) {
|
||||||
|
if position > threefingersPrev {
|
||||||
|
GenericKeyPress(keyCode: CGKeyCode(144)).send()
|
||||||
|
} else if position < threefingersPrev {
|
||||||
|
GenericKeyPress(keyCode: CGKeyCode(145)).send()
|
||||||
|
}
|
||||||
|
threefingersPrev = position
|
||||||
|
}
|
||||||
|
case .ended:
|
||||||
|
threefingersPrev = 0.0
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// ExtendNSTouchBar.swift
|
||||||
|
// MTMR
|
||||||
|
//
|
||||||
|
// Created by Anton Palgunov on 07/06/2018.
|
||||||
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
func presentSystemModal(_ touchBar: NSTouchBar!, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) -> Void {
|
||||||
|
if #available(OSX 10.14, *) {
|
||||||
|
NSTouchBar.presentSystemModalTouchBar(touchBar, systemTrayItemIdentifier: identifier)
|
||||||
|
} else {
|
||||||
|
NSTouchBar.presentSystemModalFunctionBar(touchBar, systemTrayItemIdentifier: identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func presentSystemModal(_ touchBar: NSTouchBar!, placement: Int64, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) -> Void {
|
||||||
|
if #available(OSX 10.14, *) {
|
||||||
|
NSTouchBar.presentSystemModalTouchBar(touchBar, placement: placement, systemTrayItemIdentifier: identifier)
|
||||||
|
} else {
|
||||||
|
NSTouchBar.presentSystemModalFunctionBar(touchBar, placement: placement, systemTrayItemIdentifier: identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func minimizeSystemModal(_ touchBar: NSTouchBar!) -> Void {
|
||||||
|
if #available(OSX 10.14, *) {
|
||||||
|
NSTouchBar.minimizeSystemModalTouchBar(touchBar)
|
||||||
|
} else {
|
||||||
|
NSTouchBar.minimizeSystemModalFunctionBar(touchBar)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,9 @@ struct ExactItem {
|
|||||||
let presetItem: BarItemDefinition
|
let presetItem: BarItemDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
||||||
|
let standardConfigPath = appSupportDirectory.appending("/items.json")
|
||||||
|
|
||||||
extension ItemType {
|
extension ItemType {
|
||||||
|
|
||||||
var identifierBase: String {
|
var identifierBase: String {
|
||||||
@@ -37,6 +40,14 @@ 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."
|
||||||
|
case .groupBar(items: _):
|
||||||
|
return "com.toxblh.mtmr.groupBar."
|
||||||
|
case .nightShift(items: _):
|
||||||
|
return "com.toxblh.mtmr.nightShift."
|
||||||
|
case .dnd(items: _):
|
||||||
|
return "com.toxblh.mtmr.dnd."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +63,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
|
|
||||||
var touchBar: NSTouchBar!
|
var touchBar: NSTouchBar!
|
||||||
|
|
||||||
|
fileprivate var lastPresetPath = ""
|
||||||
|
var jsonItems: [BarItemDefinition] = []
|
||||||
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
||||||
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
|
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
|
||||||
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
|
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||||
@@ -70,29 +83,51 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var touchbarNeedRefresh: Bool = true
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
createAndUpdatePreset()
|
SupportedTypesHolder.sharedInstance.register(typename: "close") { _ in
|
||||||
|
return (item: .staticButton(title: ""), action: .custom(closure: { [weak self] in
|
||||||
|
guard let `self` = self else { return }
|
||||||
|
self.reloadPreset(path: self.lastPresetPath)
|
||||||
|
}), longAction: .none, parameters: [.width: .width(30), .image: .image(source: (NSImage(named: .stopProgressFreestandingTemplate))!)])
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAndUpdatePreset(jsonItems: [BarItemDefinition]? = nil) {
|
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)
|
||||||
|
|
||||||
|
reloadStandardConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAndUpdatePreset(newJsonItems: [BarItemDefinition]) {
|
||||||
if let oldBar = self.touchBar {
|
if let oldBar = self.touchBar {
|
||||||
NSTouchBar.minimizeSystemModalFunctionBar(oldBar)
|
minimizeSystemModal(oldBar)
|
||||||
}
|
}
|
||||||
self.touchBar = NSTouchBar()
|
self.touchBar = NSTouchBar()
|
||||||
var jsonItems = jsonItems
|
self.jsonItems = newJsonItems
|
||||||
self.itemDefinitions = [:]
|
self.itemDefinitions = [:]
|
||||||
self.items = [:]
|
self.items = [:]
|
||||||
self.leftIdentifiers = []
|
self.leftIdentifiers = []
|
||||||
self.centerItems = []
|
self.centerItems = []
|
||||||
self.rightIdentifiers = []
|
self.rightIdentifiers = []
|
||||||
|
|
||||||
if (jsonItems == nil) {
|
loadItemDefinitions(jsonItems: self.jsonItems)
|
||||||
jsonItems = readConfig()
|
|
||||||
}
|
|
||||||
loadItemDefinitions(jsonItems: jsonItems!)
|
|
||||||
createItems()
|
createItems()
|
||||||
|
|
||||||
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||||
@@ -105,21 +140,40 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
func readConfig() -> [BarItemDefinition]? {
|
@objc func activeApplicationChanged(_ n: Notification) {
|
||||||
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
updateActiveApp()
|
||||||
let presetPath = appSupportDirectory.appending("/items.json")
|
}
|
||||||
|
|
||||||
|
func updateActiveApp() {
|
||||||
|
if self.blacklistAppIdentifiers.index(of: self.frontmostApplicationIdentifier!) != nil {
|
||||||
|
DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, false)
|
||||||
|
self.touchbarNeedRefresh = true
|
||||||
|
} else {
|
||||||
|
presentTouchBar()
|
||||||
|
self.touchbarNeedRefresh = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reloadStandardConfig() {
|
||||||
|
let presetPath = standardConfigPath
|
||||||
if !FileManager.default.fileExists(atPath: presetPath),
|
if !FileManager.default.fileExists(atPath: presetPath),
|
||||||
let defaultPreset = Bundle.main.path(forResource: "defaultPreset", ofType: "json") {
|
let defaultPreset = Bundle.main.path(forResource: "defaultPreset", ofType: "json") {
|
||||||
try? FileManager.default.createDirectory(atPath: appSupportDirectory, withIntermediateDirectories: true, attributes: nil)
|
try? FileManager.default.createDirectory(atPath: appSupportDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||||
try? FileManager.default.copyItem(atPath: defaultPreset, toPath: presetPath)
|
try? FileManager.default.copyItem(atPath: defaultPreset, toPath: presetPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
let jsonData = presetPath.fileData
|
reloadPreset(path: presetPath)
|
||||||
|
}
|
||||||
|
|
||||||
return jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
|
func reloadPreset(path: String) {
|
||||||
|
lastPresetPath = path
|
||||||
|
let items = path.fileData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
|
||||||
|
touchbarNeedRefresh = true
|
||||||
|
createAndUpdatePreset(newJsonItems: items)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
|
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
|
||||||
@@ -151,7 +205,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)
|
||||||
}
|
}
|
||||||
@@ -161,20 +215,23 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func presentTouchBar() {
|
@objc private func presentTouchBar() {
|
||||||
|
if touchbarNeedRefresh {
|
||||||
if self.controlStripState {
|
if self.controlStripState {
|
||||||
NSTouchBar.presentSystemModalFunctionBar(touchBar, systemTrayItemIdentifier: .controlStripItem)
|
presentSystemModal(touchBar, systemTrayItemIdentifier: .controlStripItem)
|
||||||
} else {
|
} else {
|
||||||
NSTouchBar.presentSystemModalFunctionBar(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
|
presentSystemModal(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func resetControlStrip() {
|
|
||||||
NSTouchBar.minimizeSystemModalFunctionBar(self.touchBar)
|
|
||||||
presentTouchBar()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func dismissTouchBar() {
|
@objc private func dismissTouchBar() {
|
||||||
NSTouchBar.minimizeSystemModalFunctionBar(touchBar)
|
self.touchbarNeedRefresh = true
|
||||||
|
minimizeSystemModal(touchBar)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func resetControlStrip() {
|
||||||
|
dismissTouchBar()
|
||||||
|
presentTouchBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
||||||
@@ -191,19 +248,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:
|
||||||
@@ -219,13 +274,27 @@ 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)
|
||||||
|
case .groupBar(items: let items):
|
||||||
|
barItem = GroupBarItem(identifier: identifier, items: items)
|
||||||
|
case .nightShift():
|
||||||
|
barItem = NightShiftBarItem(identifier: identifier)
|
||||||
|
case .dnd():
|
||||||
|
barItem = DnDBarItem(identifier: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
if case .bordered(let bordered)? = item.additionalParameters[.bordered], let item = barItem as? CustomButtonTouchBarItem {
|
||||||
item.isBordered = bordered
|
item.isBordered = bordered
|
||||||
}
|
}
|
||||||
@@ -236,38 +305,38 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
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.imageScaling = .scaleProportionallyDown
|
}
|
||||||
button.imagePosition = .imageLeading
|
if case .title(let value)? = item.additionalParameters[.title] {
|
||||||
|
if let item = barItem as? GroupBarItem {
|
||||||
if (item.title == "") {
|
item.collapsedRepresentationLabel = value
|
||||||
button.imagePosition = .imageOnly
|
} else if let item = barItem as? CustomButtonTouchBarItem {
|
||||||
|
item.title = value
|
||||||
}
|
}
|
||||||
|
|
||||||
button.imageHugsTitle = true
|
|
||||||
button.image = source.image
|
|
||||||
}
|
}
|
||||||
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) }
|
||||||
case .keyPress(keycode: let keycode):
|
case .keyPress(keycode: let keycode):
|
||||||
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
|
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
|
||||||
case .appleSctipt(source: let source):
|
case .appleScript(source: let source):
|
||||||
guard let appleScript = source.appleScript else {
|
guard let appleScript = source.appleScript else {
|
||||||
print("cannot create apple script for item \(item)")
|
print("cannot create apple script for item \(item)")
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
DispatchQueue.appleScriptQueue.async {
|
||||||
var error: NSDictionary?
|
var error: NSDictionary?
|
||||||
appleScript.executeAndReturnError(&error)
|
appleScript.executeAndReturnError(&error)
|
||||||
if let error = error {
|
if let error = error {
|
||||||
print("error \(error) when handling \(item) ")
|
print("error \(error) when handling \(item) ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case .shellScript(executable: let executable, parameters: let parameters):
|
case .shellScript(executable: let executable, parameters: let parameters):
|
||||||
return {
|
return {
|
||||||
let task = Process()
|
let task = Process()
|
||||||
@@ -288,18 +357,18 @@ 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) }
|
||||||
case .keyPress(keycode: let keycode):
|
case .keyPress(keycode: let keycode):
|
||||||
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
|
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
|
||||||
case .appleSctipt(source: let source):
|
case .appleScript(source: let source):
|
||||||
guard let appleScript = source.appleScript else {
|
guard let appleScript = source.appleScript else {
|
||||||
print("cannot create apple script for item \(item)")
|
print("cannot create apple script for item \(item)")
|
||||||
return {}
|
return {}
|
||||||
@@ -331,7 +400,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,8 +12,8 @@ import Foundation
|
|||||||
class BatteryBarItem: CustomButtonTouchBarItem {
|
class BatteryBarItem: CustomButtonTouchBarItem {
|
||||||
private let batteryInfo = BatteryInfo()
|
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: " ")
|
||||||
|
|
||||||
batteryInfo.start { [weak self] in
|
batteryInfo.start { [weak self] in
|
||||||
self?.refresh()
|
self?.refresh()
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ 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 = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||||
activity.interval = interval
|
activity.interval = interval
|
||||||
self.from = from
|
self.from = from
|
||||||
@@ -48,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
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
//
|
||||||
|
// DnDBarItem.swift
|
||||||
|
// MTMR
|
||||||
|
//
|
||||||
|
// Created by Anton Palgunov on 29/08/2018.
|
||||||
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class DnDBarItem : CustomButtonTouchBarItem {
|
||||||
|
private var timer: Timer!
|
||||||
|
|
||||||
|
init(identifier: NSTouchBarItem.Identifier) {
|
||||||
|
super.init(identifier: identifier, title: "")
|
||||||
|
self.isBordered = false
|
||||||
|
self.setWidth(value: 32)
|
||||||
|
|
||||||
|
self.tapClosure = { [weak self] in self?.DnDToggle() }
|
||||||
|
|
||||||
|
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||||
|
|
||||||
|
self.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func DnDToggle() {
|
||||||
|
DoNotDisturb.isEnabled = !DoNotDisturb.isEnabled
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func refresh() {
|
||||||
|
self.image = DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct DoNotDisturb {
|
||||||
|
private static let appId = "com.apple.notificationcenterui" as CFString
|
||||||
|
private static let dndPref = "com.apple.notificationcenterui.dndprefs_changed"
|
||||||
|
|
||||||
|
private static func set(_ key: String, value: CFPropertyList?) {
|
||||||
|
CFPreferencesSetValue(key as CFString, value, appId, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func commitChanges() {
|
||||||
|
CFPreferencesSynchronize(appId, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)
|
||||||
|
DistributedNotificationCenter.default().postNotificationName(NSNotification.Name(dndPref), object: nil, userInfo: nil, deliverImmediately: true)
|
||||||
|
NSRunningApplication.runningApplications(withBundleIdentifier: appId as String).first?.terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func enable() {
|
||||||
|
set("dndStart", value: nil)
|
||||||
|
set("dndEnd", value: nil)
|
||||||
|
set("doNotDisturb", value: true as CFPropertyList)
|
||||||
|
set("doNotDisturbDate", value: Date() as CFPropertyList)
|
||||||
|
commitChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func disable() {
|
||||||
|
set("dndStart", value: nil)
|
||||||
|
set("dndEnd", value: nil)
|
||||||
|
set("doNotDisturb", value: false as CFPropertyList)
|
||||||
|
set("doNotDisturbDate", value: nil)
|
||||||
|
commitChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
static var isEnabled:Bool {
|
||||||
|
get {
|
||||||
|
return CFPreferencesGetAppBooleanValue("doNotDisturb" as CFString, appId, nil)
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
newValue ? enable() : disable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
//
|
||||||
|
// GroupBarItem.swift
|
||||||
|
// MTMR
|
||||||
|
//
|
||||||
|
// Created by Daniel Apatin on 11.05.2018.
|
||||||
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
|
//
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
|
||||||
|
|
||||||
|
var jsonItems: [BarItemDefinition]
|
||||||
|
|
||||||
|
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
||||||
|
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
|
||||||
|
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||||
|
var centerIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||||
|
var centerItems: [NSTouchBarItem] = []
|
||||||
|
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||||
|
var scrollArea: NSCustomTouchBarItem?
|
||||||
|
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
||||||
|
|
||||||
|
init(identifier: NSTouchBarItem.Identifier, items: [BarItemDefinition]) {
|
||||||
|
jsonItems = items
|
||||||
|
super.init(identifier: identifier)
|
||||||
|
self.popoverTouchBar.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc override func showPopover(_ sender: Any?) {
|
||||||
|
self.itemDefinitions = [:]
|
||||||
|
self.items = [:]
|
||||||
|
self.leftIdentifiers = []
|
||||||
|
self.centerItems = []
|
||||||
|
self.rightIdentifiers = []
|
||||||
|
|
||||||
|
self.loadItemDefinitions(jsonItems: jsonItems)
|
||||||
|
self.createItems()
|
||||||
|
|
||||||
|
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||||
|
return items[identifier]
|
||||||
|
})
|
||||||
|
|
||||||
|
self.centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
||||||
|
self.scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
|
||||||
|
|
||||||
|
TouchBarController.shared.touchBar.delegate = self
|
||||||
|
TouchBarController.shared.touchBar.defaultItemIdentifiers = []
|
||||||
|
TouchBarController.shared.touchBar.defaultItemIdentifiers = self.leftIdentifiers + [centerScrollArea] + self.rightIdentifiers
|
||||||
|
|
||||||
|
if TouchBarController.shared.controlStripState {
|
||||||
|
presentSystemModal(TouchBarController.shared.touchBar, systemTrayItemIdentifier: .controlStripItem)
|
||||||
|
} else {
|
||||||
|
presentSystemModal(TouchBarController.shared.touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
||||||
|
if identifier == centerScrollArea {
|
||||||
|
return self.scrollArea
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let item = self.items[identifier],
|
||||||
|
let definition = self.itemDefinitions[identifier],
|
||||||
|
definition.align != .center else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
dateFormatter.dateFormat = "HH-mm-ss"
|
||||||
|
let time = dateFormatter.string(from: Date())
|
||||||
|
for item in jsonItems {
|
||||||
|
let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString)
|
||||||
|
let identifier = NSTouchBarItem.Identifier(identifierString)
|
||||||
|
itemDefinitions[identifier] = item
|
||||||
|
if item.align == .left {
|
||||||
|
leftIdentifiers.append(identifier)
|
||||||
|
}
|
||||||
|
if item.align == .right {
|
||||||
|
rightIdentifiers.append(identifier)
|
||||||
|
}
|
||||||
|
if item.align == .center {
|
||||||
|
centerIdentifiers.append(identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createItems() {
|
||||||
|
for (identifier, definition) in self.itemDefinitions {
|
||||||
|
self.items[identifier] = TouchBarController.shared.createItem(forIdentifier: identifier, definition: definition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,18 +13,15 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
|
|||||||
fileprivate var notificationCenter: CFNotificationCenter
|
fileprivate var notificationCenter: CFNotificationCenter
|
||||||
let buttonSize = NSSize(width: 21, height: 21)
|
let buttonSize = NSSize(width: 21, height: 21)
|
||||||
|
|
||||||
init(identifier: NSTouchBarItem.Identifier, 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.cell?.action = #selector(switchInputSource)
|
self?.switchInputSource()
|
||||||
self.button.action = #selector(switchInputSource)
|
}
|
||||||
|
|
||||||
self.button.frame.size = buttonSize
|
|
||||||
self.button.bounds.size = buttonSize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@@ -35,29 +32,21 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
|
|||||||
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()));
|
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc override func handleGestureSingle(gr: NSClickGestureRecognizer) {
|
|
||||||
super.handleGestureSingle(gr: gr)
|
|
||||||
switchInputSource()
|
|
||||||
}
|
|
||||||
|
|
||||||
@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.cell?.image = iconImage
|
iconImage.size = buttonSize
|
||||||
self.button.cell?.image?.size = buttonSize
|
self.image = iconImage
|
||||||
self.title = ""
|
self.title = ""
|
||||||
} else {
|
} else {
|
||||||
self.title = currentSource.name
|
self.title = currentSource.name
|
||||||
|
|||||||
@@ -0,0 +1,381 @@
|
|||||||
|
//
|
||||||
|
// 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() }
|
||||||
|
|
||||||
|
self.refreshAndSchedule()
|
||||||
|
}
|
||||||
|
|
||||||
|
@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!()
|
||||||
|
updatePlayer()
|
||||||
|
return
|
||||||
|
} else if (musicPlayer.className == "ITunesApplication") {
|
||||||
|
let mp = (musicPlayer as iTunesApplication)
|
||||||
|
mp.nextTrack!()
|
||||||
|
updatePlayer()
|
||||||
|
return
|
||||||
|
} else if (musicPlayer.className == "VOXApplication") {
|
||||||
|
let mp = (musicPlayer as VoxApplication)
|
||||||
|
mp.next!()
|
||||||
|
updatePlayer()
|
||||||
|
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)
|
||||||
|
updatePlayer()
|
||||||
|
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)
|
||||||
|
updatePlayer()
|
||||||
|
return
|
||||||
|
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
|
||||||
|
_ = safariApplication.doJavaScript!("document.getElementsByClassName('ytp-next-button')[0].click()", in: tab)
|
||||||
|
updatePlayer()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshAndSchedule() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.updatePlayer()
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + self.interval) { [weak self] in
|
||||||
|
self?.refreshAndSchedule()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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 = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// NightShiftBarItem.swift
|
||||||
|
// MTMR
|
||||||
|
//
|
||||||
|
// Created by Anton Palgunov on 28/08/2018.
|
||||||
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class NightShiftBarItem: CustomButtonTouchBarItem {
|
||||||
|
private let nsclient = CBBlueLightClient()
|
||||||
|
private var timer: Timer!
|
||||||
|
|
||||||
|
private var blueLightStatus: Status {
|
||||||
|
var status: Status = Status()
|
||||||
|
nsclient.getBlueLightStatus(&status)
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isNightShiftEnabled: Bool {
|
||||||
|
return self.blueLightStatus.enabled.boolValue
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setNightShift(state: Bool) {
|
||||||
|
self.nsclient.setEnabled(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(identifier: NSTouchBarItem.Identifier) {
|
||||||
|
super.init(identifier: identifier, title: "")
|
||||||
|
self.isBordered = false
|
||||||
|
self.setWidth(value: 28)
|
||||||
|
|
||||||
|
self.tapClosure = { [weak self] in self?.nightShiftAction() }
|
||||||
|
|
||||||
|
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||||
|
|
||||||
|
self.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func nightShiftAction() {
|
||||||
|
self.setNightShift(state: !self.isNightShiftEnabled)
|
||||||
|
self.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func refresh() {
|
||||||
|
self.image = isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,14 +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
|
||||||
button.bezelColor = .clear
|
|
||||||
updateTime()
|
updateTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ 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 = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||||
activity.interval = interval
|
activity.interval = interval
|
||||||
self.units = units
|
self.units = units
|
||||||
@@ -42,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 {
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
# My TouchBar. My rules
|
# My TouchBar. My rules
|
||||||
|
|
||||||
|
*The TouchBar Customization App for your MacBook Pro*
|
||||||
|
|
||||||
[](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)  
|
[](https://github.com/Toxblh/MTMR/blob/master/LICENSE) [](https://github.com/Toxblh/MTMR/releases/latest)  
|
||||||
|
|
||||||
@@ -11,6 +14,18 @@
|
|||||||
<img src="Resources/TouchBar-v0.8.1.png">
|
<img src="Resources/TouchBar-v0.8.1.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
**MTMR** Community:
|
||||||
|
[<img height="24px" src="https://github.com/discourse/DiscourseMobile/raw/master/icon.png" /> Discourse](https://forum.mtmr.app)
|
||||||
|
[<img height="24px" src="https://telegram.org/img/t_logo.png" /> Telegram](https://t.me/joinchat/AmVYGg8vW38c13_3MxdE_g)
|
||||||
|
|
||||||
|
|
||||||
|
#### Help to purchase Apple Developer License ($99)
|
||||||
|
<a href="https://www.buymeacoffee.com/toxblh" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" height="32px" ></a>
|
||||||
|
<a href="https://www.patreon.com/bePatron?u=9900748"><img height="32px" src="https://c5.patreon.com/external/logo/become_a_patron_button.png" srcset="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png 2x"></a>
|
||||||
|
[<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/854859-e299e139ab7f79855c0bc589f10b0ec6-medium_jpg.jpg?buster=1453074480" height="32px" /> Become a backer](https://opencollective.com/MTMR#backer)
|
||||||
|
[<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/854859-e299e139ab7f79855c0bc589f10b0ec6-medium_jpg.jpg?buster=1453074480" height="32px" /> Become a sponsor](https://opencollective.com/MTMR#sponsor)
|
||||||
|
|
||||||
|
|
||||||
My the idea is to create the program like a platform for plugins for customization TouchBar. I very like BTT and a full custom TouchBar (my [BTT preset](https://github.com/Toxblh/btt-touchbar-preset)). And I want to create it. And it's my the first Swift project for MacOS :)
|
My the idea is to create the program like a platform for plugins for customization TouchBar. I very like BTT and a full custom TouchBar (my [BTT preset](https://github.com/Toxblh/btt-touchbar-preset)). And I want to create it. And it's my the first Swift project for MacOS :)
|
||||||
|
|
||||||
### Roadmap
|
### Roadmap
|
||||||
@@ -36,7 +51,7 @@ My the idea is to create the program like a platform for plugins for customizati
|
|||||||
|
|
||||||
Settings:
|
Settings:
|
||||||
- [ ] Interface for plugins and export like presets
|
- [ ] Interface for plugins and export like presets
|
||||||
- [ ] Startup at login
|
- [x] Startup at login
|
||||||
- [ ] Show on/off in Dock
|
- [ ] Show on/off in Dock
|
||||||
- [ ] Show on/off in StatusBar
|
- [ ] Show on/off in StatusBar
|
||||||
- [ ] On/off Haptic Feedback
|
- [ ] On/off Haptic Feedback
|
||||||
@@ -59,16 +74,21 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
|||||||
- exitTouchbar
|
- exitTouchbar
|
||||||
- brightnessUp
|
- brightnessUp
|
||||||
- brightnessDown
|
- brightnessDown
|
||||||
|
- illuminationUp (keyboard illumination)
|
||||||
|
- illuminationDown (keyboard illumination)
|
||||||
- volumeDown
|
- volumeDown
|
||||||
- volumeUp
|
- volumeUp
|
||||||
- mute
|
- mute
|
||||||
- dock
|
- dock (half-long click to open app, full-long click to kill app)
|
||||||
|
- nightShift
|
||||||
|
- dnd (Dont disturb)
|
||||||
|
|
||||||
> Native Plugins
|
> Native Plugins
|
||||||
- battery
|
- battery
|
||||||
- currency
|
- currency
|
||||||
- weather
|
- weather
|
||||||
- inputsource
|
- inputsource
|
||||||
|
- music (tap for pause, longTap for next)
|
||||||
|
|
||||||
> Media Keys
|
> Media Keys
|
||||||
- previous
|
- previous
|
||||||
@@ -79,6 +99,10 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
|||||||
- sleep
|
- sleep
|
||||||
- displaySleep
|
- displaySleep
|
||||||
|
|
||||||
|
## Gestures on central part:
|
||||||
|
- two finger slide: change you Volume
|
||||||
|
- three finger slide: change you Brightness
|
||||||
|
|
||||||
## Built-in slider types:
|
## Built-in slider types:
|
||||||
|
|
||||||
- brightness
|
- brightness
|
||||||
@@ -110,6 +134,17 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
|||||||
"formatTemplate": "HH:mm" //optional
|
"formatTemplate": "HH:mm" //optional
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Groups
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
"type": "group",
|
||||||
|
"align": "center",
|
||||||
|
"bordered": true,
|
||||||
|
"title": "stats",
|
||||||
|
"items": [{ button }, {button}, ...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Native plugins
|
## Native plugins
|
||||||
- `weather`
|
- `weather`
|
||||||
> Provider: https://openweathermap.org Need allowance location service
|
> Provider: https://openweathermap.org Need allowance location service
|
||||||
@@ -131,6 +166,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
|
||||||
@@ -168,6 +214,20 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
|||||||
"url": "https://google.com",
|
"url": "https://google.com",
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## LongActions
|
||||||
|
This then you want to use longPress for some operations is will the same values like for Actions but different additional parameters, example:
|
||||||
|
```js
|
||||||
|
"longAction": "hidKey",
|
||||||
|
"longKeycode": 53,
|
||||||
|
```
|
||||||
|
|
||||||
|
- longAction
|
||||||
|
- longKeycode
|
||||||
|
- longActionAppleScript
|
||||||
|
- longExecutablePath
|
||||||
|
- longShellArguments
|
||||||
|
- longUrl
|
||||||
|
|
||||||
## Additional parameters:
|
## 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
|
||||||
@@ -244,8 +304,13 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
|||||||
|
|
||||||
[@ReDetection preset](Resources/ReDetection.json)
|
[@ReDetection preset](Resources/ReDetection.json)
|
||||||
|
|
||||||
|
### User's presets
|
||||||
|
|
||||||
|
[@luongvo209 preset](Resources/luongvo209.json)
|
||||||
|

|
||||||
|
|
||||||
## 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).
|
||||||
|
|
||||||
|
[](https://github.com/igrigorik/ga-beacon)
|
||||||
|
|||||||
|
After Width: | Height: | Size: 62 KiB |
@@ -3,3 +3,4 @@
|
|||||||
* try view controllers on `NSCustomTouchBarItem` instead of subclassing item itself
|
* try view controllers on `NSCustomTouchBarItem` instead of subclassing item itself
|
||||||
* try move away from enums when parse preset – enums are hard to extend
|
* try move away from enums when parse preset – enums are hard to extend
|
||||||
* find better way to hide bar items
|
* find better way to hide bar items
|
||||||
|
* extract bar items creating from TouchBarController to separate class, cover with tests
|
||||||
|
|||||||