Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b22514969 | ||
|
|
6b2e06b967 | ||
|
|
4663eb9b27 | ||
|
|
71ea94590b | ||
|
|
893e9f6982 | ||
|
|
7a3c4811ac | ||
|
|
0ce5a49172 | ||
|
|
9db9df5641 | ||
|
|
69b3f9d238 | ||
|
|
3d7bcd2d47 | ||
|
|
a37bc94588 | ||
|
|
91ceea564e | ||
|
|
ee49e94c73 | ||
|
|
2f796cc07a | ||
|
|
b2b495b551 | ||
|
|
818c32b0ed | ||
|
|
d15764d41d | ||
|
|
9d343a31d3 | ||
|
|
0956b4ebcd | ||
|
|
9ff68a6948 | ||
|
|
ac8a05d2e5 | ||
|
|
75ea408370 | ||
|
|
8760185ae7 | ||
|
|
bd0ffbefe7 | ||
|
|
783ed34c0f | ||
|
|
37c2e8523c | ||
|
|
3f2dad52ee | ||
|
|
d6b0c0313b | ||
|
|
78509fb84d | ||
|
|
877c4268fd | ||
|
|
0e07f71458 | ||
|
|
7100a6e6b8 | ||
|
|
347bad8058 | ||
|
|
d72fb0ef7a | ||
|
|
ccc3a2269b | ||
|
|
6d6048c49c | ||
|
|
055c5d77ef | ||
|
|
f64c705c0d | ||
|
|
ca2ae30873 | ||
|
|
00fae08da0 | ||
|
|
f09b9155c4 | ||
|
|
9ef185d214 | ||
|
|
12f9220e34 | ||
|
|
ba753ff331 | ||
|
|
aba1aea334 | ||
|
|
849b5b6db0 | ||
|
|
621cb3242b | ||
|
|
dd0ecf047d | ||
|
|
bbc3d9dcfe | ||
|
|
85785018f4 | ||
|
|
7b9f894bf3 | ||
|
|
3a57293206 | ||
|
|
9afa9d7470 | ||
|
|
a86f7eb304 | ||
|
|
5013262740 | ||
|
|
106f297f57 | ||
|
|
115ea1372c | ||
|
|
f45aad4fad | ||
|
|
e131f92fb5 | ||
|
|
69a8d81bce | ||
|
|
7789407766 | ||
|
|
6c331619d0 | ||
|
|
9393a4e484 | ||
|
|
4b480dddc3 | ||
|
|
a6f27a1fef | ||
|
|
79eba1a281 | ||
|
|
2ad89bd8c8 | ||
|
|
ec990e8147 | ||
|
|
16d8380830 | ||
|
|
9e43a3b561 | ||
|
|
080bc67c54 | ||
|
|
81131ec390 | ||
|
|
cd6054af03 | ||
|
|
eee33ee645 | ||
|
|
046f0fc1cf | ||
|
|
4b7788fff7 | ||
|
|
ea5fbf0ee8 | ||
|
|
d0cfa45b5d | ||
|
|
ed14f34a2d | ||
|
|
98d5a6eb0e | ||
|
|
b492b89d2f | ||
|
|
ff58329691 | ||
|
|
dd3ab9ac6f | ||
|
|
f08ce7a381 | ||
|
|
11c8d167c0 | ||
|
|
0ba5984217 | ||
|
|
1584cf979c | ||
|
|
753cfd8a31 | ||
|
|
34b51b2dd8 | ||
|
|
5bda77d0d5 | ||
|
|
2bc8912add | ||
|
|
b70c8ca4a3 | ||
|
|
2750d00317 | ||
|
|
4fb7cb9b99 | ||
|
|
651ddbef5c | ||
|
|
fe5705627f | ||
|
|
c0401ce83d | ||
|
|
1c45ca13a3 | ||
|
|
970fb99e33 | ||
|
|
46f35f4737 | ||
|
|
713f4dffc7 | ||
|
|
d0efdef8c2 | ||
|
|
65e3d67c32 | ||
|
|
e56a05657e | ||
|
|
4a84532c56 | ||
|
|
435439ecd2 | ||
|
|
bb0c81ea76 | ||
|
|
c022018ec4 | ||
|
|
e10eed17ad | ||
|
|
17e2362ebf | ||
|
|
dd97c706f9 | ||
|
|
002af40756 | ||
|
|
285fb1fb85 | ||
|
|
1e07deedae | ||
|
|
a86d31b0ac | ||
|
|
0a0b393fb7 | ||
|
|
af83fa8a72 | ||
|
|
ab4aef4e2a | ||
|
|
e3f5da0294 | ||
|
|
aaa7bc33bc | ||
|
|
74d57cdc7d | ||
|
|
d48d590462 | ||
|
|
b3f461db45 | ||
|
|
ee0eab6a76 | ||
|
|
2dc1148a2a | ||
|
|
d12bdd2bd4 | ||
|
|
92b6e5ef33 | ||
|
|
d97d8efdd4 | ||
|
|
9f6f7d9404 | ||
|
|
528506f276 | ||
|
|
ce7c3c5883 | ||
|
|
3d3ad0f076 |
@@ -1,3 +1,5 @@
|
||||
|
||||
.DS_Store
|
||||
# Xcode
|
||||
#
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
@@ -5,6 +7,7 @@
|
||||
## Build generated
|
||||
build/
|
||||
DerivedData/
|
||||
Release/
|
||||
|
||||
## Various settings
|
||||
*.pbxuser
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Anton Palgunov
|
||||
|
||||
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.
|
||||
@@ -7,19 +7,39 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368EDDE620812A1D00E10953 /* ScrollViewItem.swift */; };
|
||||
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */; };
|
||||
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */; };
|
||||
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
|
||||
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */; };
|
||||
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
|
||||
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */ = {isa = PBXBuildFile; fileRef = 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */; };
|
||||
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */; };
|
||||
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
|
||||
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */; };
|
||||
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */; };
|
||||
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; };
|
||||
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B05600D22083E9BB00EB218D /* CustomSlider.swift */; };
|
||||
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D621205E03F5006E6B86 /* TouchBarController.swift */; };
|
||||
B059D624205E04F3006E6B86 /* TouchBarItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* TouchBarItems.swift */; };
|
||||
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; };
|
||||
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; };
|
||||
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B252205C7D8000BC04DC /* AppDelegate.swift */; };
|
||||
B082B255205C7D8000BC04DC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B254205C7D8000BC04DC /* ViewController.swift */; };
|
||||
B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B082B256205C7D8000BC04DC /* Assets.xcassets */; };
|
||||
B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B082B258205C7D8000BC04DC /* Main.storyboard */; };
|
||||
B082B266205C7D8000BC04DC /* MTMRTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B265205C7D8000BC04DC /* MTMRTests.swift */; };
|
||||
B082B271205C7D8000BC04DC /* MTMRUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B270205C7D8000BC04DC /* MTMRUITests.swift */; };
|
||||
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */; };
|
||||
B09EB1E6207C0F8E00D5C1E0 /* MultitouchSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */; };
|
||||
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */; };
|
||||
B0C1CFCA205C97D30021C862 /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C1CFC9205C97D30021C862 /* WindowController.swift */; };
|
||||
B0B17431207D6B590004B740 /* PlaySmart.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17427207D6B580004B740 /* PlaySmart.scpt */; };
|
||||
B0B17432207D6B590004B740 /* Weather.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17428207D6B580004B740 /* Weather.scpt */; };
|
||||
B0B17433207D6B590004B740 /* Finder.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17429207D6B580004B740 /* Finder.scpt */; };
|
||||
B0B17434207D6B590004B740 /* Battery.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742A207D6B580004B740 /* Battery.scpt */; };
|
||||
B0B17435207D6B590004B740 /* Spotify.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742B207D6B590004B740 /* Spotify.next.scpt */; };
|
||||
B0B17436207D6B590004B740 /* iTunes.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742C207D6B590004B740 /* iTunes.next.scpt */; };
|
||||
B0B17437207D6B590004B740 /* Spotify.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742D207D6B590004B740 /* Spotify.nowPlaying.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 */; };
|
||||
B0B1743A207D6B590004B740 /* iTunes.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */; };
|
||||
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */; };
|
||||
B0F8771D207AD35400D6E430 /* battery.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0F8771C207AD35400D6E430 /* battery.scpt */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -30,39 +50,52 @@
|
||||
remoteGlobalIDString = B082B24E205C7D8000BC04DC;
|
||||
remoteInfo = MTMR;
|
||||
};
|
||||
B082B26D205C7D8000BC04DC /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = B082B247205C7D8000BC04DC /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = B082B24E205C7D8000BC04DC;
|
||||
remoteInfo = MTMR;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewItem.swift; 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>"; };
|
||||
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeTouchBarItem.swift; sourceTree = "<group>"; };
|
||||
36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptTouchBarItem.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>"; };
|
||||
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = defaultPreset.json; 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>"; };
|
||||
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>"; };
|
||||
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = DeprecatedCarbonAPI.c; sourceTree = "<group>"; };
|
||||
B0008E542080286C003AD4DD /* SupportHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportHelpers.swift; sourceTree = "<group>"; };
|
||||
B05600D22083E9BB00EB218D /* CustomSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSlider.swift; sourceTree = "<group>"; };
|
||||
B059D621205E03F5006E6B86 /* TouchBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarController.swift; sourceTree = "<group>"; };
|
||||
B059D623205E04F3006E6B86 /* TouchBarItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarItems.swift; sourceTree = "<group>"; };
|
||||
B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButtonTouchBarItem.swift; 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>"; };
|
||||
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DFRFoundation.framework; path = ../../../../../System/Library/PrivateFrameworks/DFRFoundation.framework; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
B082B254205C7D8000BC04DC /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
B082B256205C7D8000BC04DC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
B082B259205C7D8000BC04DC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
B082B25B205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
B082B25C205C7D8000BC04DC /* MTMR.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MTMR.entitlements; sourceTree = "<group>"; };
|
||||
B082B261205C7D8000BC04DC /* MTMRTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MTMRTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B082B265205C7D8000BC04DC /* MTMRTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTMRTests.swift; sourceTree = "<group>"; };
|
||||
B082B267205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
B082B26C205C7D8000BC04DC /* MTMRUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MTMRUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B082B270205C7D8000BC04DC /* MTMRUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTMRUITests.swift; sourceTree = "<group>"; };
|
||||
B082B272205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticFeedback.swift; sourceTree = "<group>"; };
|
||||
B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = ../../../../../System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = "<group>"; };
|
||||
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPress.swift; sourceTree = "<group>"; };
|
||||
B0C1CFC9205C97D30021C862 /* WindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowController.swift; sourceTree = "<group>"; };
|
||||
B0B17427207D6B580004B740 /* PlaySmart.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PlaySmart.scpt; sourceTree = "<group>"; };
|
||||
B0B17428207D6B580004B740 /* Weather.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Weather.scpt; sourceTree = "<group>"; };
|
||||
B0B17429207D6B580004B740 /* Finder.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Finder.scpt; sourceTree = "<group>"; };
|
||||
B0B1742A207D6B580004B740 /* Battery.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Battery.scpt; sourceTree = "<group>"; };
|
||||
B0B1742B207D6B590004B740 /* Spotify.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Spotify.next.scpt; sourceTree = "<group>"; };
|
||||
B0B1742C207D6B590004B740 /* iTunes.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iTunes.next.scpt; sourceTree = "<group>"; };
|
||||
B0B1742D207D6B590004B740 /* Spotify.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Spotify.nowPlaying.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>"; };
|
||||
B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iTunes.nowPlaying.scpt; 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>"; };
|
||||
B0F8771C207AD35400D6E430 /* battery.scpt */ = {isa = PBXFileReference; lastKnownFileType = text; path = battery.scpt; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -71,6 +104,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */,
|
||||
B09EB1E6207C0F8E00D5C1E0 /* MultitouchSupport.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -81,19 +115,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B082B269205C7D8000BC04DC /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
B059D62B205F11E8006E6B86 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */,
|
||||
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
@@ -102,9 +130,10 @@
|
||||
B082B246205C7D8000BC04DC = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36C2ECD2207B3B1D003CDA33 /* README.md */,
|
||||
36BDC22F207CDA8600FCFEBE /* TECHNICAL_DEBT.md */,
|
||||
B082B251205C7D8000BC04DC /* MTMR */,
|
||||
B082B264205C7D8000BC04DC /* MTMRTests */,
|
||||
B082B26F205C7D8000BC04DC /* MTMRUITests */,
|
||||
B082B250205C7D8000BC04DC /* Products */,
|
||||
B059D62B205F11E8006E6B86 /* Frameworks */,
|
||||
);
|
||||
@@ -115,7 +144,6 @@
|
||||
children = (
|
||||
B082B24F205C7D8000BC04DC /* MTMR.app */,
|
||||
B082B261205C7D8000BC04DC /* MTMRTests.xctest */,
|
||||
B082B26C205C7D8000BC04DC /* MTMRUITests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -123,21 +151,27 @@
|
||||
B082B251205C7D8000BC04DC /* MTMR */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */,
|
||||
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */,
|
||||
B0B1743B207D6ED40004B740 /* CBridge */,
|
||||
B0B17426207D6AFE0004B740 /* AppleScripts */,
|
||||
B082B252205C7D8000BC04DC /* AppDelegate.swift */,
|
||||
B082B254205C7D8000BC04DC /* ViewController.swift */,
|
||||
B082B256205C7D8000BC04DC /* Assets.xcassets */,
|
||||
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */,
|
||||
B059D623205E04F3006E6B86 /* TouchBarItems.swift */,
|
||||
B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */,
|
||||
36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */,
|
||||
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
|
||||
B059D621205E03F5006E6B86 /* TouchBarController.swift */,
|
||||
B0C1CFC9205C97D30021C862 /* WindowController.swift */,
|
||||
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */,
|
||||
B0008E542080286C003AD4DD /* SupportHelpers.swift */,
|
||||
B082B258205C7D8000BC04DC /* Main.storyboard */,
|
||||
B082B25B205C7D8000BC04DC /* Info.plist */,
|
||||
B082B25C205C7D8000BC04DC /* MTMR.entitlements */,
|
||||
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */,
|
||||
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */,
|
||||
B0F8771C207AD35400D6E430 /* battery.scpt */,
|
||||
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */,
|
||||
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */,
|
||||
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
|
||||
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
|
||||
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */,
|
||||
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
|
||||
B05600D22083E9BB00EB218D /* CustomSlider.swift */,
|
||||
);
|
||||
path = MTMR;
|
||||
sourceTree = "<group>";
|
||||
@@ -145,19 +179,40 @@
|
||||
B082B264205C7D8000BC04DC /* MTMRTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B082B265205C7D8000BC04DC /* MTMRTests.swift */,
|
||||
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */,
|
||||
B082B267205C7D8000BC04DC /* Info.plist */,
|
||||
);
|
||||
path = MTMRTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B082B26F205C7D8000BC04DC /* MTMRUITests */ = {
|
||||
B0B17426207D6AFE0004B740 /* AppleScripts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B082B270205C7D8000BC04DC /* MTMRUITests.swift */,
|
||||
B082B272205C7D8000BC04DC /* Info.plist */,
|
||||
B0B1742A207D6B580004B740 /* Battery.scpt */,
|
||||
B0B17429207D6B580004B740 /* Finder.scpt */,
|
||||
B0B1742C207D6B590004B740 /* iTunes.next.scpt */,
|
||||
B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */,
|
||||
B0B17427207D6B580004B740 /* PlaySmart.scpt */,
|
||||
B0B1742B207D6B590004B740 /* Spotify.next.scpt */,
|
||||
B0B1742D207D6B590004B740 /* Spotify.nowPlaying.scpt */,
|
||||
B0B1742E207D6B590004B740 /* Vox.next.scpt */,
|
||||
B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */,
|
||||
B0B17428207D6B580004B740 /* Weather.scpt */,
|
||||
);
|
||||
path = MTMRUITests;
|
||||
path = AppleScripts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B0B1743B207D6ED40004B740 /* CBridge */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */,
|
||||
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */,
|
||||
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */,
|
||||
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */,
|
||||
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */,
|
||||
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */,
|
||||
);
|
||||
path = CBridge;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
@@ -198,24 +253,6 @@
|
||||
productReference = B082B261205C7D8000BC04DC /* MTMRTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
B082B26B205C7D8000BC04DC /* MTMRUITests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B082B27B205C7D8000BC04DC /* Build configuration list for PBXNativeTarget "MTMRUITests" */;
|
||||
buildPhases = (
|
||||
B082B268205C7D8000BC04DC /* Sources */,
|
||||
B082B269205C7D8000BC04DC /* Frameworks */,
|
||||
B082B26A205C7D8000BC04DC /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
B082B26E205C7D8000BC04DC /* PBXTargetDependency */,
|
||||
);
|
||||
name = MTMRUITests;
|
||||
productName = MTMRUITests;
|
||||
productReference = B082B26C205C7D8000BC04DC /* MTMRUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -228,7 +265,7 @@
|
||||
TargetAttributes = {
|
||||
B082B24E205C7D8000BC04DC = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
ProvisioningStyle = Automatic;
|
||||
ProvisioningStyle = Manual;
|
||||
SystemCapabilities = {
|
||||
com.apple.Sandbox = {
|
||||
enabled = 0;
|
||||
@@ -237,13 +274,6 @@
|
||||
};
|
||||
B082B260205C7D8000BC04DC = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
ProvisioningStyle = Automatic;
|
||||
TestTargetID = B082B24E205C7D8000BC04DC;
|
||||
};
|
||||
B082B26B205C7D8000BC04DC = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
ProvisioningStyle = Automatic;
|
||||
TestTargetID = B082B24E205C7D8000BC04DC;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -262,7 +292,6 @@
|
||||
targets = (
|
||||
B082B24E205C7D8000BC04DC /* MTMR */,
|
||||
B082B260205C7D8000BC04DC /* MTMRTests */,
|
||||
B082B26B205C7D8000BC04DC /* MTMRUITests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -272,9 +301,19 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B0B17434207D6B590004B740 /* Battery.scpt in Resources */,
|
||||
B0B1743A207D6B590004B740 /* iTunes.nowPlaying.scpt in Resources */,
|
||||
B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */,
|
||||
B0B17437207D6B590004B740 /* Spotify.nowPlaying.scpt in Resources */,
|
||||
B0B17436207D6B590004B740 /* iTunes.next.scpt in Resources */,
|
||||
B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */,
|
||||
B0F8771D207AD35400D6E430 /* battery.scpt in Resources */,
|
||||
B0B17435207D6B590004B740 /* Spotify.next.scpt in Resources */,
|
||||
B0B17433207D6B590004B740 /* Finder.scpt in Resources */,
|
||||
B0B17439207D6B590004B740 /* Vox.nowPlaying.scpt in Resources */,
|
||||
B0B17438207D6B590004B740 /* Vox.next.scpt in Resources */,
|
||||
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */,
|
||||
B0B17432207D6B590004B740 /* Weather.scpt in Resources */,
|
||||
B0B17431207D6B590004B740 /* PlaySmart.scpt in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -285,13 +324,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B082B26A205C7D8000BC04DC /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -300,12 +332,21 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */,
|
||||
B082B255205C7D8000BC04DC /* ViewController.swift in Sources */,
|
||||
B0C1CFCA205C97D30021C862 /* WindowController.swift in Sources */,
|
||||
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */,
|
||||
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */,
|
||||
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */,
|
||||
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */,
|
||||
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */,
|
||||
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
|
||||
B059D624205E04F3006E6B86 /* TouchBarItems.swift in Sources */,
|
||||
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
|
||||
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
|
||||
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
|
||||
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
|
||||
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */,
|
||||
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */,
|
||||
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */,
|
||||
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */,
|
||||
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -313,15 +354,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B082B266205C7D8000BC04DC /* MTMRTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B082B268205C7D8000BC04DC /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B082B271205C7D8000BC04DC /* MTMRUITests.swift in Sources */,
|
||||
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */,
|
||||
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -333,11 +367,6 @@
|
||||
target = B082B24E205C7D8000BC04DC /* MTMR */;
|
||||
targetProxy = B082B262205C7D8000BC04DC /* PBXContainerItemProxy */;
|
||||
};
|
||||
B082B26E205C7D8000BC04DC /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = B082B24E205C7D8000BC04DC /* MTMR */;
|
||||
targetProxy = B082B26D205C7D8000BC04DC /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@@ -383,7 +412,6 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -402,7 +430,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12.2;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
@@ -455,7 +483,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12.2;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
@@ -466,9 +494,8 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = D6D8BR2QNB;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
|
||||
@@ -477,7 +504,8 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMR;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/TouchBarPrivateApi-Bridging.h";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/CBridge/TouchBarPrivateApi-Bridging.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -486,7 +514,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = D6D8BR2QNB;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@@ -497,7 +525,8 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMR;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/TouchBarPrivateApi-Bridging.h";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/CBridge/TouchBarPrivateApi-Bridging.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Release;
|
||||
@@ -506,16 +535,12 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = D6D8BR2QNB;
|
||||
INFOPLIST_FILE = MTMRTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MTMR.app/Contents/MacOS/MTMR";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -523,48 +548,14 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = D6D8BR2QNB;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = MTMRTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MTMR.app/Contents/MacOS/MTMR";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B082B27C205C7D8000BC04DC /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = D6D8BR2QNB;
|
||||
INFOPLIST_FILE = MTMRUITests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TEST_TARGET_NAME = MTMR;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
B082B27D205C7D8000BC04DC /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = D6D8BR2QNB;
|
||||
INFOPLIST_FILE = MTMRUITests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TEST_TARGET_NAME = MTMR;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -598,15 +589,6 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
B082B27B205C7D8000BC04DC /* Build configuration list for PBXNativeTarget "MTMRUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
B082B27C205C7D8000BC04DC /* Debug */,
|
||||
B082B27D205C7D8000BC04DC /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = B082B247205C7D8000BC04DC /* Project object */;
|
||||
|
||||
@@ -10,18 +10,43 @@ import Cocoa
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
|
||||
|
||||
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
TouchBarController.shared.setupControlStripPresence()
|
||||
// Insert code here to initialize your application
|
||||
TouchBarController.shared.setupControlStripPresence()
|
||||
|
||||
if let button = statusItem.button {
|
||||
button.image = #imageLiteral(resourceName: "StatusImage")
|
||||
}
|
||||
createMenu()
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
// Insert code here to tear down your application
|
||||
}
|
||||
|
||||
|
||||
@objc func openPrefereces(_ sender: Any?) {
|
||||
let task = Process()
|
||||
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
||||
let presetPath = appSupportDirectory.appending("/items.json")
|
||||
task.launchPath = "/usr/bin/open"
|
||||
task.arguments = [presetPath]
|
||||
task.launch()
|
||||
}
|
||||
|
||||
@objc func updatePreset(_ sender: Any?) {
|
||||
TouchBarController.shared.createAndUpdatePreset()
|
||||
}
|
||||
|
||||
func createMenu() {
|
||||
let menu = NSMenu()
|
||||
menu.addItem(withTitle: "Preferences", action: #selector(openPrefereces(_:)), keyEquivalent: ",")
|
||||
menu.addItem(withTitle: "Reload Preset", action: #selector(updatePreset(_:)), keyEquivalent: "r")
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
|
||||
statusItem.menu = menu
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
//
|
||||
// AppScrubberTouchBarItem.swift
|
||||
//
|
||||
// This file is part of TouchDock
|
||||
// Copyright (C) 2017 Xander Deng
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrubberDataSource {
|
||||
|
||||
var scrubber: NSScrubber!
|
||||
|
||||
var runningApplications: [NSRunningApplication] = []
|
||||
|
||||
override init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier)
|
||||
|
||||
scrubber = NSScrubber();
|
||||
scrubber.delegate = self
|
||||
scrubber.dataSource = self
|
||||
scrubber.mode = .free // .fixed
|
||||
let layout = NSScrubberFlowLayout();
|
||||
layout.itemSize = NSSize(width: 44, height: 30)
|
||||
scrubber.scrubberLayout = layout
|
||||
scrubber.selectionBackgroundStyle = .roundedBackground
|
||||
|
||||
view = scrubber
|
||||
|
||||
scrubber.register(NSScrubberImageItemView.self, forItemIdentifier: NSUserInterfaceItemIdentifier(rawValue: "ScrubberApplicationsItemReuseIdentifier"))
|
||||
|
||||
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)
|
||||
|
||||
updateRunningApplication()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func activeApplicationChanged(n: Notification) {
|
||||
updateRunningApplication()
|
||||
}
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
updateRunningApplication()
|
||||
}
|
||||
|
||||
func updateRunningApplication() {
|
||||
let newApplications = launchedApplications().filter {
|
||||
!$0.isTerminated && $0.bundleIdentifier != nil
|
||||
}
|
||||
let frontmost = NSWorkspace.shared.frontmostApplication
|
||||
let index = newApplications.index {
|
||||
$0.processIdentifier == frontmost?.processIdentifier
|
||||
}
|
||||
|
||||
runningApplications = newApplications
|
||||
scrubber.reloadData()
|
||||
|
||||
scrubber.selectedIndex = index ?? 0
|
||||
}
|
||||
|
||||
public func numberOfItems(for scrubber: NSScrubber) -> Int {
|
||||
return runningApplications.count
|
||||
}
|
||||
|
||||
public func scrubber(_ scrubber: NSScrubber, viewForItemAt index: Int) -> NSScrubberItemView {
|
||||
let item = scrubber.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "ScrubberApplicationsItemReuseIdentifier"), owner: self) as? NSScrubberImageItemView ?? NSScrubberImageItemView()
|
||||
item.imageView.imageScaling = .scaleProportionallyDown
|
||||
if let icon = runningApplications[index].icon {
|
||||
item.image = icon
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
public func didFinishInteracting(with scrubber: NSScrubber) {
|
||||
runningApplications[scrubber.selectedIndex].activate(options: [ .activateIgnoringOtherApps ])
|
||||
|
||||
// NB: if you can't open app which on another space, try to check mark
|
||||
// "When switching to an application, switch to a Space with open windows for the application"
|
||||
// in Mission control settings
|
||||
|
||||
// TODO: deminiaturize app
|
||||
// if let info = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as? [[ String : Any]] {
|
||||
// for dict in info {
|
||||
// if dict["kCGWindowOwnerName"] as! String == runningApplications[scrubber.selectedIndex].localizedName {
|
||||
// print(dict["kCGWindowNumber"], dict["kCGWindowOwnerName"])
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
private func launchedApplications() -> [NSRunningApplication] {
|
||||
let asns = _LSCopyApplicationArrayInFrontToBackOrder(~0)?.takeRetainedValue()
|
||||
return (0..<CFArrayGetCount(asns)).compactMap { index in
|
||||
let asn = CFArrayGetValueAtIndex(asns, index)
|
||||
let pid = pidFromASN(asn)
|
||||
return NSRunningApplication(processIdentifier: pid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import Foundation
|
||||
|
||||
class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
private var script: NSAppleScript!
|
||||
private let interval: TimeInterval
|
||||
private var forceHideConstraint: NSLayoutConstraint!
|
||||
|
||||
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, onTap: @escaping ()->()) {
|
||||
self.interval = interval
|
||||
super.init(identifier: identifier, title: "compile", onTap: onTap)
|
||||
self.forceHideConstraint = self.view.widthAnchor.constraint(equalToConstant: 0)
|
||||
guard let script = source.appleScript else {
|
||||
button.title = "no script"
|
||||
return
|
||||
}
|
||||
self.script = script
|
||||
button.bezelColor = .clear
|
||||
DispatchQueue.main.async {
|
||||
var error: NSDictionary?
|
||||
guard script.compileAndReturnError(&error) else {
|
||||
print(error?.description ?? "unknown error")
|
||||
DispatchQueue.main.async {
|
||||
self.button.title = "compile error"
|
||||
}
|
||||
return
|
||||
}
|
||||
self.refreshAndSchedule()
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func refreshAndSchedule() {
|
||||
print("refresh happened")
|
||||
let scriptResult = self.execute()
|
||||
DispatchQueue.main.async {
|
||||
self.button.title = scriptResult
|
||||
self.forceHideConstraint.isActive = scriptResult == ""
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + self.interval) { [weak self] in
|
||||
self?.refreshAndSchedule()
|
||||
}
|
||||
}
|
||||
|
||||
func execute() -> String {
|
||||
var error: NSDictionary?
|
||||
let output = script.executeAndReturnError(&error)
|
||||
if let error = error {
|
||||
print(error)
|
||||
return "error"
|
||||
}
|
||||
return output.stringValue ?? ""
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SourceProtocol {
|
||||
var appleScript: NSAppleScript? {
|
||||
guard let source = self.string else { return nil }
|
||||
return NSAppleScript(source: source)
|
||||
}
|
||||
}
|
||||
@@ -3,27 +3,27 @@ set percent to word 6 of theList's item 2
|
||||
set charge to word 7 of theList's item 2
|
||||
|
||||
if (charge = "charging") then
|
||||
set iconC to "⚡️"
|
||||
set iconC to "⚡️"
|
||||
else
|
||||
set iconC to ""
|
||||
set iconC to ""
|
||||
end if
|
||||
|
||||
set remainingRaw to my split(theList's item 2, " ")
|
||||
set remainingTime to remainingRaw's item 5
|
||||
|
||||
if (remainingTime = "(no") then
|
||||
set strTime to " (?)"
|
||||
set strTime to " (?)"
|
||||
else if (remainingTime = "0:00") then
|
||||
set strTime to ""
|
||||
set strTime to ""
|
||||
else
|
||||
set strTime to " (" & remainingTime & ")"
|
||||
set strTime to " (" & remainingTime & ")"
|
||||
end if
|
||||
|
||||
return iconC & percent & "%" & strTime
|
||||
|
||||
to split(someText, delimiter)
|
||||
set AppleScript's text item delimiters to delimiter
|
||||
set someText to someText's text items
|
||||
set AppleScript's text item delimiters to {""}
|
||||
return someText
|
||||
set AppleScript's text item delimiters to delimiter
|
||||
set someText to someText's text items
|
||||
set AppleScript's text item delimiters to {""}
|
||||
return someText
|
||||
end split
|
||||
@@ -0,0 +1,5 @@
|
||||
tell application "Finder"
|
||||
make new Finder window
|
||||
set target of front window to path to home folder as string
|
||||
activate
|
||||
end tell
|
||||
@@ -0,0 +1,11 @@
|
||||
if application "iTunes" is running then
|
||||
tell application "iTunes" to playpause
|
||||
end if
|
||||
|
||||
if application "Spotify" is running then
|
||||
tell application "Spotify" to playpause
|
||||
end if
|
||||
|
||||
if application "VOX" is running then
|
||||
tell application "VOX" to playpause
|
||||
end if
|
||||
@@ -0,0 +1,7 @@
|
||||
if application "Spotify" is running then
|
||||
tell application "Spotify"
|
||||
if player state is playing then
|
||||
next track
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
@@ -0,0 +1,10 @@
|
||||
if application "Spotify" is running then
|
||||
tell application "Spotify"
|
||||
if player state is playing then
|
||||
return (get artist of current track) & " – " & (get name of current track)
|
||||
else
|
||||
return ""
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
return ""
|
||||
@@ -0,0 +1,7 @@
|
||||
if application "VOX" is running then
|
||||
tell application "VOX"
|
||||
if player state is 1 then
|
||||
next
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
@@ -0,0 +1,10 @@
|
||||
if application "VOX" is running then
|
||||
tell application "VOX"
|
||||
if player state is 1 then
|
||||
return (get artist) & " – " & (get track)
|
||||
else
|
||||
return ""
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
return ""
|
||||
@@ -0,0 +1,32 @@
|
||||
# This script requires two libs. Download them:
|
||||
# https://itunes.apple.com/ru/app/json-helper-for-applescript/id453114608?l=en&mt=12
|
||||
# https://itunes.apple.com/ru/app/location-helper-for-applescript/id488536386?mt=12
|
||||
tell application "Location Helper"
|
||||
set clocation_coords to get location coordinates
|
||||
tell application "JSON Helper"
|
||||
set weather to fetch JSON from "http://api.openweathermap.org/data/2.5/weather?lat=" & item 1 of clocation_coords & "&lon=" & item 2 of clocation_coords & "&units=metric&appid=32c4256d09a4c52b38aecddba7a078f6"
|
||||
set temp to temp of main of weather as string
|
||||
set cond_icon to icon of item 1 of weather of weather as string
|
||||
if cond_icon is in ["01d", "01n"] then
|
||||
set cond to "☀️"
|
||||
else if cond_icon is in ["02d", "02n"] then
|
||||
set cond to "⛅️"
|
||||
else if cond_icon is in ["03d", "03n", "04d", "04n"] then
|
||||
set cond to "☁️"
|
||||
else if cond_icon is in ["09d", "09n"] then
|
||||
set cond to "🌧"
|
||||
else if cond_icon is in ["10d", "10n"] then
|
||||
set cond to "🌦"
|
||||
else if cond_icon is in ["11d", "11n"] then
|
||||
set cond to "🌩"
|
||||
else if cond_icon is in ["13d", "13n"] then
|
||||
set cond to "❄️"
|
||||
else if cond_icon is in ["50d", "50n"] then
|
||||
set cond to "🌫"
|
||||
else
|
||||
set cond to ""
|
||||
end if
|
||||
set temp_round to round (temp * 1.0)
|
||||
return cond & " " & temp_round & "°C"
|
||||
end tell
|
||||
end tell
|
||||
@@ -0,0 +1,7 @@
|
||||
if application "iTunes" is running then
|
||||
tell application "iTunes"
|
||||
if player state is playing then
|
||||
next track
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
@@ -0,0 +1,10 @@
|
||||
if application "iTunes" is running then
|
||||
tell application "iTunes"
|
||||
if player state is playing then
|
||||
return (get artist of current track) & " – " & (get name of current track)
|
||||
else
|
||||
return ""
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
return ""
|
||||
@@ -1,53 +1,63 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "logo-16.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "logo-32.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "logo-32.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "logo-64.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "logo-128.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "logo-256.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "logo-256.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "logo-512.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "logo-512.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "logo-1024.png",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
|
||||
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "StatusImage.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "brightnessDown.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "brightnessUp.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.2 KiB |
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Application-->
|
||||
@@ -680,50 +680,5 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="75" y="0.0"/>
|
||||
</scene>
|
||||
<!--Window Controller-->
|
||||
<scene sceneID="R2V-B0-nI4">
|
||||
<objects>
|
||||
<windowController id="B8D-0N-5wS" customClass="WindowController" customModule="MTMR" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1028"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
|
||||
</connections>
|
||||
</window>
|
||||
<connections>
|
||||
<segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
|
||||
</connections>
|
||||
</windowController>
|
||||
<customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="75" y="250"/>
|
||||
</scene>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="hIz-AP-VOD">
|
||||
<objects>
|
||||
<viewController id="XfG-lQ-9wD" customClass="ViewController" customModule="MTMR" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" wantsLayer="YES" id="m2S-Jp-Qdl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="178" height="57"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bso-ZY-Qqn">
|
||||
<rect key="frame" x="18" y="20" width="142" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="My TouchBar. My rules" id="cmP-Ef-Jrj">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
</view>
|
||||
</viewController>
|
||||
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="74" y="613"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import Cocoa
|
||||
import AppKit
|
||||
import AVFoundation
|
||||
import CoreAudio
|
||||
|
||||
class BrightnessViewController: NSCustomTouchBarItem {
|
||||
private(set) var sliderItem: CustomSlider!
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, refreshInterval: Double, image: NSImage? = nil) {
|
||||
super.init(identifier: identifier)
|
||||
|
||||
if (image == nil) {
|
||||
sliderItem = CustomSlider()
|
||||
} else {
|
||||
sliderItem = CustomSlider(knob: image!)
|
||||
}
|
||||
sliderItem.target = self
|
||||
sliderItem.action = #selector(BrightnessViewController.sliderValueChanged(_:))
|
||||
sliderItem.minValue = 0.0
|
||||
sliderItem.maxValue = 100.0
|
||||
sliderItem.floatValue = getBrightness()*100
|
||||
|
||||
self.view = sliderItem
|
||||
|
||||
let timer = Timer.scheduledTimer(timeInterval: refreshInterval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true)
|
||||
RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
sliderItem.unbind(NSBindingName.value)
|
||||
}
|
||||
|
||||
@objc func updateBrightnessSlider() {
|
||||
DispatchQueue.main.async {
|
||||
self.sliderItem.floatValue = self.getBrightness() * 100
|
||||
}
|
||||
}
|
||||
|
||||
@objc func sliderValueChanged(_ sender: Any) {
|
||||
if let sliderItem = sender as? NSSlider {
|
||||
setBrightness(level: Float32(sliderItem.intValue)/100.0)
|
||||
}
|
||||
}
|
||||
|
||||
private func getBrightness() -> Float32 {
|
||||
var level: Float32 = 0.5
|
||||
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
|
||||
|
||||
IODisplayGetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, &level)
|
||||
return level
|
||||
}
|
||||
|
||||
private func setBrightness(level: Float) {
|
||||
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
|
||||
|
||||
IODisplaySetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, level)
|
||||
IOObjectRelease(service)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// DeprecatedCarbonAPI.c
|
||||
//
|
||||
// This file is part of TouchDock
|
||||
// Copyright (C) 2017 Xander Deng
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#include "DeprecatedCarbonAPI.h"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
CFStringRef kPidKey = CFSTR("pid");
|
||||
|
||||
pid_t pidFromASN(void const *asn) {
|
||||
pid_t pid = -1;
|
||||
ProcessSerialNumber psn = {kNoProcess, kNoProcess};
|
||||
if (CFGetTypeID(asn) == _LSASNGetTypeID()) {
|
||||
_LSASNExtractHighAndLowParts(asn, &psn.highLongOfPSN, &psn.lowLongOfPSN);
|
||||
CFDictionaryRef processInfo = ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask);
|
||||
if (processInfo) {
|
||||
CFNumberRef pidNumber = CFDictionaryGetValue(processInfo, kPidKey);
|
||||
if (pidNumber) {
|
||||
CFNumberGetValue(pidNumber, kCFNumberSInt32Type, &pid);
|
||||
}
|
||||
CFRelease(processInfo);
|
||||
}
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// DeprecatedCarbonAPI.h
|
||||
//
|
||||
// This file is part of TouchDock
|
||||
// Copyright (C) 2017 Xander Deng
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#import <Carbon/Carbon.h>
|
||||
|
||||
extern CFArrayRef _LSCopyApplicationArrayInFrontToBackOrder(uint32_t sessionID);
|
||||
extern void _LSASNExtractHighAndLowParts(void const* asn, UInt32* psnHigh, UInt32* psnLow);
|
||||
extern CFTypeID _LSASNGetTypeID(void);
|
||||
|
||||
pid_t pidFromASN(void const *asn);
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// TouchBarPrivateApi-Bridging.h
|
||||
// MTMR
|
||||
//
|
||||
// Created by Anton Palgunov on 18/03/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TouchBarPrivateApi.h"
|
||||
#import "TouchBarSupport.h"
|
||||
#import "DeprecatedCarbonAPI.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
CF_EXPORT CFTypeRef MTActuatorCreateFromDeviceID(UInt64 deviceID);
|
||||
CF_EXPORT IOReturn MTActuatorOpen(CFTypeRef actuatorRef);
|
||||
CF_EXPORT IOReturn MTActuatorClose(CFTypeRef actuatorRef);
|
||||
CF_EXPORT IOReturn MTActuatorActuate(CFTypeRef actuatorRef, SInt32 actuationID, UInt32 arg1, Float32 arg2, Float32 arg3);
|
||||
CF_EXPORT bool MTActuatorIsOpen(CFTypeRef actuatorRef);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// TouchBarItems.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Anton Palgunov on 18/03/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class CustomButtonTouchBarItem: NSCustomTouchBarItem {
|
||||
let tapClosure: () -> ()
|
||||
private(set) var button: NSButton!
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, title: String, onTap callback: @escaping () -> ()) {
|
||||
self.tapClosure = callback
|
||||
super.init(identifier: identifier)
|
||||
button = NSButton(title: title, target: self, action: #selector(didTapped))
|
||||
button.title = title
|
||||
self.view = button
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func didTapped() {
|
||||
self.tapClosure()
|
||||
let hf: HapticFeedback = HapticFeedback()
|
||||
hf.tap(strong: 6)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// CustomSlider.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Anton Palgunov on 15/04/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class CustomSliderCell: NSSliderCell {
|
||||
var knobImage:NSImage!
|
||||
private var _currentKnobRect:NSRect!
|
||||
private var _barRect:NSRect!
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
init(knob:NSImage?) {
|
||||
knobImage = knob;
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func drawKnob(_ knobRect: NSRect) {
|
||||
if (knobImage == nil) {
|
||||
super.drawKnob(knobRect)
|
||||
return;
|
||||
}
|
||||
|
||||
_currentKnobRect = knobRect;
|
||||
drawBar(inside: _barRect, flipped: true)
|
||||
|
||||
let x = (knobRect.origin.x * (_barRect.size.width - (knobImage.size.width - knobRect.size.width)) / _barRect.size.width)+1;
|
||||
let y = knobRect.origin.y+3
|
||||
|
||||
knobImage.draw(
|
||||
at: NSPoint(x: x, y: y),
|
||||
from: NSZeroRect,
|
||||
operation: NSCompositingOperation.sourceOver,
|
||||
fraction: 1
|
||||
)
|
||||
}
|
||||
|
||||
override func drawBar(inside aRect: NSRect, flipped: Bool) {
|
||||
_barRect = aRect
|
||||
|
||||
var rect = aRect
|
||||
rect.size.height = CGFloat(4)
|
||||
let barRadius = CGFloat(2)
|
||||
let value = CGFloat((self.doubleValue - self.minValue) / (self.maxValue - self.minValue))
|
||||
let finalWidth = CGFloat(value * (self.controlView!.frame.size.width - 12))
|
||||
|
||||
var leftRect = rect
|
||||
leftRect.size.width = finalWidth
|
||||
let bg = NSBezierPath(roundedRect: rect, xRadius: barRadius, yRadius: barRadius)
|
||||
NSColor.lightGray.setFill()
|
||||
bg.fill()
|
||||
|
||||
let active = NSBezierPath(roundedRect: leftRect, xRadius: barRadius, yRadius: barRadius)
|
||||
NSColor.darkGray.setFill()
|
||||
active.fill()
|
||||
}
|
||||
}
|
||||
|
||||
class CustomSlider:NSSlider {
|
||||
|
||||
var currentValue:CGFloat = 0
|
||||
|
||||
override func setNeedsDisplay(_ invalidRect: NSRect) {
|
||||
super.setNeedsDisplay(invalidRect)
|
||||
}
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
if ((self.cell?.isKind(of: CustomSliderCell.self)) == false) {
|
||||
let cell:CustomSliderCell = CustomSliderCell()
|
||||
self.cell = cell
|
||||
}
|
||||
}
|
||||
|
||||
convenience init(knob:NSImage) {
|
||||
self.init()
|
||||
self.cell = CustomSliderCell(knob: knob)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
override init(frame frameRect: NSRect) {
|
||||
super.init(frame: frameRect)
|
||||
}
|
||||
|
||||
func knobImage() -> NSImage {
|
||||
let cell = self.cell as! CustomSliderCell
|
||||
return cell.knobImage
|
||||
}
|
||||
|
||||
func setKnobImage(image:NSImage) {
|
||||
let cell = self.cell as! CustomSliderCell
|
||||
cell.knobImage = image
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// HapticFeedback.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Anton Palgunov on 09/04/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import IOKit
|
||||
|
||||
class HapticFeedback {
|
||||
private var actuatorRef: CFTypeRef?
|
||||
private var deviceID: UInt64 = 0x200000001000000
|
||||
private var error: IOReturn = 0
|
||||
|
||||
// Don't know how to do strong is enum one of
|
||||
// 1 like back Click
|
||||
// 2 like Click
|
||||
// 3 week
|
||||
// 4 medium
|
||||
// 5 week medium
|
||||
// 6 strong
|
||||
// 15 nothing
|
||||
// 16 nothing
|
||||
// you can get a plist `otool -s __TEXT __tpad_act_plist /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/Current/MultitouchSupport|tail -n +3|awk -F'\t' '{print $2}'|xxd -r -p`
|
||||
|
||||
func tap(strong:Int32) -> Void {
|
||||
actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue()
|
||||
|
||||
guard actuatorRef != nil else {
|
||||
print("guard actuatorRef == nil")
|
||||
return
|
||||
}
|
||||
|
||||
error = MTActuatorOpen(actuatorRef!)
|
||||
guard error == kIOReturnSuccess else {
|
||||
print("guard MTActuatorOpen")
|
||||
return
|
||||
}
|
||||
|
||||
error = MTActuatorActuate(actuatorRef!, strong, 0, 0.0, 0.0)
|
||||
guard error == kIOReturnSuccess else {
|
||||
print("guard MTActuatorActuate")
|
||||
return
|
||||
}
|
||||
|
||||
error = MTActuatorClose(actuatorRef!)
|
||||
guard error == kIOReturnSuccess else {
|
||||
print("guard MTActuatorClose")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>0.10</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2018 Anton Palgunov. All rights reserved.</string>
|
||||
<key>NSMainStoryboardFile</key>
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
extension Data {
|
||||
|
||||
func barItemDefinitions() -> [BarItemDefinition]? {
|
||||
return try? JSONDecoder().decode([BarItemDefinition].self, from: self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct BarItemDefinition: Decodable {
|
||||
let type: ItemType
|
||||
let action: ActionType
|
||||
let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
}
|
||||
|
||||
init(type: ItemType, action: ActionType, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) {
|
||||
self.type = type
|
||||
self.action = action
|
||||
self.additionalParameters = additionalParameters
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try container.decode(String.self, forKey: .type)
|
||||
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type)
|
||||
var additionalParameters = try GeneralParameters(from: decoder).parameters
|
||||
if let result = try? parametersDecoder(decoder),
|
||||
case let (itemType, action, parameters) = result {
|
||||
parameters.forEach { additionalParameters[$0] = $1 }
|
||||
self.init(type: itemType, action: action, additionalParameters: additionalParameters)
|
||||
} else {
|
||||
self.init(type: .staticButton(title: "unknown"), action: .none, additionalParameters: additionalParameters)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SupportedTypesHolder {
|
||||
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter])
|
||||
private var supportedTypes: [String: ParametersDecoder] = [
|
||||
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), parameters: [.align: .align(.left)]) },
|
||||
"brightnessUp": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
|
||||
return (item: .staticButton(title: ""), action: .keyPress(keycode: 113), parameters: [.image: imageParameter])
|
||||
},
|
||||
"brightnessDown": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
|
||||
return (item: .staticButton(title: ""), action: .keyPress(keycode: 107), parameters: [.image: imageParameter])
|
||||
},
|
||||
"volumeDown": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!)
|
||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), parameters: [.image: imageParameter])
|
||||
},
|
||||
"volumeUp": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeUpTemplate)!)
|
||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), parameters: [.image: imageParameter])
|
||||
},
|
||||
"previous": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarRewindTemplate)!)
|
||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), parameters: [.image: imageParameter])
|
||||
},
|
||||
"play": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarPlayPauseTemplate)!)
|
||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), parameters: [.image: imageParameter])
|
||||
},
|
||||
"next": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarFastForwardTemplate)!)
|
||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), parameters: [.image: imageParameter])
|
||||
},
|
||||
"weather": { 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)
|
||||
let scriptPath = Bundle.main.path(forResource: "Weather", ofType: "scpt")!
|
||||
let item = ItemType.appleScriptTitledButton(source: Source(filePath: scriptPath), refreshInterval: interval ?? 1800.0)
|
||||
return (item: item, action: .none, parameters: [:])
|
||||
},
|
||||
"dock": { decoder in
|
||||
return (item: .dock(), action: .none, parameters: [:])
|
||||
},
|
||||
"volume": { decoder in
|
||||
enum CodingKeys: String, CodingKey { case image }
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if var img = try container.decodeIfPresent(Source.self, forKey: .image) {
|
||||
return (item: .volume(), action: .none, parameters: [.image: .image(source: img)])
|
||||
} else {
|
||||
return (item: .volume(), action: .none, parameters: [:])
|
||||
}
|
||||
},
|
||||
"brightness": { decoder in
|
||||
enum CodingKeys: String, CodingKey { case refreshInterval; case image }
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||
if var img = try container.decodeIfPresent(Source.self, forKey: .image) {
|
||||
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, parameters: [.image: .image(source: img)])
|
||||
} else {
|
||||
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, parameters: [:])
|
||||
}
|
||||
},
|
||||
"battery": { 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)
|
||||
let scriptPath = Bundle.main.path(forResource: "Battery", ofType: "scpt")!
|
||||
let item = ItemType.appleScriptTitledButton(source: Source(filePath: scriptPath), refreshInterval: interval ?? 1800.0)
|
||||
return (item: item, action: .none, parameters: [:])
|
||||
},
|
||||
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), parameters: [:]) },
|
||||
"displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), parameters: [:]) },
|
||||
]
|
||||
|
||||
static let sharedInstance = SupportedTypesHolder()
|
||||
|
||||
func lookup(by type: String) -> ParametersDecoder {
|
||||
return supportedTypes[type] ?? { decoder in
|
||||
return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), parameters: [:])
|
||||
}
|
||||
}
|
||||
|
||||
func register(typename: String, decoder: @escaping ParametersDecoder) {
|
||||
supportedTypes[typename] = decoder
|
||||
}
|
||||
|
||||
func register(typename: String, item: ItemType, action: ActionType) {
|
||||
register(typename: typename) { _ in
|
||||
return (item: item, action: action, parameters: [:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ItemType: Decodable {
|
||||
case staticButton(title: String)
|
||||
case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double)
|
||||
case timeButton(formatTemplate: String)
|
||||
case dock()
|
||||
case volume()
|
||||
case brightness(refreshInterval: Double)
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case title
|
||||
case source
|
||||
case refreshInterval
|
||||
case formatTemplate
|
||||
case image
|
||||
}
|
||||
|
||||
enum ItemTypeRaw: String, Decodable {
|
||||
case staticButton
|
||||
case appleScriptTitledButton
|
||||
case timeButton
|
||||
case dock
|
||||
case volume
|
||||
case brightness
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try container.decode(ItemTypeRaw.self, forKey: .type)
|
||||
switch type {
|
||||
case .appleScriptTitledButton:
|
||||
let source = try container.decode(Source.self, forKey: .source)
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||
self = .appleScriptTitledButton(source: source, refreshInterval: interval)
|
||||
case .staticButton:
|
||||
let title = try container.decode(String.self, forKey: .title)
|
||||
self = .staticButton(title: title)
|
||||
case .timeButton:
|
||||
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
|
||||
self = .timeButton(formatTemplate: template)
|
||||
case .dock:
|
||||
self = .dock()
|
||||
case .volume:
|
||||
self = .volume()
|
||||
case .brightness:
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
|
||||
self = .brightness(refreshInterval: interval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ActionType: Decodable {
|
||||
case none
|
||||
case hidKey(keycode: Int)
|
||||
case keyPress(keycode: Int)
|
||||
case appleSctipt(source: SourceProtocol)
|
||||
case shellScript(executable: String, parameters: [String])
|
||||
case custom(closure: ()->())
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case action
|
||||
case keycode
|
||||
case actionAppleScript
|
||||
case executablePath
|
||||
case shellArguments
|
||||
}
|
||||
|
||||
private enum ActionTypeRaw: String, Decodable {
|
||||
case hidKey
|
||||
case keyPress
|
||||
case appleScript
|
||||
case shellScript
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
|
||||
switch type {
|
||||
case .some(.hidKey):
|
||||
let keycode = try container.decode(Int.self, forKey: .keycode)
|
||||
self = .hidKey(keycode: keycode)
|
||||
case .some(.keyPress):
|
||||
let keycode = try container.decode(Int.self, forKey: .keycode)
|
||||
self = .keyPress(keycode: keycode)
|
||||
case .some(.appleScript):
|
||||
let source = try container.decode(Source.self, forKey: .actionAppleScript)
|
||||
self = .appleSctipt(source: source)
|
||||
case .some(.shellScript):
|
||||
let executable = try container.decode(String.self, forKey: .executablePath)
|
||||
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
|
||||
self = .shellScript(executable: executable, parameters: parameters)
|
||||
case .none:
|
||||
self = .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ItemType: Equatable {}
|
||||
func ==(lhs: ItemType, rhs: ItemType) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case let (.staticButton(a), .staticButton(b)):
|
||||
return a == b
|
||||
case let (.appleScriptTitledButton(a, b), .appleScriptTitledButton(c, d)):
|
||||
return a == c && b == d
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension ActionType: Equatable {}
|
||||
func ==(lhs: ActionType, rhs: ActionType) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.none, .none):
|
||||
return true
|
||||
case let (.hidKey(a), .hidKey(b)),
|
||||
let (.keyPress(a), .keyPress(b)):
|
||||
return a == b
|
||||
case let (.appleSctipt(a), .appleSctipt(b)):
|
||||
return a == b
|
||||
case let (.shellScript(a, b), .shellScript(c, d)):
|
||||
return a == c && b == d
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
enum GeneralParameter {
|
||||
case width(_: CGFloat)
|
||||
case image(source: SourceProtocol)
|
||||
case align(_: Align)
|
||||
}
|
||||
|
||||
struct GeneralParameters: Decodable {
|
||||
let parameters: [GeneralParameters.CodingKeys: GeneralParameter]
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case width
|
||||
case image
|
||||
case align
|
||||
}
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
var result: [GeneralParameters.CodingKeys: GeneralParameter] = [:]
|
||||
if let value = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
|
||||
result[.width] = .width(value)
|
||||
}
|
||||
if let imageSource = try container.decodeIfPresent(Source.self, forKey: .image) {
|
||||
result[.image] = .image(source: imageSource)
|
||||
}
|
||||
let align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center
|
||||
result[.align] = .align(align)
|
||||
parameters = result
|
||||
}
|
||||
}
|
||||
protocol SourceProtocol {
|
||||
var data: Data? { get }
|
||||
var string: String? { get }
|
||||
var image: NSImage? { get }
|
||||
}
|
||||
struct Source: Decodable, SourceProtocol {
|
||||
let filePath: String?
|
||||
let base64: String?
|
||||
let inline: String?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case filePath
|
||||
case base64
|
||||
case inline
|
||||
}
|
||||
|
||||
var data: Data? {
|
||||
return base64?.base64Data ?? inline?.data(using: .utf8) ?? filePath?.fileData
|
||||
}
|
||||
var string: String? {
|
||||
return inline ?? self.data?.utf8string
|
||||
}
|
||||
var image: NSImage? {
|
||||
return data?.image
|
||||
}
|
||||
|
||||
private init(filePath: String?, base64: String?, inline: String?) {
|
||||
self.filePath = filePath
|
||||
self.base64 = base64
|
||||
self.inline = inline
|
||||
}
|
||||
init(filePath: String) {
|
||||
self.init(filePath: filePath, base64: nil, inline: nil)
|
||||
}
|
||||
}
|
||||
extension NSImage: SourceProtocol {
|
||||
var data: Data? {
|
||||
return nil
|
||||
}
|
||||
var string: String? {
|
||||
return nil
|
||||
}
|
||||
var image: NSImage? {
|
||||
return self
|
||||
}
|
||||
}
|
||||
extension SourceProtocol where Self: Equatable {}
|
||||
func ==(left: SourceProtocol, right: SourceProtocol) -> Bool {
|
||||
return left.data == right.data
|
||||
}
|
||||
|
||||
extension String {
|
||||
var base64Data: Data? {
|
||||
return Data(base64Encoded: self)
|
||||
}
|
||||
var fileData: Data? {
|
||||
return try? Data(contentsOf: URL(fileURLWithPath: self))
|
||||
}
|
||||
}
|
||||
extension Data {
|
||||
var utf8string: String? {
|
||||
return String(data: self, encoding: .utf8)
|
||||
}
|
||||
var image: NSImage? {
|
||||
return NSImage(data: self)?.resize(maxSize: NSSize(width: 24, height: 24))
|
||||
}
|
||||
}
|
||||
|
||||
enum Align: String, Decodable {
|
||||
case left
|
||||
case center
|
||||
case right
|
||||
}
|
||||
@@ -13,6 +13,10 @@ protocol KeyPress {
|
||||
func send()
|
||||
}
|
||||
|
||||
struct GenericKeyPress: KeyPress {
|
||||
var keyCode: CGKeyCode
|
||||
}
|
||||
|
||||
extension KeyPress {
|
||||
func send () {
|
||||
let src = CGEventSource(stateID: .hidSystemState)
|
||||
@@ -25,19 +29,6 @@ extension KeyPress {
|
||||
}
|
||||
}
|
||||
|
||||
struct ESCKeyPress: KeyPress {
|
||||
let keyCode: CGKeyCode = 53
|
||||
}
|
||||
|
||||
struct BrightnessUpPress: KeyPress {
|
||||
let keyCode: CGKeyCode = 107
|
||||
}
|
||||
|
||||
struct BrightnessDownPress: KeyPress {
|
||||
let keyCode: CGKeyCode = 113
|
||||
}
|
||||
|
||||
|
||||
func doKey(_ key: Int, down: Bool) {
|
||||
let flags = NSEvent.ModifierFlags(rawValue: down ? 0xa00 : 0xb00)
|
||||
let data1 = (key << 16) | ((down ? 0xa : 0xb) << 8)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import Foundation
|
||||
|
||||
class ScrollViewItem: NSCustomTouchBarItem {
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem]) {
|
||||
super.init(identifier: identifier)
|
||||
let views = items.compactMap { $0.view }
|
||||
let stackView = NSStackView(views: views)
|
||||
stackView.spacing = 1
|
||||
stackView.orientation = .horizontal
|
||||
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
|
||||
scrollView.documentView = stackView
|
||||
self.view = scrollView
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// SupportHelpers.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Anton Palgunov on 13/04/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
func trim() -> String {
|
||||
return self.trimmingCharacters(in: NSCharacterSet.whitespaces)
|
||||
}
|
||||
}
|
||||
|
||||
extension NSImage {
|
||||
func resize(maxSize:NSSize) -> NSImage {
|
||||
var ratio:Float = 0.0
|
||||
let imageWidth = Float(self.size.width)
|
||||
let imageHeight = Float(self.size.height)
|
||||
let maxWidth = Float(maxSize.width)
|
||||
let maxHeight = Float(maxSize.height)
|
||||
|
||||
// Get ratio (landscape or portrait)
|
||||
if (imageWidth > imageHeight) {
|
||||
// Landscape
|
||||
ratio = maxWidth / imageWidth;
|
||||
}
|
||||
else {
|
||||
// Portrait
|
||||
ratio = maxHeight / imageHeight;
|
||||
}
|
||||
|
||||
// Calculate new size based on the ratio
|
||||
let newWidth = imageWidth * ratio
|
||||
let newHeight = imageHeight * ratio
|
||||
|
||||
// Create a new NSSize object with the newly calculated size
|
||||
let newSize:NSSize = NSSize(width: Int(newWidth), height: Int(newHeight))
|
||||
|
||||
// Cast the NSImage to a CGImage
|
||||
var imageRect:NSRect = NSMakeRect(0, 0, self.size.width, self.size.height)
|
||||
let imageRef = self.cgImage(forProposedRect: &imageRect, context: nil, hints: nil)
|
||||
|
||||
// Create NSImage from the CGImage using the new size
|
||||
let imageWithNewSize = NSImage(cgImage: imageRef!, size: newSize)
|
||||
|
||||
// Return the new image
|
||||
return imageWithNewSize
|
||||
}
|
||||
|
||||
func rotateByDegreess(degrees:CGFloat) -> NSImage {
|
||||
|
||||
var imageBounds = NSZeroRect ; imageBounds.size = self.size
|
||||
let pathBounds = NSBezierPath(rect: imageBounds)
|
||||
var transform = NSAffineTransform()
|
||||
transform.rotate(byDegrees: degrees)
|
||||
pathBounds.transform(using: transform as AffineTransform)
|
||||
let rotatedBounds:NSRect = NSMakeRect(NSZeroPoint.x, NSZeroPoint.y , self.size.width, self.size.height )
|
||||
let rotatedImage = NSImage(size: rotatedBounds.size)
|
||||
|
||||
//Center the image within the rotated bounds
|
||||
imageBounds.origin.x = NSMidX(rotatedBounds) - (NSWidth(imageBounds) / 2)
|
||||
imageBounds.origin.y = NSMidY(rotatedBounds) - (NSHeight(imageBounds) / 2)
|
||||
|
||||
// Start a new transform
|
||||
transform = NSAffineTransform()
|
||||
// Move coordinate system to the center (since we want to rotate around the center)
|
||||
transform.translateX(by: +(NSWidth(rotatedBounds) / 2 ), yBy: +(NSHeight(rotatedBounds) / 2))
|
||||
transform.rotate(byDegrees: degrees)
|
||||
// Move the coordinate system bak to normal
|
||||
transform.translateX(by: -(NSWidth(rotatedBounds) / 2 ), yBy: -(NSHeight(rotatedBounds) / 2))
|
||||
// Draw the original image, rotated, into the new image
|
||||
rotatedImage.lockFocus()
|
||||
transform.concat()
|
||||
self.draw(in: imageBounds, from: NSZeroRect, operation: NSCompositingOperation.copy, fraction: 1.0)
|
||||
rotatedImage.unlockFocus()
|
||||
|
||||
return rotatedImage
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import Cocoa
|
||||
|
||||
class TimeTouchBarItem: NSCustomTouchBarItem {
|
||||
private let dateFormatter = DateFormatter()
|
||||
private var timer: Timer!
|
||||
private let button = NSButton(title: "", target: nil, action: nil)
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, formatTemplate: String) {
|
||||
dateFormatter.setLocalizedDateFormatFromTemplate(formatTemplate)
|
||||
super.init(identifier: identifier)
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
|
||||
self.view = button
|
||||
button.bezelColor = .clear
|
||||
updateTime()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func updateTime() {
|
||||
button.title = self.dateFormatter.string(from: Date())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,170 +8,227 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
struct ExactItem {
|
||||
let identifier: NSTouchBarItem.Identifier
|
||||
let presetItem: BarItemDefinition
|
||||
}
|
||||
|
||||
extension ItemType {
|
||||
|
||||
var identifierBase: String {
|
||||
switch self {
|
||||
case .staticButton(title: _):
|
||||
return "com.toxblh.mtmr.staticButton."
|
||||
case .appleScriptTitledButton(source: _):
|
||||
return "com.toxblh.mtmr.appleScriptButton."
|
||||
case .timeButton(formatTemplate: _):
|
||||
return "com.toxblh.mtmr.timeButton."
|
||||
case .dock():
|
||||
return "com.toxblh.mtmr.dock"
|
||||
case .volume():
|
||||
return "com.toxblh.mtmr.volume"
|
||||
case .brightness(refreshInterval: _):
|
||||
return "com.toxblh.mtmr.brightness"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NSTouchBarItem.Identifier {
|
||||
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
|
||||
static let centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea")
|
||||
}
|
||||
|
||||
class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
|
||||
static let shared = TouchBarController()
|
||||
|
||||
|
||||
let touchBar = NSTouchBar()
|
||||
|
||||
var timer = Timer()
|
||||
var timeButton: NSButton = NSButton()
|
||||
|
||||
|
||||
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
||||
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
|
||||
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var centerItems: [NSTouchBarItem] = []
|
||||
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in
|
||||
self?.dismissTouchBar()
|
||||
}))
|
||||
|
||||
createAndUpdatePreset()
|
||||
}
|
||||
|
||||
func createAndUpdatePreset() {
|
||||
self.itemDefinitions = [:]
|
||||
self.items = [:]
|
||||
self.leftIdentifiers = []
|
||||
self.centerItems = []
|
||||
self.rightIdentifiers = []
|
||||
|
||||
loadItemDefinitions()
|
||||
createItems()
|
||||
centerItems = self.itemDefinitions.compactMap { (identifier, definition) -> NSTouchBarItem? in
|
||||
return definition.align == .center ? items[identifier] : nil
|
||||
}
|
||||
|
||||
touchBar.delegate = self
|
||||
touchBar.defaultItemIdentifiers = [
|
||||
.escButton,
|
||||
|
||||
.brightDown,
|
||||
.brightUp,
|
||||
|
||||
.prev,
|
||||
.play,
|
||||
.next,
|
||||
|
||||
.sleep,
|
||||
.weather,
|
||||
|
||||
.volumeDown,
|
||||
.volumeUp,
|
||||
.battery,
|
||||
.time,
|
||||
]
|
||||
touchBar.defaultItemIdentifiers = self.leftIdentifiers + [.centerScrollArea] + self.rightIdentifiers
|
||||
self.presentTouchBar()
|
||||
}
|
||||
|
||||
func setupControlStripPresence() {
|
||||
func loadItemDefinitions() {
|
||||
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
||||
let presetPath = appSupportDirectory.appending("/items.json")
|
||||
if !FileManager.default.fileExists(atPath: presetPath),
|
||||
let defaultPreset = Bundle.main.path(forResource: "defaultPreset", ofType: "json") {
|
||||
try? FileManager.default.createDirectory(atPath: appSupportDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||
try? FileManager.default.copyItem(atPath: defaultPreset, toPath: presetPath)
|
||||
}
|
||||
let jsonData = presetPath.fileData
|
||||
let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, additionalParameters: [:])]
|
||||
|
||||
for item in jsonItems {
|
||||
let identifierString = item.type.identifierBase.appending(UUID().uuidString)
|
||||
let identifier = NSTouchBarItem.Identifier(identifierString)
|
||||
itemDefinitions[identifier] = item
|
||||
if item.align == .left {
|
||||
leftIdentifiers.append(identifier)
|
||||
}
|
||||
if item.align == .right {
|
||||
rightIdentifiers.append(identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createItems() {
|
||||
for (identifier, definition) in self.itemDefinitions {
|
||||
self.items[identifier] = self.createItem(forIdentifier: identifier, definition: definition)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func setupControlStripPresence() {
|
||||
DFRSystemModalShowsCloseBoxWhenFrontMost(false)
|
||||
let item = NSCustomTouchBarItem(identifier: .controlStripItem)
|
||||
item.view = NSButton(image: #imageLiteral(resourceName: "Strip"), target: self, action: #selector(presentTouchBar))
|
||||
NSTouchBarItem.addSystemTrayItem(item)
|
||||
DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, true)
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTime), userInfo: nil, repeats: true)
|
||||
}
|
||||
|
||||
|
||||
func updateControlStripPresence() {
|
||||
DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, true)
|
||||
}
|
||||
|
||||
|
||||
@objc private func presentTouchBar() {
|
||||
NSTouchBar.presentSystemModalFunctionBar(touchBar, systemTrayItemIdentifier: .controlStripItem)
|
||||
NSTouchBar.presentSystemModalFunctionBar(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
|
||||
}
|
||||
|
||||
private func dismissTouchBar() {
|
||||
|
||||
@objc private func dismissTouchBar() {
|
||||
NSTouchBar.minimizeSystemModalFunctionBar(touchBar)
|
||||
}
|
||||
|
||||
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
||||
switch identifier {
|
||||
case .escButton:
|
||||
let item = NSCustomTouchBarItem(identifier: identifier)
|
||||
item.view = NSButton(title: "esc", target: self, action: #selector(handleEsc))
|
||||
return item
|
||||
|
||||
case .brightUp:
|
||||
let item = NSCustomTouchBarItem(identifier: identifier)
|
||||
item.view = NSButton(title: "🔆", target: self, action: #selector(handleBrightUp))
|
||||
return item
|
||||
case .brightDown:
|
||||
let item = NSCustomTouchBarItem(identifier: identifier)
|
||||
item.view = NSButton(title: "🔅", target: self, action: #selector(handleBrightDown))
|
||||
return item
|
||||
|
||||
case .volumeDown:
|
||||
let item = NSCustomTouchBarItem(identifier: identifier)
|
||||
item.view = NSButton(title: "🔉", target: self, action: #selector(handleVolumeDown))
|
||||
return item
|
||||
case .volumeUp:
|
||||
let item = NSCustomTouchBarItem(identifier: identifier)
|
||||
item.view = NSButton(title: "🔊", target: self, action: #selector(handleVolumeUp))
|
||||
return item
|
||||
|
||||
// case .prev:
|
||||
// let item = NSCustomTouchBarItem(identifier: identifier)
|
||||
// item.view = NSButton(title: "⏪", target: self, action: #selector(handlePrev))
|
||||
// return item
|
||||
case .play:
|
||||
let item = NSCustomTouchBarItem(identifier: identifier)
|
||||
item.view = NSButton(title: "⏯", target: self, action: #selector(handlePlay))
|
||||
return item
|
||||
case .next:
|
||||
let item = NSCustomTouchBarItem(identifier: identifier)
|
||||
item.view = NSButton(title: "⏩", target: self, action: #selector(handleNext))
|
||||
return item
|
||||
|
||||
case .time:
|
||||
let item = NSCustomTouchBarItem(identifier: identifier)
|
||||
timeButton = NSButton(title: self.getCurrentTime(), target: self, action: nil)
|
||||
item.view = timeButton
|
||||
return item
|
||||
|
||||
default:
|
||||
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
||||
if identifier == .centerScrollArea {
|
||||
return ScrollViewItem(identifier: identifier, items: centerItems)
|
||||
}
|
||||
|
||||
guard let item = self.items[identifier],
|
||||
let definition = self.itemDefinitions[identifier],
|
||||
definition.align != .center else {
|
||||
return nil
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func getCurrentTime() -> String {
|
||||
let date = Date()
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.setLocalizedDateFormatFromTemplate("HH:mm")
|
||||
let timestamp = dateFormatter.string(from: date)
|
||||
return timestamp
|
||||
}
|
||||
|
||||
@objc func updateTime() {
|
||||
timeButton.title = getCurrentTime()
|
||||
}
|
||||
|
||||
@objc func handleEsc() {
|
||||
let sender = ESCKeyPress()
|
||||
sender.send()
|
||||
}
|
||||
|
||||
@objc func handleVolumeUp() {
|
||||
HIDPostAuxKey(Int(NX_KEYTYPE_SOUND_UP))
|
||||
}
|
||||
|
||||
@objc func handleVolumeDown() {
|
||||
HIDPostAuxKey(Int(NX_KEYTYPE_SOUND_DOWN))
|
||||
}
|
||||
|
||||
@objc func handleBrightDown() {
|
||||
// HIDPostAuxKey(Int(NX_KEYTYPE_BRIGHTNESS_DOWN))
|
||||
|
||||
let sender = BrightnessUpPress()
|
||||
sender.send()
|
||||
}
|
||||
|
||||
@objc func handleBrightUp() {
|
||||
// HIDPostAuxKey(Int(NX_KEYTYPE_BRIGHTNESS_UP))
|
||||
func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? {
|
||||
let action = self.action(forItem: item)
|
||||
|
||||
let sender = BrightnessDownPress()
|
||||
sender.send()
|
||||
var barItem: NSTouchBarItem!
|
||||
switch item.type {
|
||||
case .staticButton(title: let title):
|
||||
barItem = CustomButtonTouchBarItem(identifier: identifier, title: title, onTap: action)
|
||||
case .appleScriptTitledButton(source: let source, refreshInterval: let interval):
|
||||
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action)
|
||||
case .timeButton(formatTemplate: let template):
|
||||
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template)
|
||||
case .dock:
|
||||
barItem = AppScrubberTouchBarItem(identifier: identifier)
|
||||
case .volume:
|
||||
if case .image(let source)? = item.additionalParameters[.image] {
|
||||
barItem = VolumeViewController(identifier: identifier, image: source.image)
|
||||
} else {
|
||||
barItem = VolumeViewController(identifier: identifier)
|
||||
}
|
||||
case .brightness(refreshInterval: let interval):
|
||||
if case .image(let source)? = item.additionalParameters[.image] {
|
||||
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval, image: source.image)
|
||||
} else {
|
||||
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
|
||||
}
|
||||
}
|
||||
if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
|
||||
widthBarItem.setWidth(value: value)
|
||||
}
|
||||
if case .image(let source)? = item.additionalParameters[.image], let item = barItem as? CustomButtonTouchBarItem {
|
||||
let button = item.button!
|
||||
button.image = source.image
|
||||
button.imagePosition = .imageLeading
|
||||
button.imageHugsTitle = true
|
||||
button.bezelColor = .clear
|
||||
}
|
||||
return barItem
|
||||
}
|
||||
|
||||
@objc func handlePrev() {
|
||||
HIDPostAuxKey(Int(NX_KEYTYPE_PREVIOUS))
|
||||
|
||||
func action(forItem item: BarItemDefinition) -> ()->() {
|
||||
switch item.action {
|
||||
case .hidKey(keycode: let keycode):
|
||||
return { HIDPostAuxKey(keycode) }
|
||||
case .keyPress(keycode: let keycode):
|
||||
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
|
||||
case .appleSctipt(source: let source):
|
||||
guard let appleScript = source.appleScript else {
|
||||
print("cannot create apple script for item \(item)")
|
||||
return {}
|
||||
}
|
||||
return {
|
||||
var error: NSDictionary?
|
||||
appleScript.executeAndReturnError(&error)
|
||||
if let error = error {
|
||||
print("error \(error) when handling \(item) ")
|
||||
}
|
||||
}
|
||||
case .shellScript(executable: let executable, parameters: let parameters):
|
||||
return {
|
||||
let task = Process()
|
||||
task.launchPath = executable
|
||||
task.arguments = parameters
|
||||
task.launch()
|
||||
}
|
||||
case .custom(closure: let closure):
|
||||
return closure
|
||||
case .none:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handlePlay() {
|
||||
HIDPostAuxKey(Int(NX_KEYTYPE_PLAY))
|
||||
}
|
||||
|
||||
@objc func handleNext() {
|
||||
HIDPostAuxKey(Int(NX_KEYTYPE_NEXT))
|
||||
}
|
||||
|
||||
// func getBattery() {
|
||||
// var error: NSDictionary?
|
||||
// if let scriptObject = NSAppleScript(source: <#T##String#>) {
|
||||
// if let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError(
|
||||
// &error) {
|
||||
// print(output.stringValue)
|
||||
// } else if (error != nil) {
|
||||
// print("error: \(error)")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
protocol CanSetWidth {
|
||||
func setWidth(value: CGFloat)
|
||||
}
|
||||
|
||||
extension NSCustomTouchBarItem: CanSetWidth {
|
||||
func setWidth(value: CGFloat) {
|
||||
self.view.widthAnchor.constraint(equalToConstant: value).isActive = true
|
||||
}
|
||||
}
|
||||
|
||||
extension BarItemDefinition {
|
||||
var align: Align {
|
||||
if case .align(let result)? = self.additionalParameters[.align] {
|
||||
return result
|
||||
}
|
||||
return .center
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
//
|
||||
// TouchBarItems.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Anton Palgunov on 18/03/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
extension NSTouchBarItem.Identifier {
|
||||
static let escButton = NSTouchBarItem.Identifier("com.toxblh.mtmr.escButton")
|
||||
|
||||
// Volume
|
||||
static let volumeUp = NSTouchBarItem.Identifier("com.toxblh.mtmr.volumeUp")
|
||||
static let volumeDown = NSTouchBarItem.Identifier("com.toxblh.mtmr.volumeDown")
|
||||
|
||||
// Brightness
|
||||
static let brightUp = NSTouchBarItem.Identifier("com.toxblh.mtmr.brightUp")
|
||||
static let brightDown = NSTouchBarItem.Identifier("com.toxblh.mtmr.brightDown")
|
||||
|
||||
// Music
|
||||
static let prev = NSTouchBarItem.Identifier("com.toxblh.mtmr.prev")
|
||||
static let next = NSTouchBarItem.Identifier("com.toxblh.mtmr.next")
|
||||
static let play = NSTouchBarItem.Identifier("com.toxblh.mtmr.play")
|
||||
|
||||
// Plugins
|
||||
static let sleep = NSTouchBarItem.Identifier("com.toxblh.mtmr.sleep")
|
||||
static let weather = NSTouchBarItem.Identifier("com.toxblh.mtmr.weather")
|
||||
static let time = NSTouchBarItem.Identifier("com.toxblh.mtmr.time")
|
||||
static let battery = NSTouchBarItem.Identifier("com.toxblh.mtmr.battery")
|
||||
static let nowPlaying = NSTouchBarItem.Identifier("com.toxblh.mtmr.nowPlaying")
|
||||
|
||||
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
//
|
||||
// TouchBarPrivateApi-Bridging.h
|
||||
// MTMR
|
||||
//
|
||||
// Created by Anton Palgunov on 18/03/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TouchBarPrivateApi.h"
|
||||
#import "TouchBarSupport.h"
|
||||
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// ViewController.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Anton Palgunov on 16/03/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class ViewController: NSViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
override var representedObject: Any? {
|
||||
didSet {
|
||||
// Update the view, if already loaded.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import Cocoa
|
||||
import AppKit
|
||||
import AVFoundation
|
||||
import CoreAudio
|
||||
|
||||
class VolumeViewController: NSCustomTouchBarItem {
|
||||
private(set) var sliderItem: CustomSlider!
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, image: NSImage? = nil) {
|
||||
super.init(identifier: identifier)
|
||||
|
||||
var forPropertyAddress = AudioObjectPropertyAddress(
|
||||
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
|
||||
mScope: kAudioDevicePropertyScopeOutput,
|
||||
mElement: kAudioObjectPropertyElementMaster)
|
||||
|
||||
addListenerBlock(listenerBlock: audioObjectPropertyListenerBlock,
|
||||
onAudioObjectID: defaultDeviceID,
|
||||
forPropertyAddress: &forPropertyAddress)
|
||||
|
||||
if (image == nil) {
|
||||
sliderItem = CustomSlider()
|
||||
} else {
|
||||
sliderItem = CustomSlider(knob: image!)
|
||||
}
|
||||
sliderItem.target = self
|
||||
sliderItem.action = #selector(VolumeViewController.sliderValueChanged(_:))
|
||||
sliderItem.minValue = 0.0
|
||||
sliderItem.maxValue = 100.0
|
||||
sliderItem.floatValue = getInputGain()*100
|
||||
|
||||
self.view = sliderItem
|
||||
}
|
||||
|
||||
func addListenerBlock( listenerBlock: @escaping AudioObjectPropertyListenerBlock, onAudioObjectID: AudioObjectID, forPropertyAddress: UnsafePointer<AudioObjectPropertyAddress>) {
|
||||
|
||||
if (kAudioHardwareNoError != AudioObjectAddPropertyListenerBlock(onAudioObjectID, forPropertyAddress, nil, listenerBlock)) {
|
||||
print("Error calling: AudioObjectAddPropertyListenerBlock") }
|
||||
}
|
||||
|
||||
func audioObjectPropertyListenerBlock (numberAddresses: UInt32, addresses: UnsafePointer<AudioObjectPropertyAddress>) {
|
||||
var index: UInt32 = 0
|
||||
while index < numberAddresses {
|
||||
let address: AudioObjectPropertyAddress = addresses[Int(index)]
|
||||
switch address.mSelector {
|
||||
case kAudioHardwareServiceDeviceProperty_VirtualMasterVolume:
|
||||
DispatchQueue.main.async {
|
||||
self.sliderItem.floatValue = self.getInputGain() * 100
|
||||
}
|
||||
default:
|
||||
|
||||
print("We didn't expect this!")
|
||||
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
sliderItem.unbind(NSBindingName.value)
|
||||
}
|
||||
|
||||
@objc func sliderValueChanged(_ sender: Any) {
|
||||
if let sliderItem = sender as? NSSlider {
|
||||
_ = setInputGain(Float32(sliderItem.intValue)/100.0)
|
||||
}
|
||||
}
|
||||
|
||||
private var defaultDeviceID: AudioObjectID {
|
||||
var deviceID: AudioObjectID = AudioObjectID(0)
|
||||
var size: UInt32 = UInt32(MemoryLayout<AudioObjectID>.size)
|
||||
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
|
||||
address.mSelector = AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice)
|
||||
address.mScope = AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal)
|
||||
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
|
||||
AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &size, &deviceID)
|
||||
return deviceID
|
||||
}
|
||||
|
||||
private func getInputGain() -> Float32 {
|
||||
var volume: Float32 = 0.5
|
||||
var size: UInt32 = UInt32(MemoryLayout.size(ofValue: volume))
|
||||
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
|
||||
address.mSelector = AudioObjectPropertySelector(kAudioHardwareServiceDeviceProperty_VirtualMasterVolume)
|
||||
address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput)
|
||||
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
|
||||
AudioObjectGetPropertyData(defaultDeviceID, &address, 0, nil, &size, &volume)
|
||||
return volume
|
||||
}
|
||||
|
||||
private func setInputGain(_ volume: Float32) -> OSStatus {
|
||||
var inputVolume: Float32 = volume
|
||||
|
||||
if inputVolume == 0.0 {
|
||||
_ = setMute( mute: 1)
|
||||
} else {
|
||||
_ = setMute( mute: 0)
|
||||
}
|
||||
|
||||
let size: UInt32 = UInt32(MemoryLayout.size(ofValue: inputVolume))
|
||||
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
|
||||
address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput)
|
||||
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
|
||||
address.mSelector = AudioObjectPropertySelector(kAudioHardwareServiceDeviceProperty_VirtualMasterVolume)
|
||||
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &inputVolume)
|
||||
}
|
||||
|
||||
private func setMute( mute: Int) -> OSStatus {
|
||||
var muteVal: Int = mute
|
||||
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
|
||||
address.mSelector = AudioObjectPropertySelector(kAudioDevicePropertyMute)
|
||||
let size: UInt32 = UInt32(MemoryLayout.size(ofValue: muteVal))
|
||||
address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput)
|
||||
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
|
||||
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &muteVal)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// WindowController.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Anton Palgunov on 17/03/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class WindowController: NSWindowController {
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
[
|
||||
{ "type": "escape", "align": "left" },
|
||||
{ "type": "exitTouchbar", "width": 44, "align": "left" },
|
||||
{ "type": "brightnessDown", "width": 44, "align": "left" },
|
||||
{
|
||||
"type": "brightness",
|
||||
"width": 60,
|
||||
"align": "left",
|
||||
"image": {
|
||||
"base64":
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAGUExURffLOPfLNyaSVzUAAAACdFJOU/kBxOqnWgAAAbJJREFUSMfVljtyhDAQBVulQCFH4CgcDR1NR9ERFBKoeA5GfGddtkNvwFINFKP5tED22+Zxwviv6QVKfIEc/iNoF5gkpLIeYI8SUp4PsAUJiekADQntF6isQjvxCTrhAJlFqMMBeIH9BMsD7DAb2BhvYbIyNAOCZIWqYKGTpDZJFQu9EKVd44RxQRq3IrULWD62C8wSssWUZEsR0k6wcDOrJZmoBpMKI+s5qkBQCQOUJADVOECdOsDS0gDbsgHMfT4rVwHSrZQFIN5ABka8BgDgAeZ+BztBgvUEnSgVlhNsTFJjvoF5HAZorBpdYKAiSRbqNyBIUr6AjZMdPwO72R40MElS+wZUWA+wQ6LAYkFvdIhkmA+wQSDDdIAGAZ6A34H0x0fca11gBZZsIHSIfnE/5+NjCn/OuiuUB+/aunZwDeNayjXdTpDN0wlY+r1PfWu75nfj8RogN2JuCN2Y5qgMwTI0wGPUnQw6Qarx0sVNKA5Mn6VUL22lIbZoYitDbPmlvocc9Umfl2D7adz1reC3pF8av4m+DCenp/ndZuG3E7fhuC3pH2+vnz8V3MfE+bnxBTXuuIMTrLWHAAAAAElFTkSuQmCC"
|
||||
}
|
||||
},
|
||||
{ "type": "brightnessUp", "width": 44, "align": "left" },
|
||||
{
|
||||
"type": "appleScriptTitledButton",
|
||||
"source": {
|
||||
"inline":
|
||||
"if application \"iTunes\" is running then\rtell application \"iTunes\"\rif player state is playing then\rreturn (get artist of current track) & \" – \" & (get name of current track)\relse\rreturn \"\"\rend if\rend tell\rend if\rreturn \"\"\r"
|
||||
},
|
||||
"action": "appleScript",
|
||||
"actionAppleScript": {
|
||||
"inline":
|
||||
"if application \"iTunes\" is running then\rtell application \"iTunes\"\rif player state is playing then\rnext track\rend if\rend tell\rend if\r"
|
||||
},
|
||||
"refreshInterval": 1,
|
||||
"image": {
|
||||
"base64":
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADAUExURUdwTOVVZCzB+3qc0gkDBgEBAgcKEwAAAA4EBP5aVU2V95iJv7V3rtBOvH5W6jaOyclScKZGX3wuQCMuUqZN7+NQYXtDvFd9/sxYni2z6UhBhyhvnIp7sf38/PXz9ePm69/k6fHv8/j29+3q7/v6+ufq7uvu8fTw8+1egOFki/dbboVj/HNy/T+j/dNtnEul81vC8Vmf3OeRqOBVofK4xZfF7sDb7PLe6LxU1KKK79PL6vrW3fh4g5Zi4bi16daa0A3Qc90AAAAddFJOUwD3/v0uOlYNG/z+/v7998OYYztt/Le2/eDqi5jEo2rNTwAABMtJREFUWMPdl1tjqjoQhauC0Hqvta26JVxUQBQFRPBS/f//as8kISBqd/twXs5KjBDyrZkEFfP09D+WoigqFxz+mlbVXncweEZVBoNuD1x+E1vtMVZoURn01J/mob52KRNuj4k5mZjJcRueFotFpfv6EwdVRfy0tSdXSranheN0/zkRSB7x42Q6udExdBy3972Fog4o/kBg4X6bhPJaeX7eTqeT6b0MJtPpdOu6n49XQgX+dOQ8vpWEvYn/2AHiL052PpoROcsOtND17ztQ3rwTuCQz9O/moKiMf6BkG/puKBzurKQ64PmbU2bDzUxk3Uql4lZcl3Vpvt9VbxLoLZwjY7E1WcNZoB0XpbELie/3Sg6KVHG2jGPs1LTCE2UXFfgIgtBgyq8d/E/pehJq1zmZGc7kAPsMX4Ec932T25uX5vUklFcHJlDU1OT4wllkvOtn9lrSbF7dCUggNEtaMOXhQZq4WkpBcksJQBCOnyjvM4P8KqQgFW9BFJka2NMKB1gw+VMxvN9smnwI1EuzpxS+g9FWYySjsTpOtnq+H162iW01m/wyXLUPzT/5HKQoSjQmU8vE6TAElvWggbhuNRpScQa002bVtJmBCz9qNusWBkJmoyHmoHajC4yybVujhR26mJVha7lDo2FrhnA4N0aq+BpE24zjgsMoEsfU0AADaKCwemiIRZA+o6N9oygyMi/EAWk0DMNgFvCmN/5IwqCV3PCGzzIwbINrzgwykVq2iorUalm2UTZotXKWqVYz5uBjzDUoxrxWyzKQWy061LZsNIJ3PAMDIcbVauwdGmxrNblgYNnCgStN54ylBSsYoAxerwwgJsCWTS0sepym0Mdp1gYBw5lmwgDXIDEoaeHLYE36BafzuQWFQ9RASM/XQPpMD5YQ2gA/AwPArQJyZWDsgo64C+/pBRkDG4s31hdmwFNAGz1mBjPukBs8qdSgLDBA1LJm1lw/14IgWAdAQ5nhax4HY/FR7qfpHQMPUChzS0c6eFmv17MZo7HZBP3MQJHTNCnzxPMgOFUteAGtMwMmfRPIijBgc+AmNK6+9zw+9Iw05YsG8aaT/7Kro7eUcoSWw3n/1W57SxgGOZCYxl+VDDabcf6LpNTf3g6IQ4XY7TbiyyUBmhqsViswWK02cE7ITIdmtxnWlcJz6f1tPyMsha+2R4UGyJPdywotwGSHOKHdm+IMYA40BRhtkXOb42DAh8crppjw8CyB4nMBlvFtD/0WSfZebkDHguLNar2JdyTXptqRrx6OmMKZkISQc4Yv9yyDXHiiEx1qXL1OAFdhBJPAAQeRQDEiRZEm+kwnu2p1XHo64yQ8j47bL1kCZ87pDKWxuW4mQJ9O9ba31xE5Y/rnA4VoTCJQwvnyBNgk+pkDi8sSJjlKRPxhX7r3Lytz0LPMi1H1Qv7VuzwuAzh4h1ukKFi/YV9+9E8THZbne2Ezxd/xsNGQ6u+wgoeH4SH9Tl367t+2Ko/acA8Oj/DhWP7X/30Zkvj4WMYlj10MOISXf7DlkPvvH6g43u0oCzDS1U5f/sHWC3d7cn3UAQf4HeHfwxXQY4yu/HTDKNXro3Gngw4vw2FnPKrXJfUXu0fqIdeFZOnXm08FTRSxcf391pW7oNGT8vRf6i9jqljwYzAm6AAAAABJRU5ErkJggg=="
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "appleScriptTitledButton",
|
||||
"source": {
|
||||
"inline":
|
||||
"if application \"Spotify\" is running then\rtell application \"Spotify\"\rif player state is playing then\rreturn (get artist of current track) & \" – \" & (get name of current track)\relse\rreturn \"\"\rend if\rend tell\rend if\rreturn \"\"\r"
|
||||
},
|
||||
"action": "appleScript",
|
||||
"actionAppleScript": {
|
||||
"inline":
|
||||
"if application \"Spotify\" is running then\rtell application \"Spotify\"\rif player state is playing then\rnext track\rend if\rend tell\rend if\r"
|
||||
},
|
||||
"refreshInterval": 1,
|
||||
"image": {
|
||||
"base64":
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAYUExURUdwTB3WXx3UXh3VXx7XYBkXFRpVLRyURmIaeAQAAAAEdFJOUwDDO3fSqUUkAAABbklEQVRIx61VbW6DMAztoAeYNA7ApB6gkzhAWS24wIAL0HABaK6/pHFNEhy8TXu/kPzkPD/8cTj8K7KPAqB+K5NhQPCUrABCXe7HOUYYZxgVRLiG8RfY4DUgFFtC7cffAfZTFBwBdhWEKfgEq4ocEjgj4ZQifO6/QG9kkETp1dDeVWfRKx3XYSW0LoqY5kCElXDrQkyeCCuh6WL0M4nIWQIyzqixdfKU1koFDKvyCA8NJMzU4xiD+b4kfHRpsIyKc6hBwjVptFHVY51EMAINNDFGJITKDNQcdpX74Hz0CQ3rY5qwMp4EIxrlafzrsYZ2Veb0DkRgfNCUok4Y1fqEijfyi2b8RE9beWqa48Y/uvCNMcH9btfUi+/CGLR1vhL6Zz9N/vBlaCU+7lwY/cmJ67Ryen/2tj23PLqJBodZH8vgj544vOL4pxfI5acrSFxi8hrkU9TSKr78ZpnL50A8KPJJEo+afBblwyqf5j/iGys5j6ScrST2AAAAAElFTkSuQmCC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "appleScriptTitledButton",
|
||||
"source": {
|
||||
"inline":
|
||||
"if application \"VOX\" is running then\rtell application \"VOX\"\rif player state is 1 then\rreturn (get artist) & \" – \" & (get track)\relse\rreturn \"\"\rend if\rend tell\rend if\rreturn \"\"\r"
|
||||
},
|
||||
"action": "appleScript",
|
||||
"actionAppleScript": {
|
||||
"inline":
|
||||
"if application \"VOX\" is running then\rtell application \"VOX\"\rif player state is 1 then\rnext\rend if\rend tell\rend if\r"
|
||||
},
|
||||
"refreshInterval": 1,
|
||||
"image": {
|
||||
"base64":
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADAUExURUdwTHd7gqeqtKClrd/k7n+Di7e7xI6TnNHX4cPI0Xp9hJibpOfs9u/1/vX7/rq/yZiborK3wEpOUxgYFlFXYiIhHycpLD9ESjI0NgsLCj09O4SKk19lcbBkALhoAHJ3gT8rAzMvJJugqKhcAL5xAOSxB4I/AJ5SAGpweXuAiZJJAG8zAeXr9WNiYM+NANqeAMd9APHACfr9/e7JPrO5w6mrsA4bKC0gCHhtTyAYBFcpAZduKK2JP/fost/Eb5SUlFFfi9AAAAASdFJOUwDJE9FgWjb+/vyHppTXtbn9mDupvZ0AAAdoSURBVFjDlZcLQ6O6FoWrtgI+Owl5EGgp2DrEgnV6wD4c7f//V3ftAE7nzOj17NGxpayPtXfSZGcw+CjOh1c3N9eIm5ur4fngv8Xp8KZE+F2U5bq8GZ5+WU5qP6jzsRdTeF7e1AEoN8Ovya/LMsg9mxZJErpIksJarwnK8nr4FblfQ52EDOH0zL1KUuvVfnn9eTVOryCPoVZQuGj/FOApMAhx9Uktzq/L2ksT3FuQhIVVVVRVRa+SogA1KbzgExPD0s/xdBOmKdOqODR95IdEaZba0LAkbfzyg0rA/pgekxZKV4cmtxXTEmFUEufNodIKYJjLkcbf9UEM90mqZJU3qeJRxDkXAv/hlbJNXkmVJjCBNP5CGPq1TZSyiWB5XomIi+woRBSJKj8YUVgFQ4E//FMfQM8s40VTyIhDpLVRNIJKaWLwSNqm4mEMl/YPwrkP/9AbER+UkxtWbbcvLrbbkBlCcDa23BABHn4bi1PSM2a1PFgZwbwJty+TybSLyeRlyxQlIuNY6FgpDKd/PB+uXP2tFHEheCtvhW3Q65dtiEyQRixMrBjG4uo4gTpNFApvU9Kr6sWp77qY3BFj8lLBBJdpykOLwaqPkgioAEnFi1RCz7Z3uJ2U37sgCBB3W5ZJLm0VJQmVIXgfgSAvQpYK1ACDx7bTOT2dlA8uWsYEl0EQ3KDUKcOECvqRCGgGpApXf9M/PNx3QQwyMZ9uGWZWiGelsFAHvQFUMEx4WAku2HZC+k6+vF/ip0U4wmSrBJcFZgss9xZuYYAlWhaGC1PhMb1++R6EaAl3lRFcFUI5C7duDlAFwpCzSogsfCH93fdWvm+jRYCASs5fwkyIinGsVrBAc2FYxwVLlKhgQG3n8+kv/WwjhNhsZvveQ5+EqrhKWOHVlMNtgyUkhHkpdPjy1CZA+pl4bkOIfWcBQ/EEC1xWWmKxsi6HussA9WUwQBV0BmbPz3y32+x2nD8/z/okpvM5LAjGeEg51JiFtQeAEqHhWUIGJq0B0m9mLjYdgSxMnQVhGHIIkcP5YNjEAGjJJDfF5AnzzRnY/9K3hP3SjSUAk0oLzaQOwyJGEb41FgWVuOIymHYZCKfPGFaxzBHEcQ5cYrXD3LHNt8E3WkdDbpBY8vMdMHsW0Jui9v26MCDAggNMAfgZoghGdIDbPMXewY0RWfHzqQMsN2QgC/MAkVcZWZi1DuYAJJlQBlVM0vx2cDZOw4QAPEt/AcTzDgkkh3w8Hh8SPZvtKIc2haefKapo2mE4G5x5nQOuLQA0jwggCcAqF1SFnZQEwEwCwGZ0P+sAXQqa69gBJvgeLCUBhMS6arSWogV0NWwB2gHG5KAggDYO0OXQATghJP4QIPsFiJHCEYB2YgBE5gBzNxEyjVHYcOxKKDfquRE6u39wFXh6dADJaccG4HLsANIIEb89PnaEvSYLG2kYAG0G+3YMnh4fDxbfe4wkAS47gAAyShsA2iSWUgsiYHfJMCF2ZKDVA9AkET4WbhgvB6OxxeoiuZZRVT/2BFjQfOemMX53qNn+e5fA42OtAOCYiYXNR4PznL4LJoLA1G8rQjjCTCmxc9+GzU4oNXvonr9avTUZPc7QdyHH0j4mgIqQVZQHq1VvAgRKf7MRKISZPWAG0ONXq1Vg6WYahMIbYz24pJ6ECYIyf7EiBBGm3/da0c6KX/ifTnv9P74iu66G3iUAI4+KoCNpeNS8rha9ifnkfmlcLO+n816+WL3mEVVcY2ew4xEtqsgBjUWEQkWsfFssWhdgPM0xoBg8iKl2JF8s3kpngCtMo3jsNljkUDAmYQFVWC9+tAgHeQ93AR/8WKw9d6MMWWFdBn0OJsJsjERZLn4QomOsWmmrpk9Kn0eYxpGiMfBG7dZ0FrcW8B2N1PoV9zkEUehn1b2hy68lxg/F0owVaXzW7Y0j6kwZuiqhUIaW8M54F9Olcq0irF0R1vAkfTdAFmwaKhjDUgfCur39n+NwF9ZrFhnMWm5YiM737L0/GHkxtYD4RKIWILz1+jf86/Vv69I4fSTRTNojAxgIdPUgCE7VjXhzhHAEJ1/nUUSl4qTHKeDyuMnCycCzjiCwyUYsWK9fofzhxIC8rtcBi2SlqPeEnu7/rekGwcutwhTHhKoYHlWvEa8u6FVN17D/Or3Ncfu/Wu5R7I2bmNECRiYYRpuaYtKiBa+0u0j9K7aguMFZZvTvVvUCFuoce5ygSmA9xlcRDR8iyzaZqVq50Coc1zBw8WezfBEfmqBGX+8QXLs1HY9tV3aMMeQSp4A6aP6qdx4aNHyMWnxBHbpwzTIW9vadkGTf/1BPdYhxsqrR7zuGoD4/6pp+iKVkaY3zWGxHHx55TuJxXZa1RSmwIyAyeoGVlf4wG9CBLD755Nx1ehHHub9e+3lBpxUh+0DuuO4ef/H5AXR0gkpg+EofjQed1ej8VsTuEk6T8cno/54cRyc2zms3Bcr+4Esvazz9C/IOYWlESNseoWtU3n5V3tYCDEA8tAce5j3enFyc/scT/Pno4qSLi9HHhf8f8eOIRf9lFswAAAAASUVORK5CYII="
|
||||
}
|
||||
},
|
||||
{ "type": "previous", "width": 44 },
|
||||
{ "type": "play", "width": 44 },
|
||||
{ "type": "next", "width": 44 },
|
||||
{ "type": "weather", "refreshInterval": 1800 },
|
||||
{ "type": "sleep", "width": 44 },
|
||||
{ "type": "volumeDown", "width": 34, "align": "right" },
|
||||
{ "type": "volume", "width": 60, "align": "right" },
|
||||
{ "type": "volumeUp", "width": 34, "align": "right" },
|
||||
{ "type": "battery", "align": "right" },
|
||||
{ "type": "timeButton", "align": "right" }
|
||||
]
|
||||
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// MTMRTests.swift
|
||||
// MTMRTests
|
||||
//
|
||||
// Created by Anton Palgunov on 16/03/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import MTMR
|
||||
|
||||
class MTMRTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testExample() {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import XCTest
|
||||
@testable import MTMR
|
||||
|
||||
class ParseConfig: XCTestCase {
|
||||
|
||||
func testButtonNoAction() {
|
||||
let buttonNoActionFixture = """
|
||||
[ { "type": "staticButton", "title": "Pew" } ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
|
||||
XCTAssertEqual(result?.first?.type, .staticButton(title: "Pew"))
|
||||
XCTAssertEqual(result?.first?.action, .some(.none))
|
||||
}
|
||||
|
||||
func testButtonKeyCodeAction() {
|
||||
let buttonKeycodeFixture = """
|
||||
[ { "type": "staticButton", "title": "Pew", "action": "hidKey", "keycode": 123} ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture)
|
||||
XCTAssertEqual(result?.first?.type, .staticButton(title: "Pew"))
|
||||
XCTAssertEqual(result?.first?.action, .hidKey(keycode: 123))
|
||||
}
|
||||
|
||||
func testPredefinedItem() {
|
||||
let buttonKeycodeFixture = """
|
||||
[ { "type": "brightnessUp" } ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture)
|
||||
XCTAssertEqual(result?.first?.type, .staticButton(title: "🔆"))
|
||||
XCTAssertEqual(result?.first?.action, .keyPress(keycode: 113))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// MTMRUITests.swift
|
||||
// MTMRUITests
|
||||
//
|
||||
// Created by Anton Palgunov on 16/03/2018.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
class MTMRUITests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
// In UI tests it is usually best to stop immediately when a failure occurs.
|
||||
continueAfterFailure = false
|
||||
// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
|
||||
XCUIApplication().launch()
|
||||
|
||||
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testExample() {
|
||||
// Use recording to get started writing UI tests.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +1,217 @@
|
||||
|
||||
<p align="center">
|
||||
<img src="Resources/logo.png" width="120">
|
||||
</p>
|
||||
|
||||
# My TouchBar. My rules
|
||||
[](https://github.com/Toxblh/MTMR/releases)
|
||||
|
||||
<p align="center">
|
||||
<img src="Resources/TouchBarV0.1.png">
|
||||
<img src="Resources/TouchBar-v0.8.1.png">
|
||||
</p>
|
||||
|
||||
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. 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
|
||||
- [x] Create the first prototype with TouchBar in Storyboard
|
||||
- [x] Put in stripe menu on startup the application
|
||||
- [x] Find how to simulate real buttons like brightness, volume, night shift and etc.
|
||||
- [x] Time in touchbar!
|
||||
- [ ] First the weather plugin
|
||||
- [ ] Create the same panel like in my [BTT preset](https://github.com/Toxblh/btt-touchbar-preset)
|
||||
- [ ] Find how to open full-screen TouchBar without the cross and stripe menu
|
||||
- [ ] Find how to add haptic feedback
|
||||
- [x] First the weather plugin
|
||||
- [x] Find how to open full-screen TouchBar without the cross and stripe menu
|
||||
- [x] Find how to add haptic feedback
|
||||
- [x] Add icon and menu in StatusBar
|
||||
- [x] Hide from Dock
|
||||
- [x] Status menu: "preferences", "quit"
|
||||
- [x] JSON or another approch for save preset, maybe in `~/Library/Application Support/MTMR/`
|
||||
- [x] Custom buttons size, actions by click
|
||||
- [x] Layout: [always left, NSSliderView for center, always right]
|
||||
- [ ] Overwrite default values from item types (e.g. title for brightness)
|
||||
- [ ] Custom settings for paddings and margins for buttons
|
||||
- [ ] XPC Service for scripts
|
||||
- [ ] UI for settings
|
||||
- [ ] Import config from BTT
|
||||
- [ ] System for autoupdate (maybe https://sparkle-project.org/)
|
||||
|
||||
Settings:
|
||||
- [ ] Intarface for plugins and export like presets
|
||||
- [ ] Startup at login
|
||||
- [ ] Show on/off in Dock
|
||||
- [ ] Show on/off in StatusBar
|
||||
- [ ] On/off Haptic Feedback
|
||||
|
||||
Maybe:
|
||||
- [ ] Refactoring the application on packages (AppleScript, JavaScript? and Swift?)
|
||||
|
||||
|
||||
## Installation
|
||||
1. Download last release [Releases](https://github.com/Toxblh/MTMR/releases)
|
||||
2. Unzip
|
||||
3. Open MTMR
|
||||
4. Open preset `open ~/Library/Application\ Support/MTMR/items.json` and customize it. Restart MTMR to apply changes.
|
||||
|
||||
## Preset
|
||||
|
||||
File for customize your preset for MTMR: `open ~/Library/Application\ Support/MTMR/items.json`
|
||||
|
||||
## Built-in button types:
|
||||
|
||||
- escape
|
||||
- exitTouchbar
|
||||
- brightnessUp
|
||||
- brightnessDown
|
||||
- volumeDown
|
||||
- volumeUp
|
||||
- dock
|
||||
|
||||
> Media Keys
|
||||
- previous
|
||||
- play
|
||||
- next
|
||||
|
||||
> AppleScript plugins
|
||||
- weather
|
||||
- battery
|
||||
- sleep
|
||||
- displaySleep
|
||||
|
||||
## Built-in slider types:
|
||||
|
||||
- brightness
|
||||
- volume
|
||||
|
||||
### You can also make a custom buttons using these types
|
||||
- `staticButton`
|
||||
```json
|
||||
"type": "staticButton",
|
||||
"title": "esc",
|
||||
```
|
||||
|
||||
- `appleScriptTitledButton`
|
||||
```js
|
||||
"type": "appleScriptTitledButton",
|
||||
"refreshInterval": 60, //optional
|
||||
"source": {
|
||||
"filePath": "/Users/toxblh/Library/Application Support/MTMR/iTunes.nowPlaying.scpt",
|
||||
// or
|
||||
"inline": "tell application \"Finder\"\rmake new Finder window\rset target of front window to path to home folder as string\ractivate\rend tell",
|
||||
// or
|
||||
"base64": "StringInbase64"
|
||||
},
|
||||
```
|
||||
|
||||
- `timeButton`
|
||||
```js
|
||||
"type": "timeButton",
|
||||
"formatTemplate": "HH:mm" //optional
|
||||
```
|
||||
|
||||
## Actions:
|
||||
- `hidKey`
|
||||
```json
|
||||
"action": "hidKey",
|
||||
"keycode": 53,
|
||||
```
|
||||
|
||||
- `keyPress`
|
||||
```json
|
||||
"action": "keyPress",
|
||||
"keycode": 1,
|
||||
```
|
||||
|
||||
- `appleScript`
|
||||
```js
|
||||
"action": "appleScript",
|
||||
"actionAppleScript": {
|
||||
"inline": "tell application \"Finder\"\rmake new Finder window\rset target of front window to path to home folder as string\ractivate\rend tell"
|
||||
// "filePath" or "base64" will work as well
|
||||
},
|
||||
```
|
||||
|
||||
- `shellScript`
|
||||
```js
|
||||
"action": "shellScript",
|
||||
"executablePath": "/usr/bin/pmset",
|
||||
"shellArguments": "sleepnow", // optional
|
||||
|
||||
```
|
||||
|
||||
## Additional paramaters:
|
||||
|
||||
- `width` allow to restrict how much room a particular button will take
|
||||
```json
|
||||
"width": 34
|
||||
```
|
||||
|
||||
- `align` can stick the item to the side. default is center
|
||||
```js
|
||||
"align": "left" //or "right" or "center"
|
||||
```
|
||||
|
||||
## Example configuration:
|
||||
```json
|
||||
[
|
||||
{ "type": "escape", "width": 110 },
|
||||
{ "type": "exitTouchbar", "align": "left" },
|
||||
{
|
||||
"type": "brightnessUp",
|
||||
"align": "left",
|
||||
"width": 36
|
||||
},
|
||||
{
|
||||
"type": "staticButton",
|
||||
"align": "left",
|
||||
"title": "🔆",
|
||||
"action": "keyPress",
|
||||
"keycode": 113,
|
||||
"width": 36
|
||||
},
|
||||
|
||||
{
|
||||
"type": "appleScriptTitledButton",
|
||||
"source": {
|
||||
"filePath": "/Users/toxblh/Library/Application Support/MTMR/iTunes.nowPlaying.scpt"
|
||||
},
|
||||
"refreshInterval": 1
|
||||
},
|
||||
{
|
||||
"type": "staticButton",
|
||||
"align": "left",
|
||||
"image": { "base64" : "%base64Finder%"},
|
||||
"action": "appleScript",
|
||||
"actionAppleScript": {
|
||||
"inline": "tell application \"Finder\"\rmake new Finder window\rset target of front window to path to home folder as string\ractivate\rend tell"
|
||||
},
|
||||
"width": 36
|
||||
},
|
||||
{
|
||||
"type": "appleScriptTitledButton",
|
||||
"source": {
|
||||
"inline": "if application \"Safari\" is running then\r\ttell application \"Safari\"\r\t\trepeat with t in tabs of windows\r\t\t\ttell t\r\t\t\t\tif URL starts with \"https:\/\/music.yandex.ru\" and name does not end with \"на Яндекс.Музыке\" then\r\t\t\t\t\treturn name of t as text\r\t\t\t\tend if\r\t\t\tend tell\r\t\tend repeat\r\tend tell\rend if\rreturn \"\""
|
||||
},
|
||||
"refreshInterval": 1
|
||||
},
|
||||
{ "type": "previous", "width": 36, "align": "right" },
|
||||
{ "type": "play", "width": 36, "align": "right" },
|
||||
{ "type": "next", "width": 36, "align": "right" },
|
||||
{ "type": "sleep", "width": 36 , "align": "right"},
|
||||
{ "type": "displaySleep", "align": "right" },
|
||||
{ "type": "weather", "refreshInterval": 1800, "width": 70, "align": "right" },
|
||||
{ "type": "volumeDown", "width": 36 , "align": "right"},
|
||||
{ "type": "volumeUp", "width": 36 , "align": "right"},
|
||||
{ "type": "battery", "refreshInterval": 60 , "align": "right"},
|
||||
{ "type": "appleScriptTitledButton", "refreshInterval": 1800, "source": { "filePath": "/Users/redetection/Library/Application Support/MTMR/Weather.scpt"} , "align": "right"},
|
||||
{ "type": "timeButton", "formatTemplate": "HH:mm", "width": 64, "align": "right" }
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
### Author's presets
|
||||
|
||||
[@Toxblh preset](Resources/toxblh.json)
|
||||
|
||||
[@ReDetection preset](Resources/ReDetection.json)
|
||||
|
||||
## Credits
|
||||
|
||||
Built by [@Toxblh](https://patreon.com/toxblh) and [@ReDetection](http://patreon.com/ReDetection).
|
||||
|
||||
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,5 @@
|
||||
## TODOs
|
||||
|
||||
* try view controllers on `NSCustomTouchBarItem` instead of subclassing item itself
|
||||
* try move away from enums when parse preset – enums are hard to extend
|
||||
* find better way to hide bar items
|
||||
@@ -0,0 +1,23 @@
|
||||
NAME='MTMR'
|
||||
|
||||
rm -r Release 2>/dev/null
|
||||
|
||||
xcodebuild archive \
|
||||
-scheme "$NAME" \
|
||||
-archivePath Release/App.xcarchive
|
||||
|
||||
xcodebuild \
|
||||
-exportArchive \
|
||||
-archivePath Release/App.xcarchive \
|
||||
-exportOptionsPlist export-options.plist \
|
||||
-exportPath Release
|
||||
|
||||
cd Release
|
||||
rm -r App.xcarchive
|
||||
|
||||
# Prerequisite: npm i -g create-dmg
|
||||
create-dmg "${NAME}.app"
|
||||
|
||||
VERSION=`mdls -raw -name kMDItemVersion MTMR.app`
|
||||
echo $VERSION
|
||||
zip -r "${NAME}v${VERSION}.zip" "${NAME}.app"
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>teamID</key>
|
||||
<string>D6D8BR2QNB</string>
|
||||
<key>method</key>
|
||||
<string>mac-application</string>
|
||||
</dict>
|
||||
</plist>
|
||||