Compare commits

..
139 Commits
Author SHA1 Message Date
toxblh cd75be6c26 v0.14 2018-04-30 22:17:04 +01:00
Anton Palgunov 7646c6e89d Merge pull request #58 from ReDetection/bordered-parameter
WIP: Bordered parameter, background parameter
2018-04-30 19:20:07 +01:00
toxblh 9c970aff63 if button have only image, change position to .imageonly 2018-04-30 19:19:33 +01:00
Serg 49d7333d5e implement highlight for buttons with no border 2018-04-30 23:18:59 +07:00
Serg cd69bc341c fix margin for some buttons 2018-04-30 22:54:15 +07:00
Serg 453895f395 fix wrong title while first execution 2018-04-30 18:53:32 +07:00
Serg 12796d6387 fix for colors in non-bordered buttons 2018-04-30 18:40:26 +07:00
Anton Palgunov f65e72faed Merge pull request #57 from Toxblh/revert-56-master
Revert "tapAction and longTapAction in config now have nested values"
2018-04-30 11:29:52 +01:00
Anton Palgunov df535b2893 Revert "tapAction and longTapAction in config now have nested values" 2018-04-30 11:29:42 +01:00
Anton Palgunov 574ca0fb84 Merge pull request #56 from ad/master
tapAction and longTapAction in config now have nested values
2018-04-30 11:08:28 +01:00
ad 4a03ba79ec + background option for button
# Conflicts:
#	MTMR/CustomButtonTouchBarItem.swift
#	MTMR/ItemsParsing.swift
2018-04-30 15:43:55 +07:00
ad 907b79965d + hexColor 2018-04-30 15:26:29 +07:00
Serg fbec803d47 introduce bordered parameter, restore 0.13 borders 2018-04-30 15:14:27 +07:00
ad 3f9574245a more constants from ev_keymap.h 2018-04-30 10:15:07 +03:00
ad 2bf6b90b04 * fix for brightness 2018-04-30 10:11:22 +03:00
ad 9d596c6adc Merge remote-tracking branch 'upstream/master' 2018-04-30 09:51:42 +03:00
ad 175e9edb8c Merge branch 'nestedActions' 2018-04-30 09:51:26 +03:00
ad 8e58bb179a tap and longTap 2018-04-30 09:50:59 +03:00
Anton Palgunov d37d9607d3 Merge pull request #55 from ad/master
deinit inputsource observer, button highlight on touch
2018-04-29 23:00:07 +01:00
ad 6b553c4d0d + deinit inputsource observer 2018-04-30 00:57:32 +03:00
ad 788927edb3 + highlighting button background on press 2018-04-30 00:57:09 +03:00
ad 9013779f71 Merge remote-tracking branch 'upstream/master' 2018-04-29 22:51:34 +03:00
toxblh a85507c544 upd Readme, upd Default 2018-04-29 19:44:58 +01:00
toxblh 59aa113995 build.sh upd 2018-04-29 19:27:01 +01:00
Anton Palgunov 9c5a19925b Merge pull request #54 from Toxblh/fix-leaked-items-and-timers
Fix leaking timers
2018-04-29 18:19:55 +01:00
Serg 2aa602d77d recreate touchbar to clear it's cache (items survive otherwise, still doing their job) 2018-04-29 23:37:09 +07:00
ad a1b3f13902 Merge remote-tracking branch 'upstream/master' 2018-04-29 14:59:13 +03:00
Anton Palgunov 60384561d2 Merge pull request #53 from Toxblh/fix-warnings
Fix compiler warnings
2018-04-29 12:55:00 +01:00
Serg 77938340d6 fix compiler warnings 2018-04-29 18:03:30 +07:00
ad 93d2f496d0 * first working 2018-04-29 01:18:05 +03:00
ad 0808f0617a * fix 2018-04-28 02:28:09 +03:00
ad 966a924183 Merge remote-tracking branch 'upstream/master' 2018-04-28 02:23:57 +03:00
ad a648402d04 Merge branch 'master' of https://github.com/ad/MTMR 2018-04-28 02:22:24 +03:00
ad 207af6468e * button highlight on touch 2018-04-28 02:22:05 +03:00
Anton Palgunov 6609a7c806 Merge pull request #49 from ad/master
* NSButtonCell
2018-04-27 23:39:02 +01:00
toxblh 479190d12b fix center line for apple script 2018-04-27 23:38:13 +01:00
ad 5ed1790601 * scrollView baseline fix 2018-04-27 21:53:27 +03:00
toxblh b95f5af95f lead position for image in image+title button 2018-04-27 19:13:55 +01:00
ad c76027ca9d * fix for button.image size 2018-04-27 20:18:22 +03:00
ad 55dc551516 * fix for longAction 2018-04-27 19:17:19 +03:00
ad cf5d8bf546 * staticButton image size 2018-04-27 19:03:58 +03:00
ad 8031108fc9 * font baseline like in original touchbar, styled battery widget (time showing as superscript) 2018-04-27 18:46:29 +03:00
ad d1a577868b * button background 2018-04-27 16:17:40 +03:00
ad 5f02bb2962 * font style 2018-04-27 14:32:07 +03:00
ad cc1332dd02 * button font color 2018-04-27 14:05:13 +03:00
ad a0dbfca828 * input source widget image size 2018-04-27 12:55:54 +03:00
ad 76bdf12745 * fixed switching input source 2018-04-26 22:50:38 +03:00
ad e1268d0d65 * NSButtonCell 2018-04-26 22:36:12 +03:00
toxblh 9c1e5f5032 Merge branch 'master' of https://github.com/Toxblh/MTMR 2018-04-26 14:54:36 +01:00
toxblh 88cde9256a v0.13.2 2018-04-26 14:54:24 +01:00
Anton Palgunov d974f92e3d brew instruction 2018-04-25 13:48:39 +01:00
toxblh 4b8ef1cd96 Merge remote-tracking branch 'origin/master' 2018-04-24 22:54:12 +01:00
toxblh e65c57dbaa If level a battery less 10% it will be red. 2018-04-24 22:53:56 +01:00
Anton Palgunov babc8451e0 Merge pull request #48 from jstart/patch-1
Spelling :)
2018-04-24 22:36:20 +01:00
Christopher Truman 7698ea1c8e Spelling :) 2018-04-24 14:10:18 -07:00
Anton Palgunov aa95d1a1bd Merge pull request #46 from ad/master
* trying to terminate app before forceTerminate (to prevent loosing of unsaved data)
2018-04-24 13:36:55 +01:00
ad 3fe6c22068 * trying to terminate app before forceTerminate (to prevent loosing of unsaved data) 2018-04-24 13:19:22 +03:00
toxblh 5ab19ee27b v0.13.1 2018-04-24 11:14:29 +01:00
Anton Palgunov ae4b0aa870 Merge pull request #45 from ad/master
Allow multiple instances of currency and weather widgets (different schedulers for one type widget)
2018-04-24 10:59:22 +01:00
ad 1e2043ebda * allow multiple instances of currency and weather widgets (different schedulers for one type widget) 2018-04-24 12:49:36 +03:00
Anton Palgunov 409b8d8b46 Merge pull request #44 from ad/master
* dock widget improvements
2018-04-24 10:47:34 +01:00
ad 8bccbf3834 * dock widget improvements 2018-04-23 20:09:05 +03:00
toxblh 1b5ae00493 v0.13 2018-04-23 13:09:48 +01:00
toxblh 22c315d631 Parse JSON with comments. 2018-04-23 13:08:55 +01:00
toxblh ffbbafa9d9 Fixed central sort. Close #33 2018-04-23 00:54:29 +01:00
toxblh 719a212913 disable parse with comments now, need improve speed loading for JSONC 2018-04-22 23:34:32 +01:00
toxblh 651d883672 + delete button. Close #37 2018-04-22 23:31:05 +01:00
Anton Palgunov 9853e0448f Merge pull request #41 from ad/master
Switch input source widget
2018-04-22 19:38:22 +01:00
ad 0f41638d0f * file moved to new location 2018-04-22 19:28:23 +03:00
ad f6e5b3ca09 Merge branch 'inputSourceWidget' 2018-04-22 19:25:49 +03:00
ad 04b821510b + switch input source by tap 2018-04-22 19:22:45 +03:00
ad d632bd3ee7 + show input source 2018-04-22 17:55:20 +03:00
toxblh aa341b818f reorginize project 2018-04-22 15:20:00 +01:00
Anton Palgunov a513c0083a Merge pull request #40 from ad/replaceTimers
Weather and Currency timers replaced with NSBackgroundActivityScheduler (more energy efficiency)
2018-04-22 11:42:23 +01:00
ad 683cc4c9f4 Weather and Currency timers replaced with NSBackgroundActivityScheduler (more energy efficiency) 2018-04-22 13:38:16 +03:00
Anton Palgunov 3c171b449a Merge pull request #39 from ad/stripComments
+ strip comments in config file
2018-04-22 10:17:34 +01:00
ad b4f62f6be1 + strip comments in config file 2018-04-22 12:15:04 +03:00
Anton Palgunov 8a9b40a030 Merge pull request #38 from ad/master
+ persistent app in dock (press for 0.5 sec), close app (press for 2.0 sec). Reload view if default config changed (write event fired)
2018-04-21 20:16:59 +01:00
ad 3375308386 + reload if default config changed (write event fired) 2018-04-21 20:50:22 +03:00
ad 2fc9dbccd8 Merge remote-tracking branch 'upstream/master' 2018-04-21 17:22:19 +03:00
ad 051a303f96 + persistent app in dock (press for 0.5 sec), close app (press for 2.0 sec) 2018-04-21 16:40:43 +03:00
Anton Palgunov 41a859aea3 Merge pull request #32 from ad/master
Background color parameter moved to CustomButtonTouchBarItem
2018-04-21 13:16:36 +01:00
ad bb978f2de5 Background color parameter moved to CustomButtonTouchBarItem for future ability to change from config file 2018-04-21 15:01:09 +03:00
Anton Palgunov 9bf42dfb79 Merge pull request #31 from ad/master
Action for battery plugin
2018-04-21 12:08:01 +01:00
ad 641eb18b89 + longAction (action for long press on widgets, now working only with longUrl). TODO: nested options for action/longAction like for image/sourse/actionAppleScript 2018-04-21 10:20:12 +03:00
ad d76e16b9e6 * action for battery plugin 2018-04-21 08:27:02 +03:00
ad 9abb17ee76 Merge remote-tracking branch 'upstream/master' 2018-04-21 08:19:44 +03:00
toxblh 4b39b000e0 Upd default. Readme. Fixed reload for central scroller. 2018-04-21 01:43:37 +01:00
toxblh 9b86ed1dee update Readme 2018-04-21 00:23:48 +01:00
toxblh 7874433d9b Added Native Battery 2018-04-21 00:22:45 +01:00
toxblh d4108d848e merge stash 2018-04-20 22:57:51 +01:00
toxblh f78f82893a merged master 2018-04-20 22:56:47 +01:00
toxblh 05eabdbd0a Added Button mute { "type": "mute", "width": 40, "align": "right" }, #23 2018-04-20 22:54:38 +01:00
toxblh 4109f6efd4 delete .DS_Store 2018-04-20 22:50:36 +01:00
ad bbbbf5b5a4 Merge remote-tracking branch 'upstream/master' 2018-04-20 21:01:42 +03:00
Anton Palgunov 8513ed0fa3 Merge pull request #28 from Toxblh/openUrl
Open url action
2018-04-20 18:42:23 +01:00
ad 0e2a958b5c Merge remote-tracking branch 'upstream/master' 2018-04-20 16:26:28 +03:00
Daniel Apatin ac234aa4bf Fix for temperature value with decimals 2018-04-20 16:24:00 +03:00
Daniel Apatin e6d84888ba Update TouchBarController.swift 2018-04-20 15:21:30 +03:00
Daniel Apatin 2aecb2ffb3 Update ItemsParsing.swift 2018-04-20 15:20:01 +03:00
ad cd8b595552 - 2018-04-20 14:42:36 +03:00
ad 76934e6ff0 + action for currency, weather, battery
It will allow to process custom action on click by widget
2018-04-20 14:26:26 +03:00
Daniel Apatin 3e35e03bfb + action for currency, weather, battery
It will allow to process custom action on click by widget
2018-04-20 14:25:31 +03:00
ad 6f3425f4c3 Merge branch 'master' into actionURL 2018-04-20 14:20:24 +03:00
ad 0c007b7940 + action openURL 2018-04-20 13:53:25 +03:00
ad a7060b74e4 * weather and currency now is CustomButtonTouchBarItem 2018-04-20 13:42:08 +03:00
Daniel Apatin 92c4eed4ed Update WeatherBarItem.swift 2018-04-20 13:35:41 +03:00
Daniel Apatin 94d519cd65 Update CurrencyBarItem.swift 2018-04-20 13:35:21 +03:00
Daniel Apatin 7341136897 Update TouchBarController.swift 2018-04-20 13:34:56 +03:00
Daniel Apatin 4459a95691 Merge pull request #27 from Toxblh/ad-patch-weather
+ text and image sets for weather condition  ad committed
2018-04-20 11:19:52 +03:00
Daniel Apatin 7bc8458d8b Update WeatherBarItem.swift 2018-04-20 11:18:11 +03:00
Daniel Apatin bfc4dcf22c Update ItemsParsing.swift 2018-04-20 11:17:29 +03:00
Daniel Apatin 4eb14ea166 Update TouchBarController.swift 2018-04-20 11:16:50 +03:00
ad c15488b61f + text and image sets for weather condition 2018-04-20 11:13:03 +03:00
toxblh 33bb3b8d65 Merge remote-tracking branch 'origin/master' into batteryButton 2018-04-19 20:53:41 +01:00
Daniel Apatin c5a7a3142b Fixed currency formatting (trailing zero), added colours 2018-04-19 21:54:12 +03:00
ad 5b08861dec * fixed formatting (trailing zero), added colours 2018-04-19 21:50:06 +03:00
ad 82f192a5c0 Merge branch 'weatherWidget' 2018-04-19 19:01:22 +03:00
ad 9230b1c3e6 Merge branch 'currencyWidget' 2018-04-19 19:01:14 +03:00
ad 647e0accea Merge branch 'openPresetFile' 2018-04-19 19:01:04 +03:00
Anton Palgunov 0ce6648bf9 Merge pull request #26 from ad/master
fixed deminiaturization and Finder opening
2018-04-19 16:59:36 +01:00
ad fe3e647b56 Merge remote-tracking branch 'upstream/master' 2018-04-19 18:54:14 +03:00
ad fe07ea3b46 * fixed deminiaturization and Finder opening 2018-04-19 18:53:29 +03:00
toxblh 4b22514969 v0.10 - added open preset, better work reload 2018-04-19 16:23:27 +01:00
Anton Palgunov 03bcafd5ff Merge pull request #25 from ad/openPresetFile
+ open preset from dialog (not replacing default file)
2018-04-19 16:19:45 +01:00
ad fdd30413c4 * open dialog at default configs directory 2018-04-19 18:18:54 +03:00
ad 3a57269510 + open preset from dialog (not replacing default file) 2018-04-19 18:01:16 +03:00
Anton Palgunov e9de35ee37 Merge pull request #24 from ad/currencyWidget
* currency list
2018-04-19 12:35:43 +01:00
Anton Palgunov e027c640f7 Merge pull request #22 from ad/weatherWidget
Weather widget / volume slider fix / dock shadow
2018-04-19 12:27:44 +01:00
ad 242e2d3c22 * currency list 2018-04-18 22:14:10 +03:00
Daniel Apatin b3500684d4 Merge pull request #1 from ad/currencyWidget
+ currency widget
2018-04-18 19:53:04 +03:00
ad 42c1ff27c4 + currency widget
{
        "type": "currency",
        "refreshInterval": 600,
        "align": "right",
        "from": "USD",
        "to": "RUB"
    },
2018-04-18 19:50:23 +03:00
ad 4c16321a4a + weather widget 2018-04-18 18:27:28 +03:00
ad adaed36fbd Merge remote-tracking branch 'upstream/master' 2018-04-18 16:20:36 +03:00
toxblh f724a0d355 Draft. Has issues 2018-04-18 09:51:22 +01:00
toxblh 6b2e06b967 update readme 2018-04-17 18:00:59 +01:00
toxblh 4663eb9b27 v0.9 dock plugin 2018-04-17 09:35:24 +01:00
ad 7774b1fa5e * add shadow to app scrubber 2018-04-17 10:55:21 +03:00
ad 20699e9de7 * removed unused code 2018-04-16 23:42:44 +03:00
27 changed files with 1405 additions and 253 deletions
Vendored
BIN
View File
Binary file not shown.
+2
View File
@@ -1,3 +1,5 @@
.DS_Store
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+36 -12
View File
@@ -18,7 +18,11 @@
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 */; };
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */; };
607EEA4D2087A8DA009DA5F0 /* CurrencyBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */; };
60F7D454208CC31400ABF5D2 /* InputSourceBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */; };
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; };
B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */; };
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B05600D22083E9BB00EB218D /* CustomSlider.swift */; };
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D621205E03F5006E6B86 /* TouchBarController.swift */; };
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; };
@@ -66,7 +70,11 @@
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>"; };
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherBarItem.swift; sourceTree = "<group>"; };
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyBarItem.swift; sourceTree = "<group>"; };
60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputSourceBarItem.swift; sourceTree = "<group>"; };
B0008E542080286C003AD4DD /* SupportHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportHelpers.swift; sourceTree = "<group>"; };
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryBarItem.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 /* CustomButtonTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButtonTouchBarItem.swift; sourceTree = "<group>"; };
@@ -151,26 +159,23 @@
B082B251205C7D8000BC04DC /* MTMR */ = {
isa = PBXGroup;
children = (
B0B88A07208CD12000A2C160 /* Widgets */,
B0B1743B207D6ED40004B740 /* CBridge */,
B0B17426207D6AFE0004B740 /* AppleScripts */,
B082B252205C7D8000BC04DC /* AppDelegate.swift */,
B082B256205C7D8000BC04DC /* Assets.xcassets */,
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */,
B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */,
36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */,
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
B059D621205E03F5006E6B86 /* TouchBarController.swift */,
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */,
B0008E542080286C003AD4DD /* SupportHelpers.swift */,
B082B258205C7D8000BC04DC /* Main.storyboard */,
B082B25B205C7D8000BC04DC /* Info.plist */,
B082B25C205C7D8000BC04DC /* MTMR.entitlements */,
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */,
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */,
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
B082B252205C7D8000BC04DC /* AppDelegate.swift */,
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */,
B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */,
36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */,
B059D621205E03F5006E6B86 /* TouchBarController.swift */,
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */,
B0008E542080286C003AD4DD /* SupportHelpers.swift */,
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */,
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */,
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
B05600D22083E9BB00EB218D /* CustomSlider.swift */,
);
path = MTMR;
@@ -215,6 +220,21 @@
path = CBridge;
sourceTree = "<group>";
};
B0B88A07208CD12000A2C160 /* Widgets */ = {
isa = PBXGroup;
children = (
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */,
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */,
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */,
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */,
);
path = Widgets;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -340,13 +360,17 @@
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
60F7D454208CC31400ABF5D2 /* InputSourceBarItem.swift in Sources */,
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */,
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */,
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */,
607EEA4D2087A8DA009DA5F0 /* CurrencyBarItem.swift in Sources */,
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */,
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */,
B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -0,0 +1,8 @@
<?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>BuildSystemType</key>
<string>Latest</string>
</dict>
</plist>
+50 -2
View File
@@ -11,19 +11,21 @@ import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
private var fileSystemSource: DispatchSourceFileSystemObject?
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
TouchBarController.shared.setupControlStripPresence()
if let button = statusItem.button {
button.image = #imageLiteral(resourceName: "StatusImage")
}
createMenu()
reloadOnDefaultConfigChanged()
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
@objc func openPrefereces(_ sender: Any?) {
@@ -39,14 +41,60 @@ class AppDelegate: NSObject, NSApplicationDelegate {
TouchBarController.shared.createAndUpdatePreset()
}
@objc func openPreset(_ sender: Any?) {
let dialog = NSOpenPanel();
dialog.title = "Choose a items.json file"
dialog.showsResizeIndicator = true
dialog.showsHiddenFiles = true
dialog.canChooseDirectories = false
dialog.canCreateDirectories = false
dialog.allowsMultipleSelection = false
dialog.allowedFileTypes = ["json"]
dialog.directoryURL = NSURL.fileURL(withPath: NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR"), isDirectory: true)
if (dialog.runModal() == NSApplication.ModalResponse.OK) {
let result = dialog.url
if (result != nil) {
let path = result!.path
let jsonData = path.fileData
let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
TouchBarController.shared.createAndUpdatePreset(jsonItems: jsonItems)
}
}
}
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(withTitle: "Open Preset", action: #selector(openPreset(_:)), keyEquivalent: "O")
menu.addItem(NSMenuItem.separator())
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
statusItem.menu = menu
}
func reloadOnDefaultConfigChanged() {
let file = NSURL.fileURL(withPath: NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR/items.json"))
let fd = open(file.path, O_EVTONLY)
self.fileSystemSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fd, eventMask: .write, queue: DispatchQueue(label: "DefaultConfigChanged"))
self.fileSystemSource?.setEventHandler(handler: {
print("Config changed, reloading...")
DispatchQueue.main.async {
TouchBarController.shared.createAndUpdatePreset()
}
})
self.fileSystemSource?.setCancelHandler(handler: {
close(fd)
})
self.fileSystemSource?.resume()
}
}
-118
View File
@@ -1,118 +0,0 @@
//
// 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)
}
}
+9 -5
View File
@@ -5,9 +5,9 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
private let interval: TimeInterval
private var forceHideConstraint: NSLayoutConstraint!
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, onTap: @escaping ()->()) {
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, onTap: @escaping ()->(), onLongTap: @escaping ()->()) {
self.interval = interval
super.init(identifier: identifier, title: "compile", onTap: onTap)
super.init(identifier: identifier, title: "⏳", onTap: onTap, onLongTap: onLongTap)
self.forceHideConstraint = self.view.widthAnchor.constraint(equalToConstant: 0)
guard let script = source.appleScript else {
button.title = "no script"
@@ -18,9 +18,11 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
DispatchQueue.main.async {
var error: NSDictionary?
guard script.compileAndReturnError(&error) else {
print(error?.description ?? "unknown error")
#if DEBUG
print(error?.description ?? "unknown error")
#endif
DispatchQueue.main.async {
self.button.title = "compile error"
self.button.title = "error"
}
return
}
@@ -33,7 +35,9 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
}
func refreshAndSchedule() {
print("refresh happened")
#if DEBUG
print("refresh happened (interval \(self.interval)), self \(self.identifier.rawValue))")
#endif
let scriptResult = self.execute()
DispatchQueue.main.async {
self.button.title = scriptResult
+95 -7
View File
@@ -8,26 +8,114 @@
import Cocoa
class CustomButtonTouchBarItem: NSCustomTouchBarItem {
let tapClosure: () -> ()
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
private let tapClosure: (() -> ())?
private let longTapClosure: (() -> ())?
private(set) var button: NSButton!
private var singleClick: NSClickGestureRecognizer!
private var longClick: NSPressGestureRecognizer!
init(identifier: NSTouchBarItem.Identifier, title: String, onTap callback: @escaping () -> ()) {
init(identifier: NSTouchBarItem.Identifier, title: String, onTap callback: @escaping () -> (), onLongTap callbackLong: @escaping () -> (), bezelColor: NSColor? = .clear) {
self.tapClosure = callback
self.longTapClosure = callbackLong
super.init(identifier: identifier)
button = NSButton(title: title, target: self, action: #selector(didTapped))
button = NSButton(title: title, target: nil, action: nil)
button.cell = CustomButtonCell()
button.isBordered = true
button.bezelStyle = .rounded
button.title = title
self.view = button
longClick = NSPressGestureRecognizer(target: self, action: #selector(handleGestureLong))
longClick.allowedTouchTypes = .direct
longClick.delegate = self
singleClick = NSClickGestureRecognizer(target: self, action: #selector(handleGestureSingle))
singleClick.allowedTouchTypes = .direct
singleClick.delegate = self
self.view.addGestureRecognizer(longClick)
self.view.addGestureRecognizer(singleClick)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func didTapped() {
self.tapClosure()
func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool {
if gestureRecognizer == singleClick && otherGestureRecognizer == longClick {
return false
}
return true
}
@objc func handleGestureSingle(gr: NSClickGestureRecognizer) {
let hf: HapticFeedback = HapticFeedback()
hf.tap(strong: 6)
switch gr.state {
case .ended:
hf.tap(strong: 2)
self.tapClosure?()
break
default:
break
}
}
@objc func handleGestureLong(gr: NSPressGestureRecognizer) {
let hf: HapticFeedback = HapticFeedback()
switch gr.state {
case .began:
if let closure = self.longTapClosure {
hf.tap(strong: 2)
closure()
} else if let closure = self.tapClosure {
hf.tap(strong: 6)
closure()
print("long click")
}
break
default:
break
}
}
}
class CustomButtonCell: NSButtonCell {
init() {
super.init(textCell: "")
}
override func highlight(_ flag: Bool, withFrame cellFrame: NSRect, in controlView: NSView) {
super.highlight(flag, withFrame: cellFrame, in: controlView)
if !self.isBordered {
self.setTitle(self.title, withColor: flag ? .lightGray : .white)
}
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var title: String! {
get {
return self.attributedTitle.string
}
set (newTitle) {
setTitle(newTitle, withColor: .white)
}
}
func setTitle(_ title: String, withColor color: NSColor) {
let attrTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: NSFont.systemFont(ofSize: 15, weight: .regular), .baselineOffset: 1])
attrTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
self.attributedTitle = attrTitle
}
}
+8 -8
View File
@@ -49,19 +49,19 @@ class CustomSliderCell: NSSliderCell {
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 bgRect = aRect
bgRect.size.height = CGFloat(4)
var leftRect = rect
leftRect.size.width = finalWidth
let bg = NSBezierPath(roundedRect: rect, xRadius: barRadius, yRadius: barRadius)
let bg = NSBezierPath(roundedRect: bgRect, xRadius: barRadius, yRadius: barRadius)
NSColor.lightGray.setFill()
bg.fill()
let active = NSBezierPath(roundedRect: leftRect, xRadius: barRadius, yRadius: barRadius)
var activeRect = bgRect
activeRect.size.width = CGFloat((Double(bgRect.size.width) / (self.maxValue - self.minValue)) * self.doubleValue)
let active = NSBezierPath(roundedRect: activeRect, xRadius: barRadius, yRadius: barRadius)
NSColor.darkGray.setFill()
active.fill()
}
+7 -1
View File
@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.8.1</string>
<string>0.14</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
@@ -26,6 +26,12 @@
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2018 Anton Palgunov. All rights reserved.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Weather widget need your location for correct work</string>
<key>NSLocationUsageDescription</key>
<string>Weather widget need your location for correct work</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Weather widget need your location for correct work</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPrincipalClass</key>
+172 -38
View File
@@ -2,25 +2,25 @@ import Foundation
import AppKit
extension Data {
func barItemDefinitions() -> [BarItemDefinition]? {
return try? JSONDecoder().decode([BarItemDefinition].self, from: self)
return try? JSONDecoder().decode([BarItemDefinition].self, from: self.utf8string!.stripComments().data(using: .utf8)!)
}
}
struct BarItemDefinition: Decodable {
let type: ItemType
let action: ActionType
let longAction: LongActionType
let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]
private enum CodingKeys: String, CodingKey {
case type
}
init(type: ItemType, action: ActionType, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) {
init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) {
self.type = type
self.action = action
self.longAction = longAction
self.additionalParameters = additionalParameters
}
@@ -30,66 +30,87 @@ struct BarItemDefinition: Decodable {
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 {
case let (itemType, action, longAction, parameters) = result {
parameters.forEach { additionalParameters[$0] = $1 }
self.init(type: itemType, action: action, additionalParameters: additionalParameters)
self.init(type: itemType, action: action, longAction: longAction, additionalParameters: additionalParameters)
} else {
self.init(type: .staticButton(title: "unknown"), action: .none, additionalParameters: additionalParameters)
self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, additionalParameters: additionalParameters)
}
}
}
class SupportedTypesHolder {
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter])
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, longAction: LongActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter])
private var supportedTypes: [String: ParametersDecoder] = [
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), parameters: [.align: .align(.left)]) },
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), longAction: .none, parameters: [.align: .align(.left)]) },
"delete": { _ in return (item: .staticButton(title: "del"), action: .keyPress(keycode: 117), longAction: .none, parameters: [:])},
"brightnessUp": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
return (item: .staticButton(title: ""), action: .keyPress(keycode: 113), parameters: [.image: imageParameter])
return (item: .staticButton(title: ""), action: .keyPress(keycode: 113), longAction: .none, parameters: [.image: imageParameter])
},
"brightnessDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
return (item: .staticButton(title: ""), action: .keyPress(keycode: 107), parameters: [.image: imageParameter])
return (item: .staticButton(title: ""), action: .keyPress(keycode: 107), longAction: .none, 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])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), longAction: .none, 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])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), longAction: .none, parameters: [.image: imageParameter])
},
"mute": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarAudioOutputMuteTemplate)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), longAction: .none, 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])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), longAction: .none, 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])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), longAction: .none, 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])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), longAction: .none, parameters: [.image: imageParameter])
},
"weather": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval }
enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key ; case icon_type }
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: [:])
let units = try container.decodeIfPresent(String.self, forKey: .units)
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key)
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type)
let action = try ActionType(from: decoder)
let longAction = try LongActionType(from: decoder)
return (item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"), action: action, longAction: longAction, parameters: [:])
},
"currency": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to }
let container = try decoder.container(keyedBy: CodingKeys.self)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
let from = try container.decodeIfPresent(String.self, forKey: .from)
let to = try container.decodeIfPresent(String.self, forKey: .to)
let action = try ActionType(from: decoder)
let longAction = try LongActionType(from: decoder)
return (item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), action: action, longAction: longAction, parameters: [:])
},
"dock": { decoder in
return (item: .dock(), action: .none, parameters: [:])
return (item: .dock(), action: .none, longAction: .none, parameters: [:])
},
"inputsource": { decoder in
return (item: .inputsource(), action: .none, longAction: .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)])
return (item: .volume(), action: .none, longAction: .none, parameters: [.image: .image(source: img)])
} else {
return (item: .volume(), action: .none, parameters: [:])
return (item: .volume(), action: .none, longAction: .none, parameters: [:])
}
},
"brightness": { decoder in
@@ -97,28 +118,20 @@ class SupportedTypesHolder {
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)])
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [.image: .image(source: img)])
} else {
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, parameters: [:])
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .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: [:]) },
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), longAction: .none, parameters: [:]) },
"displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), longAction: .none, parameters: [:])},
]
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: [:])
return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), longAction: try LongActionType(from: decoder), parameters: [:])
}
}
@@ -126,9 +139,9 @@ class SupportedTypesHolder {
supportedTypes[typename] = decoder
}
func register(typename: String, item: ItemType, action: ActionType) {
func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) {
register(typename: typename) { _ in
return (item: item, action: action, parameters: [:])
return (item: item, action: action, longAction: longAction, parameters: [:])
}
}
}
@@ -137,26 +150,41 @@ enum ItemType: Decodable {
case staticButton(title: String)
case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double)
case timeButton(formatTemplate: String)
case battery()
case dock()
case volume()
case brightness(refreshInterval: Double)
case weather(interval: Double, units: String, api_key: String, icon_type: String)
case currency(interval: Double, from: String, to: String)
case inputsource()
private enum CodingKeys: String, CodingKey {
case type
case title
case source
case refreshInterval
case from
case to
case units
case api_key
case icon_type
case formatTemplate
case image
case url
case longUrl
}
enum ItemTypeRaw: String, Decodable {
case staticButton
case appleScriptTitledButton
case timeButton
case battery
case dock
case volume
case brightness
case weather
case currency
case inputsource
}
init(from decoder: Decoder) throws {
@@ -173,6 +201,8 @@ enum ItemType: Decodable {
case .timeButton:
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
self = .timeButton(formatTemplate: template)
case .battery:
self = .battery()
case .dock:
self = .dock()
case .volume:
@@ -180,6 +210,19 @@ enum ItemType: Decodable {
case .brightness:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
self = .brightness(refreshInterval: interval)
case .weather:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
let units = try container.decodeIfPresent(String.self, forKey: .units) ?? "metric"
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
self = .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type)
case .currency:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
self = .currency(interval: interval, from: from, to: to)
case .inputsource:
self = .inputsource()
}
}
}
@@ -191,6 +234,7 @@ enum ActionType: Decodable {
case appleSctipt(source: SourceProtocol)
case shellScript(executable: String, parameters: [String])
case custom(closure: ()->())
case openUrl(url: String)
private enum CodingKeys: String, CodingKey {
case action
@@ -198,6 +242,7 @@ enum ActionType: Decodable {
case actionAppleScript
case executablePath
case shellArguments
case url
}
private enum ActionTypeRaw: String, Decodable {
@@ -205,6 +250,7 @@ enum ActionType: Decodable {
case keyPress
case appleScript
case shellScript
case openUrl
}
init(from decoder: Decoder) throws {
@@ -224,6 +270,62 @@ enum ActionType: Decodable {
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 .some(.openUrl):
let url = try container.decode(String.self, forKey: .url)
self = .openUrl(url: url)
case .none:
self = .none
}
}
}
enum LongActionType: Decodable {
case none
case hidKey(keycode: Int)
case keyPress(keycode: Int)
case appleSctipt(source: SourceProtocol)
case shellScript(executable: String, parameters: [String])
case custom(closure: ()->())
case openUrl(url: String)
private enum CodingKeys: String, CodingKey {
case longAction
case keycode
case actionAppleScript
case executablePath
case shellArguments
case longUrl
}
private enum LongActionTypeRaw: String, Decodable {
case hidKey
case keyPress
case appleScript
case shellScript
case openUrl
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let longType = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction)
switch longType {
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 .some(.openUrl):
let longUrl = try container.decode(String.self, forKey: .longUrl)
self = .openUrl(url: longUrl)
case .none:
self = .none
}
@@ -255,6 +357,28 @@ func ==(lhs: ActionType, rhs: ActionType) -> Bool {
return a == b
case let (.shellScript(a, b), .shellScript(c, d)):
return a == c && b == d
case let (.openUrl(a), .openUrl(b)):
return a == b
default:
return false
}
}
extension LongActionType: Equatable {}
func ==(lhs: LongActionType, rhs: LongActionType) -> Bool {
switch (lhs, rhs) {
case (.none, .none):
return true
case let (.hidKey(a), .hidKey(b)),
let (.keyPress(a), .keyPress(b)):
return a == b
case let (.appleSctipt(a), .appleSctipt(b)):
return a == b
case let (.shellScript(a, b), .shellScript(c, d)):
return a == c && b == d
case let (.openUrl(a), .openUrl(b)):
return a == b
default:
return false
}
@@ -264,6 +388,8 @@ enum GeneralParameter {
case width(_: CGFloat)
case image(source: SourceProtocol)
case align(_: Align)
case bordered(_: Bool)
case background(_:NSColor)
}
struct GeneralParameters: Decodable {
@@ -273,6 +399,8 @@ struct GeneralParameters: Decodable {
case width
case image
case align
case bordered
case background
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
@@ -285,6 +413,12 @@ struct GeneralParameters: Decodable {
}
let align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center
result[.align] = .align(align)
if let borderedFlag = try container.decodeIfPresent(Bool.self, forKey: .bordered) {
result[.bordered] = .bordered(borderedFlag)
}
if let backgroundColor = try container.decodeIfPresent(String.self, forKey: .background)?.hexColor {
result[.background] = .background(backgroundColor)
}
parameters = result
}
}
+2
View File
@@ -58,6 +58,7 @@ func HIDPostAuxKey(_ key: Int) {
// hidsystem/ev_keymap.h
let NX_KEYTYPE_SOUND_UP = 0
let NX_KEYTYPE_SOUND_DOWN = 1
let NX_KEYTYPE_MUTE = 7
let NX_KEYTYPE_BRIGHTNESS_UP = 2
let NX_KEYTYPE_BRIGHTNESS_DOWN = 3
@@ -69,3 +70,4 @@ let NX_KEYTYPE_PREVIOUS = 18
+1
View File
@@ -10,6 +10,7 @@ class ScrollViewItem: NSCustomTouchBarItem {
stackView.orientation = .horizontal
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
scrollView.documentView = stackView
// scrollView.documentView?.bounds.origin = CGPoint(x: 0.0, y: -2.5)
self.view = scrollView
}
+23
View File
@@ -12,6 +12,29 @@ extension String {
func trim() -> String {
return self.trimmingCharacters(in: NSCharacterSet.whitespaces)
}
func stripComments() -> String {
// ((\s|,)\/\*[\s\S]*?\*\/)|(( |, ")\/\/.*)
return self.replacingOccurrences(of: "((\\s|,)\\/\\*[\\s\\S]*?\\*\\/)|(( |, \\\")\\/\\/.*)", with: "", options: .regularExpression)
}
var hexColor: NSColor? {
let hex = trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int = UInt32()
Scanner(string: hex).scanHexInt32(&int)
let a, r, g, b: UInt32
switch hex.count {
case 3: // RGB (12-bit)
(r, g, b, a) = ((int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17, 255)
case 6: // RGB (24-bit)
(r, g, b, a) = (int >> 16, int >> 8 & 0xFF, int & 0xFF, 255)
case 8: // ARGB (32-bit)
(r, g, b, a) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
return nil
}
return NSColor(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
}
}
extension NSImage {
+135 -23
View File
@@ -23,12 +23,20 @@ extension ItemType {
return "com.toxblh.mtmr.appleScriptButton."
case .timeButton(formatTemplate: _):
return "com.toxblh.mtmr.timeButton."
case .battery():
return "com.toxblh.mtmr.battery."
case .dock():
return "com.toxblh.mtmr.dock"
case .volume():
return "com.toxblh.mtmr.volume"
case .brightness(refreshInterval: _):
return "com.toxblh.mtmr.brightness"
case .weather(interval: _, units: _, api_key: _, icon_type: _):
return "com.toxblh.mtmr.weather"
case .currency(interval: _, from: _, to: _):
return "com.toxblh.mtmr.currency"
case .inputsource():
return "com.toxblh.mtmr.inputsource."
}
}
@@ -36,49 +44,62 @@ extension ItemType {
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 touchBar: NSTouchBar!
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
var centerIdentifiers: [NSTouchBarItem.Identifier] = []
var centerItems: [NSTouchBarItem] = []
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
var scrollArea: NSCustomTouchBarItem?
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
private override init() {
super.init()
SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in
self?.dismissTouchBar()
}))
SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in self?.dismissTouchBar()}), longAction: .none)
createAndUpdatePreset()
}
func createAndUpdatePreset() {
func createAndUpdatePreset(jsonItems: [BarItemDefinition]? = nil) {
if let oldBar = self.touchBar {
NSTouchBar.minimizeSystemModalFunctionBar(oldBar)
}
self.touchBar = NSTouchBar()
var jsonItems = jsonItems
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
if (jsonItems == nil) {
jsonItems = readConfig()
}
loadItemDefinitions(jsonItems: jsonItems!)
createItems()
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
return items[identifier]
})
self.centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
self.scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
touchBar.delegate = self
touchBar.defaultItemIdentifiers = self.leftIdentifiers + [.centerScrollArea] + self.rightIdentifiers
touchBar.defaultItemIdentifiers = []
touchBar.defaultItemIdentifiers = self.leftIdentifiers + [centerScrollArea] + self.rightIdentifiers
self.presentTouchBar()
}
func loadItemDefinitions() {
func readConfig() -> [BarItemDefinition]? {
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
let presetPath = appSupportDirectory.appending("/items.json")
if !FileManager.default.fileExists(atPath: presetPath),
@@ -86,11 +107,18 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
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: [:])]
return jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
}
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH-mm-ss"
let time = dateFormatter.string(from: Date())
for item in jsonItems {
let identifierString = item.type.identifierBase.appending(UUID().uuidString)
let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString)
let identifier = NSTouchBarItem.Identifier(identifierString)
itemDefinitions[identifier] = item
if item.align == .left {
@@ -99,6 +127,9 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
if item.align == .right {
rightIdentifiers.append(identifier)
}
if item.align == .center {
centerIdentifiers.append(identifier)
}
}
}
@@ -129,8 +160,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
}
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
if identifier == .centerScrollArea {
return ScrollViewItem(identifier: identifier, items: centerItems)
if identifier == centerScrollArea {
return self.scrollArea
}
guard let item = self.items[identifier],
@@ -143,15 +174,18 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? {
let action = self.action(forItem: item)
let longAction = self.longAction(forItem: item)
var barItem: NSTouchBarItem!
switch item.type {
case .staticButton(title: let title):
barItem = CustomButtonTouchBarItem(identifier: identifier, title: title, onTap: action)
barItem = CustomButtonTouchBarItem(identifier: identifier, title: title, onTap: action, onLongTap: longAction, bezelColor: NSColor.controlColor)
case .appleScriptTitledButton(source: let source, refreshInterval: let interval):
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action)
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action, onLongTap: longAction)
case .timeButton(formatTemplate: let template):
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template)
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, onTap: action, onLongTap: longAction)
case .battery():
barItem = BatteryBarItem(identifier: identifier, onTap: action, onLongTap: longAction)
case .dock:
barItem = AppScrubberTouchBarItem(identifier: identifier)
case .volume:
@@ -166,16 +200,42 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
} else {
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
}
case .weather(interval: let interval, units: let units, api_key: let api_key, icon_type: let icon_type):
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type, onTap: action, onLongTap: longAction)
case .currency(interval: let interval, from: let from, to: let to):
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, onTap: action, onLongTap: longAction)
case .inputsource():
barItem = InputSourceBarItem(identifier: identifier, onTap: action, onLongTap: longAction)
}
if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
widthBarItem.setWidth(value: value)
}
if case .bordered(let bordered)? = item.additionalParameters[.bordered], let item = barItem as? CustomButtonTouchBarItem {
item.button.isBordered = bordered
item.button.bezelStyle = bordered ? .rounded : .inline
}
if case .background(let color)? = item.additionalParameters[.background], let item = barItem as? CustomButtonTouchBarItem {
if item.button.cell?.isBordered == false {
let newCell = NSButtonCell()
newCell.title = item.button.title
item.button.cell = newCell
item.button.cell?.isBordered = true
}
item.button.bezelColor = color
(item.button.cell as? NSButtonCell)?.backgroundColor = color
}
if case .image(let source)? = item.additionalParameters[.image], let item = barItem as? CustomButtonTouchBarItem {
let button = item.button!
button.image = source.image
button.imageScaling = .scaleProportionallyDown
button.imagePosition = .imageLeading
if (button.title == "") {
button.imagePosition = .imageOnly
}
button.imageHugsTitle = true
button.bezelColor = .clear
button.image = source.image
}
return barItem
}
@@ -205,6 +265,16 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
task.arguments = parameters
task.launch()
}
case .openUrl(url: let url):
return {
if let url = URL(string: url), NSWorkspace.shared.open(url) {
#if DEBUG
print("URL was successfully opened")
#endif
} else {
print("error", url)
}
}
case .custom(closure: let closure):
return closure
case .none:
@@ -212,6 +282,48 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
}
}
func longAction(forItem item: BarItemDefinition) -> ()->() {
switch item.longAction {
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 .openUrl(url: let url):
return {
if let url = URL(string: url), NSWorkspace.shared.open(url) {
#if DEBUG
print("URL was successfully opened")
#endif
} else {
print("error", url)
}
}
case .custom(closure: let closure):
return closure
case .none:
return {}
}
}
}
protocol CanSetWidth {
+254
View File
@@ -0,0 +1,254 @@
//
// AppScrubberTouchBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 18.04.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
import Cocoa
class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrubberDataSource {
private var scrubber: NSScrubber!
private let hf: HapticFeedback = HapticFeedback()
private var timer: Timer!
private var ticks: Int = 0
private let minTicks: Int = 5
private let maxTicks: Int = 20
private var lastSelected: Int = 0
private var persistentAppIdentifiers: [String] = []
private var runningAppsIdentifiers: [String] = []
private var frontmostApplicationIdentifier: String? {
get {
guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil }
return frontmostId
}
}
private var applications: [DockItem] = []
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: 36, height: 32)
layout.itemSpacing = 2
scrubber.scrubberLayout = layout
scrubber.selectionBackgroundStyle = .roundedBackground
scrubber.showsAdditionalContentIndicators = true
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)
if let persistent = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.dock.persistent") {
self.persistentAppIdentifiers = persistent
}
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()
let index = newApplications.index {
$0.bundleIdentifier == frontmostApplicationIdentifier
}
applications = newApplications
applications += getDockPersistentAppsList()
scrubber.reloadData()
scrubber.selectedIndex = index ?? 0
}
public func numberOfItems(for scrubber: NSScrubber) -> Int {
return applications.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
let app = applications[index]
if let icon = app.icon {
item.image = icon
item.image.size = NSSize(width: 26, height: 26)
}
item.removeFromSuperview()
let dotView = NSView(frame: .zero)
dotView.wantsLayer = true
if self.runningAppsIdentifiers.contains(app.bundleIdentifier!) {
dotView.layer?.backgroundColor = NSColor.white.cgColor
} else {
dotView.layer?.backgroundColor = NSColor.black.cgColor
}
dotView.layer?.cornerRadius = 1.5
dotView.setFrameOrigin(NSPoint(x: 17, y: 1))
dotView.frame.size = NSSize(width: 3, height: 3)
item.addSubview(dotView)
return item
}
public func didBeginInteracting(with scrubber: NSScrubber) {
stopTimer()
self.ticks = 0
self.timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(checkTimer), userInfo: nil, repeats: true)
}
@objc private func checkTimer() {
self.ticks += 1
if (self.ticks == minTicks) {
hf.tap(strong: 2)
}
if (self.ticks > maxTicks) {
stopTimer()
hf.tap(strong: 6)
}
}
private func stopTimer() {
self.timer?.invalidate()
self.timer = nil
self.lastSelected = 0
}
public func didCancelInteracting(with scrubber: NSScrubber) {
stopTimer()
}
public func didFinishInteracting(with scrubber: NSScrubber) {
stopTimer()
if (ticks == 0) {
return
}
if (self.ticks >= minTicks && scrubber.selectedIndex > 0) {
self.longPress(selected: scrubber.selectedIndex)
return
}
let bundleIdentifier = applications[scrubber.selectedIndex].bundleIdentifier
if bundleIdentifier!.contains("file://") {
NSWorkspace.shared.openFile(bundleIdentifier!.replacingOccurrences(of: "file://", with: ""))
} else {
NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIdentifier!, options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil)
hf.tap(strong: 6)
}
updateRunningApplication()
// NB: if you can't open app which on another space, try to check mark
// "When switching to an application, switch to a Space with open windows for the application"
// in Mission control settings
}
private func longPress(selected: Int) {
let bundleIdentifier = applications[selected].bundleIdentifier
if (self.ticks > maxTicks) {
if let processIdentifier = applications[selected].pid {
if !(NSRunningApplication(processIdentifier: processIdentifier)?.terminate())! {
NSRunningApplication(processIdentifier: processIdentifier)?.forceTerminate()
}
}
} else {
hf.tap(strong: 6)
if let index = self.persistentAppIdentifiers.index(of: bundleIdentifier!) {
self.persistentAppIdentifiers.remove(at: index)
} else {
self.persistentAppIdentifiers.append(bundleIdentifier!)
}
UserDefaults.standard.set(self.persistentAppIdentifiers, forKey: "com.toxblh.mtmr.dock.persistent")
UserDefaults.standard.synchronize()
}
self.ticks = 0
updateRunningApplication()
}
private func launchedApplications() -> [DockItem] {
self.runningAppsIdentifiers = []
var returnable: [DockItem] = []
for app in NSWorkspace.shared.runningApplications {
guard app.activationPolicy == NSApplication.ActivationPolicy.regular else { continue }
guard let bundleIdentifier = app.bundleIdentifier else { continue }
self.runningAppsIdentifiers.append(bundleIdentifier)
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier), pid: app.processIdentifier)
returnable.append(dockItem)
}
return returnable
}
public func getIcon(forBundleIdentifier bundleIdentifier: String? = nil, orPath path: String? = nil, orType type: String? = nil) -> NSImage {
if bundleIdentifier != nil {
if let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleIdentifier!) {
return NSWorkspace.shared.icon(forFile: appPath)
}
}
if path != nil {
return NSWorkspace.shared.icon(forFile: path!)
}
let genericIcon = NSImage(contentsOfFile: "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericDocumentIcon.icns")
return genericIcon ?? NSImage(size: .zero)
}
public func getDockPersistentAppsList() -> [DockItem] {
var returnable: [DockItem] = []
for bundleIdentifier in persistentAppIdentifiers {
if !self.runningAppsIdentifiers.contains(bundleIdentifier) {
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier))
returnable.append(dockItem)
}
}
return returnable
}
}
public class DockItem: NSObject {
var bundleIdentifier: String!, icon: NSImage!, pid: Int32!
convenience init(bundleIdentifier: String, icon: NSImage, pid: Int32? = nil) {
self.init()
self.bundleIdentifier = bundleIdentifier
self.icon = icon
self.pid = pid
}
}
+145
View File
@@ -0,0 +1,145 @@
//
// BatteryBarItem.swift
// MTMR
//
// Created by Anton Palgunov on 18/04/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import IOKit.ps
import Foundation
class BatteryBarItem: CustomButtonTouchBarItem {
private var timer: Timer!
init(identifier: NSTouchBarItem.Identifier, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
super.init(identifier: identifier, title: " ", onTap: onTap, onLongTap: onLongTap)
self.view = button
let batteryInfo = BatteryInfo(button: button)
batteryInfo.start()
batteryInfo.updateInfo()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class BatteryInfo: NSObject {
var current: Int = 0
var timeToEmpty: Int = 0
var timeToFull: Int = 0
var isCharged: Bool = false
var isCharging: Bool = false
var ACPower: String = ""
var timeRemaining: String = ""
var button: NSButton
var loop:CFRunLoopSource?
init(button: NSButton) {
self.button = button
super.init()
self.start()
}
func start() {
let opaque = Unmanaged.passRetained(self).toOpaque()
let context = UnsafeMutableRawPointer(opaque)
loop = IOPSNotificationCreateRunLoopSource({ (context) in
guard let ctx = context else {
return
}
let watcher = Unmanaged<BatteryInfo>.fromOpaque(ctx).takeUnretainedValue()
watcher.updateInfo()
}, context).takeRetainedValue() as CFRunLoopSource
CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
}
func stop() {
if !(self.loop != nil) {
return
}
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), self.loop, CFRunLoopMode.defaultMode)
self.loop = nil
}
func getPSInfo() {
let psInfo = IOPSCopyPowerSourcesInfo().takeRetainedValue()
let psList = IOPSCopyPowerSourcesList(psInfo).takeRetainedValue() as [CFTypeRef]
for ps in psList {
if let psDesc = IOPSGetPowerSourceDescription(psInfo, ps).takeUnretainedValue() as? [String: Any] {
let current = psDesc[kIOPSCurrentCapacityKey]
if (current != nil) {
self.current = current as! Int
}
let timeToEmpty = psDesc[kIOPSTimeToEmptyKey]
if (timeToEmpty != nil) {
self.timeToEmpty = timeToEmpty as! Int
}
let timeToFull = psDesc[kIOPSTimeToFullChargeKey]
if (timeToFull != nil) {
self.timeToFull = timeToFull as! Int
}
let isCharged = psDesc[kIOPSIsChargedKey]
if (isCharged != nil) {
self.isCharged = isCharged as! Bool
}
let isCharging = psDesc[kIOPSIsChargingKey]
if (isCharging != nil) {
self.isCharging = isCharging as! Bool
}
let ACPower = psDesc[kIOPSPowerSourceStateKey]
if (ACPower != nil) {
self.ACPower = ACPower as! String
}
}
}
}
func getFormattedTime(time: Int) -> String {
if (time > 0) {
let timeFormatted = NSString(format: " %d:%02d", time / 60, time % 60) as String
return timeFormatted
}
return ""
}
public func updateInfo() {
var title = ""
self.getPSInfo()
if ACPower == "AC Power" {
if current < 100 {
title += "⚡️"
}
timeRemaining = getFormattedTime(time: timeToFull)
} else {
timeRemaining = getFormattedTime(time: timeToEmpty)
}
title += String(current) + "%"
var color = NSColor.white
if current <= 10 && ACPower != "AC Power" {
color = NSColor.red
}
let regularFont = button.attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont, .baselineOffset: 1])
let newTitleSecond = NSMutableAttributedString(string: timeRemaining as String, attributes: [NSAttributedStringKey.foregroundColor: color, NSAttributedStringKey.font: NSFont.systemFont(ofSize: 8, weight: .regular), NSAttributedStringKey.baselineOffset: 7])
newTitle.append(newTitleSecond)
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
button.attributedTitle = newTitle
}
}
+117
View File
@@ -0,0 +1,117 @@
//
// CurrencyBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 18.04.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
import CoreLocation
class CurrencyBarItem: CustomButtonTouchBarItem {
private let activity: NSBackgroundActivityScheduler
private var prefix: String
private var from: String
private var to: String
private var oldValue: Float32!
private let currencies = [
"USD": "$",
"EUR": "€",
"RUB": "₽",
"JPY": "¥",
"GBP": "₤",
"CAD": "$",
"KRW": "₩",
"CNY": "¥",
"AUD": "$",
"BRL": "R$",
"IDR": "Rp",
"MXN": "$",
"SGD": "$",
"CHF": "Fr.",
"BTC": "฿",
"LTC": "Ł",
"ETH": "Ξ",
]
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval
self.from = from
self.to = to
if let prefix = currencies[from] {
self.prefix = prefix
} else {
self.prefix = from
}
super.init(identifier: identifier, title: "⏳", onTap: onTap, onLongTap: onLongTap)
self.view = button
activity.repeats = true
activity.qualityOfService = .utility
activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in
self.updateCurrency()
completion(NSBackgroundActivityScheduler.Result.finished)
}
updateCurrency()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func updateCurrency() {
let urlRequest = URLRequest(url: URL(string: "https://api.coinbase.com/v2/exchange-rates?currency=\(from)")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
var value: Float32!
if let data_array = json["data"] as? [String : AnyObject] {
if let rates = data_array["rates"] as? [String : AnyObject] {
if let item = rates["\(self.to)"] as? String {
value = Float32(item)
}
}
}
if value != nil {
DispatchQueue.main.async {
self.setCurrency(value: value!)
}
}
} catch let jsonError {
print(jsonError.localizedDescription)
}
}
}
task.resume()
}
func setCurrency(value: Float32) {
var color = NSColor.white
if let oldValue = self.oldValue {
if oldValue < value {
color = NSColor.green
} else if oldValue > value {
color = NSColor.red
}
}
self.oldValue = value
let title = String(format: "%@%.2f", self.prefix, value)
let regularFont = button.attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont])
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
button.attributedTitle = newTitle
}
}
+144
View File
@@ -0,0 +1,144 @@
//
// InputSourceBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 22.04.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
class InputSourceBarItem: CustomButtonTouchBarItem {
fileprivate var notificationCenter: CFNotificationCenter
let buttonSize = NSSize(width: 21, height: 21)
init(identifier: NSTouchBarItem.Identifier, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
notificationCenter = CFNotificationCenterGetDistributedCenter();
super.init(identifier: identifier, title: "⏳", onTap: onTap, onLongTap: onLongTap)
observeIputSourceChangedNotification();
textInputSourceDidChange()
self.button.cell?.action = #selector(switchInputSource)
self.button.action = #selector(switchInputSource)
self.button.frame.size = buttonSize
self.button.bounds.size = buttonSize
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()));
}
@objc override func handleGestureSingle(gr: NSClickGestureRecognizer) {
super.handleGestureSingle(gr: gr)
switchInputSource()
}
@objc public func textInputSourceDidChange() {
let currentSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
var iconImage: NSImage? = nil
if let imageURL = currentSource.iconImageURL {
if let image = NSImage(contentsOf: imageURL) {
iconImage = image
}
}
if iconImage == nil, let iconRef = currentSource.iconRef {
iconImage = NSImage(iconRef: iconRef)
}
if (iconImage != nil) {
self.button.cell?.image = iconImage
self.button.cell?.image?.size = buttonSize
} else {
self.button.title = currentSource.name
}
}
@objc private func switchInputSource() {
var inputSources: [TISInputSource] = []
let currentSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
let inputSourceNSArray = TISCreateInputSourceList(nil, false).takeRetainedValue() as NSArray
let elements = inputSourceNSArray as! [TISInputSource]
inputSources = elements.filter({
$0.category == TISInputSource.Category.keyboardInputSource && $0.isSelectable
})
for item in inputSources {
if (item.id != currentSource.id) {
TISSelectInputSource(item)
break
}
}
}
@objc public func observeIputSourceChangedNotification(){
let callback: CFNotificationCallback = { center, observer, name, object, info in
let mySelf = Unmanaged<InputSourceBarItem>.fromOpaque(observer!).takeUnretainedValue()
mySelf.textInputSourceDidChange()
}
CFNotificationCenterAddObserver(notificationCenter,
UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()),
callback,
kTISNotifySelectedKeyboardInputSourceChanged,
nil,
.deliverImmediately)
}
}
extension TISInputSource {
enum Category {
static var keyboardInputSource: String {
return kTISCategoryKeyboardInputSource as String
}
}
private func getProperty(_ key: CFString) -> AnyObject? {
let cfType = TISGetInputSourceProperty(self, key)
if (cfType != nil) {
return Unmanaged<AnyObject>.fromOpaque(cfType!).takeUnretainedValue()
} else {
return nil
}
}
var id: String {
return getProperty(kTISPropertyInputSourceID) as! String
}
var name: String {
return getProperty(kTISPropertyLocalizedName) as! String
}
var category: String {
return getProperty(kTISPropertyInputSourceCategory) as! String
}
var isSelectable: Bool {
return getProperty(kTISPropertyInputSourceIsSelectCapable) as! Bool
}
var sourceLanguages: [String] {
return getProperty(kTISPropertyInputSourceLanguages) as! [String]
}
var iconImageURL: URL? {
return getProperty(kTISPropertyIconImageURL) as! URL?
}
var iconRef: IconRef? {
return OpaquePointer(TISGetInputSourceProperty(self, kTISPropertyIconRef)) as IconRef?
}
}
@@ -1,13 +1,13 @@
import Cocoa
class TimeTouchBarItem: NSCustomTouchBarItem {
class TimeTouchBarItem: CustomButtonTouchBarItem {
private let dateFormatter = DateFormatter()
private var timer: Timer!
private let button = NSButton(title: "", target: nil, action: nil)
// private let button = NSButton(title: "", target: nil, action: nil)
init(identifier: NSTouchBarItem.Identifier, formatTemplate: String) {
init(identifier: NSTouchBarItem.Identifier, formatTemplate: String, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
dateFormatter.setLocalizedDateFormatFromTemplate(formatTemplate)
super.init(identifier: identifier)
super.init(identifier: identifier, title: " ", onTap: onTap, onLongTap: onLongTap)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
self.view = button
button.bezelColor = .clear
@@ -14,9 +14,7 @@ class VolumeViewController: NSCustomTouchBarItem {
mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster)
addListenerBlock(listenerBlock: audioObjectPropertyListenerBlock,
onAudioObjectID: defaultDeviceID,
forPropertyAddress: &forPropertyAddress)
AudioObjectAddPropertyListenerBlock(defaultDeviceID, &forPropertyAddress, nil, audioObjectPropertyListenerBlock)
if (image == nil) {
sliderItem = CustomSlider()
@@ -32,27 +30,9 @@ class VolumeViewController: NSCustomTouchBarItem {
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
DispatchQueue.main.async {
self.sliderItem.floatValue = self.getInputGain() * 100
}
}
+141
View File
@@ -0,0 +1,141 @@
//
// WeatherBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 18.04.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
import CoreLocation
class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
private let activity: NSBackgroundActivityScheduler
private var units: String
private var api_key: String
private var units_str = "°F"
private var prev_location: CLLocation!
private var location: CLLocation!
private let iconsImages = ["01d": "☀️", "01n": "☀️", "02d": "⛅️", "02n": "⛅️", "03d": "☁️", "03n": "☁️", "04d": "☁️", "04n": "☁️", "09d": "⛅️", "09n": "⛅️", "10d": "🌦", "10n": "🌦", "11d": "🌩", "11n": "🌩", "13d": "❄️", "13n": "❄️", "50d": "🌫", "50n": "🌫"]
private let iconsText = ["01d": "☀", "01n": "☀", "02d": "☁", "02n": "☁", "03d": "☁", "03n": "☁", "04d": "☁", "04n": "☁", "09d": "☂", "09n": "☂", "10d": "☂", "10n": "☂", "11d": "☈", "11n": "☈", "13d": "☃", "13n": "☃", "50d": "♨", "50n": "♨"]
private var iconsSource: Dictionary<String, String>
private var manager:CLLocationManager!
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text", onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval
self.units = units
self.api_key = api_key
if self.units == "metric" {
units_str = "°C"
}
if self.units == "imperial" {
units_str = "°F"
}
if icon_type == "images" {
iconsSource = iconsImages
} else {
iconsSource = iconsText
}
super.init(identifier: identifier, title: "⏳", onTap: onTap, onLongTap: onLongTap)
self.view = button
let status = CLLocationManager.authorizationStatus()
if status == .restricted || status == .denied {
print("User permission not given")
return
}
if !CLLocationManager.locationServicesEnabled() {
print("Location services not enabled");
return
}
activity.repeats = true
activity.qualityOfService = .utility
activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in
self.updateWeather()
completion(NSBackgroundActivityScheduler.Result.finished)
}
updateWeather()
manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
manager.startUpdatingLocation()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func updateWeather() {
if self.location != nil {
let urlRequest = URLRequest(url: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)&units=\(self.units)&appid=\(self.api_key)")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
// print(json)
var temperature: Int!
var condition_icon = ""
if let main = json["main"] as? [String : AnyObject] {
if let temp = main["temp"] as? Double {
temperature = Int(temp)
}
}
if let weather = json["weather"] as? NSArray, let item = weather[0] as? NSDictionary {
let icon = item["icon"] as! String
if let test = self.iconsSource[icon] {
condition_icon = test
}
}
if temperature != nil {
DispatchQueue.main.async {
self.setWeather(text: "\(condition_icon) \(temperature!)\(self.units_str)")
}
}
} catch let jsonError {
print(jsonError.localizedDescription)
}
}
}
task.resume()
}
}
func setWeather(text: String) {
button.title = text
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let lastLocation = locations.last!
self.location = lastLocation
if prev_location == nil {
updateWeather()
}
prev_location = lastLocation
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error);
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// print("inside didChangeAuthorization ");
updateWeather()
}
}
+4 -1
View File
@@ -66,11 +66,14 @@
{ "type": "previous", "width": 44 },
{ "type": "play", "width": 44 },
{ "type": "next", "width": 44 },
{ "type": "weather", "refreshInterval": 1800 },
{ "type": "weather", "icon_type": "images", "units": "metric" },
{ "type": "currency", "from": "BTC", "to": "USD" },
{ "type": "sleep", "width": 44 },
{ "type": "mute", "width": 40, "align": "right" },
{ "type": "volumeDown", "width": 34, "align": "right" },
{ "type": "volume", "width": 60, "align": "right" },
{ "type": "volumeUp", "width": 34, "align": "right" },
{ "type": "inputsource", "align": "right" },
{ "type": "battery", "align": "right" },
{ "type": "timeButton", "align": "right" }
]
+43 -10
View File
@@ -5,9 +5,10 @@
# My TouchBar. My rules
[![GitHub release](https://img.shields.io/github/release/toxblh/MTMR.svg)](https://github.com/Toxblh/MTMR/releases)
[![license](https://img.shields.io/github/license/Toxblh/MTMR.svg)](https://github.com/Toxblh/MTMR/blob/master/LICENSE) [![Total downloads](https://img.shields.io/github/downloads/Toxblh/MTMR/total.svg)](https://github.com/Toxblh/MTMR/releases/latest) ![minimal system requirements](https://img.shields.io/badge/required-macOS%2010.12.2-blue.svg)
<p align="center">
<img src="Resources/TouchBar-v0.6.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 (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 :)
@@ -34,7 +35,7 @@ My the idea is to create the program like a platform for plugins for customizati
- [ ] System for autoupdate (maybe https://sparkle-project.org/)
Settings:
- [ ] Intarface for plugins and export like presets
- [ ] Interface for plugins and export like presets
- [ ] Startup at login
- [ ] Show on/off in Dock
- [ ] Show on/off in StatusBar
@@ -45,10 +46,8 @@ Maybe:
## 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.
- Download last [release](https://github.com/Toxblh/MTMR/releases)
- Or via Homebrew `brew cask install mtmr`
## Preset
@@ -62,6 +61,14 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
- brightnessDown
- volumeDown
- volumeUp
- mute
- dock
> Native Plugins
- battery
- currency
- weather
- inputsource
> Media Keys
- previous
@@ -69,8 +76,6 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
- next
> AppleScript plugins
- weather
- battery
- sleep
- displaySleep
@@ -105,8 +110,30 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
"formatTemplate": "HH:mm" //optional
```
## Native plugins
- `weather`
> Provider: https://openweathermap.org Need allowance location service
```js
"type": "weather",
"refreshInterval": 600,
"units": "metric", // or imperial
"icon_type": "text" // or images
"api_key": "" // you can get the key on openweather
```
- `currency`
> Provider: https://coinbase.com
```js
"type": "currency",
"refreshInterval": 600,
"align": "right",
"from": "BTC",
"to": "USD",
```
## Actions:
- `hidKey`
> https://github.com/aosm/IOHIDFamily/blob/master/IOHIDSystem/IOKit/hidsystem/ev_keymap.h use only numbers
```json
"action": "hidKey",
"keycode": 53,
@@ -131,11 +158,17 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
```js
"action": "shellScript",
"executablePath": "/usr/bin/pmset",
"shellArguments": "sleepnow", // optional
"shellArguments": ["sleepnow"], // optional
```
## Additional paramaters:
- `openUrl`
```js
"action": "openUrl",
"url": "https://google.com",
```
## Additional parameters:
- `width` allow to restrict how much room a particular button will take
```json
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+2 -1
View File
@@ -18,6 +18,7 @@ rm -r App.xcarchive
# Prerequisite: npm i -g create-dmg
create-dmg "${NAME}.app"
VERSION=`mdls -raw -name kMDItemVersion MTMR.app`
VERSION=`mdls -raw -name kMDItemVersion ${NAME}.app`
echo $VERSION
zip -r "${NAME}v${VERSION}.zip" "${NAME}.app"