Compare commits

...
241 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
Anton Palgunov 71ea94590b Merge pull request #20 from ad/apps
Launched apps (like dock)
2018-04-16 23:15:47 +01:00
ad 20699e9de7 * removed unused code 2018-04-16 23:42:44 +03:00
ad 893e9f6982 * removed local changes 2018-04-16 23:07:15 +03:00
ad 7a3c4811ac * finally 2018-04-16 22:53:34 +03:00
ad 0ce5a49172 * removed buggy "recent apps" 2018-04-16 22:43:06 +03:00
ad 9db9df5641 * code cleanup 2018-04-16 22:37:53 +03:00
ad 69b3f9d238 * removed animation 2018-04-16 15:56:04 +03:00
ad 3d7bcd2d47 * 2018-04-16 15:36:15 +03:00
ad a37bc94588 Merge remote-tracking branch 'upstream/master' 2018-04-16 11:27:16 +03:00
toxblh 91ceea564e improve build.sh 2018-04-16 01:17:19 +01:00
toxblh ee49e94c73 up version 2018-04-16 00:58:14 +01:00
toxblh 2f796cc07a Update default preset, now it's more useful and like a demo version. 2018-04-16 00:55:41 +01:00
toxblh b2b495b551 A bit better for icon only button, will render without title "Button" 2018-04-16 00:54:21 +01:00
toxblh 818c32b0ed Reload preset from menu 2018-04-16 00:53:40 +01:00
toxblh d15764d41d A bit better Slider UI 2018-04-16 00:53:03 +01:00
toxblh 9d343a31d3 rotate function for images 2018-04-15 23:17:53 +01:00
toxblh 0956b4ebcd move CustomSlider to file 2018-04-15 21:52:36 +01:00
toxblh 9ff68a6948 fix error with uncatching nil, when created slider without image 2018-04-15 21:11:12 +01:00
ad ac8a05d2e5 + show running apps (like dock) 2018-04-15 22:55:20 +03:00
Anton Palgunov 75ea408370 Merge pull request #19 from ad/master
* prevent crash on return from sleep (removed lockFocus/unlockFocus)
2018-04-15 11:58:50 +01:00
ad 8760185ae7 * prevent crash on return from sleep (removed lockFocus/unlockFocus) 2018-04-15 12:38:07 +03:00
Serg bd0ffbefe7 spaces in filepath should be escaped 2018-04-14 18:43:08 +07:00
Anton Palgunov 783ed34c0f Merge pull request #16 from ad/master
* image can be set for slider
2018-04-14 11:12:50 +01:00
ad 37c2e8523c * image can be set for slider 2018-04-14 13:04:46 +03:00
toxblh 3f2dad52ee upd Readme 2018-04-14 08:18:28 +01:00
Serg d6b0c0313b fix a typo 2018-04-14 13:04:56 +07:00
toxblh 78509fb84d Less spacing in scroll 2018-04-14 02:17:01 +01:00
toxblh 877c4268fd removed all warnings 2018-04-14 00:02:19 +01:00
toxblh 0e07f71458 Scripts for create dmg 2018-04-13 22:14:38 +01:00
toxblh 7100a6e6b8 New version. Layouts 2018-04-13 21:20:45 +01:00
Anton Palgunov 347bad8058 Merge pull request #13 from Toxblh/align
Align (left/center/right)
2018-04-13 21:18:29 +01:00
toxblh d72fb0ef7a Update my preset to actual. Fix xcode error 2018-04-13 21:15:02 +01:00
Serg ccc3a2269b Merge branch 'master' into align 2018-04-14 03:00:58 +07:00
Serg 6d6048c49c build quickfix 2018-04-14 03:00:38 +07:00
Serg 055c5d77ef Merge remote-tracking branch 'ad/master' 2018-04-14 02:54:03 +07:00
Serg f64c705c0d update example preset, update mine preset 2018-04-14 02:47:05 +07:00
ad ca2ae30873 + configurable refresh interval for brightness 2018-04-13 22:42:53 +03:00
Serg 00fae08da0 mention change in docs 2018-04-14 02:33:34 +07:00
ad f09b9155c4 + listener for changes of brightness (timer 0.5 sec)
Can't find any listener
2018-04-13 22:17:12 +03:00
Serg 9ef185d214 remove flexSpace 2018-04-14 02:09:34 +07:00
Serg 12f9220e34 implement scrolling view at the center 2018-04-14 01:58:07 +07:00
Serg ba753ff331 Merge branch 'master' into align
# Conflicts:
#	MTMR/ItemsParsing.swift
2018-04-14 01:46:44 +07:00
Serg aba1aea334 Merge pull request #12 from ad/master
+ listener for changes of sytem volume
2018-04-14 01:42:39 +07:00
ad 849b5b6db0 * fixed bug with mute at 0.0 2018-04-13 21:40:34 +03:00
ad 621cb3242b + listener for changes of sytem volume
known bug: sound not mutes at value 0.0
2018-04-13 21:27:59 +03:00
Serg dd0ecf047d additional parameters as dictionary for easier mapping 2018-04-14 00:53:22 +07:00
Serg bbc3d9dcfe filter center items, prepare for scrollView 2018-04-14 00:32:12 +07:00
Serg 85785018f4 create all items in advance 2018-04-14 00:23:47 +07:00
Serg 7b9f894bf3 parse align 2018-04-14 00:23:19 +07:00
toxblh 3a57293206 add brightness icons like original Touch Bar 2018-04-13 17:21:51 +01:00
Serg 9afa9d7470 Merge branch 'tries/button-image'
# Conflicts:
#	MTMR.xcodeproj/project.pbxproj
2018-04-13 22:31:07 +07:00
Anton Palgunov a86f7eb304 Update README.md 2018-04-13 14:54:44 +01:00
toxblh 5013262740 v0.6.1 2018-04-13 14:46:38 +01:00
Anton Palgunov 106f297f57 Merge pull request #11 from ad/master
+ volume and brightness sliders
2018-04-13 14:44:39 +01:00
ad 115ea1372c + volume and brightness sliders 2018-04-13 16:25:25 +03:00
toxblh f45aad4fad add build.sh 2018-04-13 11:50:27 +01:00
toxblh e131f92fb5 del trim() in parser 2018-04-13 09:25:55 +01:00
toxblh 69a8d81bce del trim() for aScript 2018-04-13 09:22:03 +01:00
Serg 7789407766 no staticImageButton in readme 2018-04-13 14:00:21 +07:00
toxblh 6c331619d0 Added 2 Support function (trim and resize). Image and title close forever :) resize input image to good size 2018-04-13 01:15:11 +01:00
Anton Palgunov 9393a4e484 Merge pull request #9 from Toxblh/openPreferences
Open preset for editing then you click by Preferences in menu.
2018-04-12 20:07:38 +01:00
toxblh 4b480dddc3 del the quote 2018-04-12 18:57:02 +01:00
toxblh a6f27a1fef open preset for editing then you click by Preferences in menu. 2018-04-12 18:55:54 +01:00
Serg 79eba1a281 image as generic parameter 2018-04-13 00:00:33 +07:00
Serg 2ad89bd8c8 fix a typo 2018-04-12 17:16:48 +07:00
Anton Palgunov ec990e8147 Merge pull request #7 from Toxblh/example-scripts
Example presets and credits
2018-04-12 10:51:10 +01:00
toxblh 16d8380830 link to releases 2018-04-12 10:49:37 +01:00
toxblh 9e43a3b561 changes for Installation part 2018-04-12 10:48:07 +01:00
toxblh 080bc67c54 upd from release 2018-04-12 10:46:15 +01:00
toxblh 81131ec390 new screenshot. 2 json author's presets. Instruction. 2018-04-12 10:41:50 +01:00
Serg cd6054af03 example presets and credits 2018-04-12 14:30:08 +07:00
toxblh eee33ee645 v0.6 2018-04-12 07:57:18 +01:00
Anton Palgunov 046f0fc1cf Merge pull request #6 from Toxblh/cleanup
Cleanup project
2018-04-12 07:34:59 +01:00
Serg 4b7788fff7 some cleanup 2018-04-12 08:58:24 +07:00
Serg ea5fbf0ee8 action has unified source too 2018-04-12 08:48:24 +07:00
Serg d0cfa45b5d hacky solution to autohide if title is empty 2018-04-11 23:46:05 +07:00
toxblh ed14f34a2d conflict resolved 2018-04-11 17:09:34 +01:00
toxblh 98d5a6eb0e add staticImageButton for buttons with custom image 2018-04-11 17:03:38 +01:00
Serg b492b89d2f unified source for scripts (and image data in future) 2018-04-11 22:37:46 +07:00
Serg ff58329691 displaysleep item 2018-04-11 13:47:09 +07:00
Serg dd3ab9ac6f no codesign for debug, no UI tests 2018-04-11 07:29:28 +07:00
toxblh f08ce7a381 add FlexSpace. A bit refactor structure. Correct loading scripts from preset 2018-04-11 01:07:58 +01:00
Serg 11c8d167c0 custom width 2018-04-11 00:56:14 +07:00
Serg 0ba5984217 shell scripts and sleep implemented 2018-04-10 23:37:20 +07:00
Serg 1584cf979c cleanup extra code 2018-04-10 23:21:38 +07:00
toxblh 753cfd8a31 add buttons: battery and time with format 2018-04-10 14:07:52 +01:00
toxblh 34b51b2dd8 a bit fix json lint 2018-04-10 13:23:12 +01:00
Serg 5bda77d0d5 Merge remote-tracking branch 'upstream/master' into preset
# Conflicts:
#	README.md
2018-04-10 19:02:59 +07:00
Serg 2bc8912add weather – example on bundled scripts 2018-04-10 19:01:41 +07:00
Serg b70c8ca4a3 start to log my dirty code 2018-04-10 18:44:47 +07:00
Serg 2750d00317 media keys implemented 2018-04-10 18:41:25 +07:00
Serg 4fb7cb9b99 cleaner custom actions, potentially able to register from the outside 2018-04-10 18:16:28 +07:00
Serg 651ddbef5c escape button implemented 2018-04-10 17:57:51 +07:00
Serg fe5705627f missing changes :( 2018-04-10 17:51:21 +07:00
Serg c0401ce83d exit touchbar implemented 2018-04-10 17:50:34 +07:00
Serg 1c45ca13a3 generic actions sketched 2018-04-10 17:39:07 +07:00
Serg 970fb99e33 interval for apple script 2018-04-10 16:58:52 +07:00
Serg 46f35f4737 preset working 2018-04-10 16:50:41 +07:00
toxblh 713f4dffc7 added StatusMenu. Del from Dock. 2018-04-10 09:16:05 +01:00
Serg d0efdef8c2 initial json parsing 2018-04-10 14:13:20 +07:00
toxblh 65e3d67c32 a bit upd readme 2018-04-10 00:00:34 +01:00
toxblh e56a05657e add first badge to readme 😅 2018-04-09 23:25:30 +01:00
toxblh 4a84532c56 upd roadmap 2018-04-09 23:00:39 +01:00
63 changed files with 3143 additions and 596 deletions
+3
View File
@@ -1,3 +1,5 @@
.DS_Store
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
@@ -5,6 +7,7 @@
## Build generated
build/
DerivedData/
Release/
## Various settings
*.pbxuser
+143 -159
View File
@@ -7,24 +7,43 @@
objects = {
/* Begin PBXBuildFile section */
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368EDDE620812A1D00E10953 /* ScrollViewItem.swift */; };
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */; };
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */; };
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */; };
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */ = {isa = PBXBuildFile; fileRef = 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */; };
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */; };
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */; };
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */; };
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 /* TouchBarItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* TouchBarItems.swift */; };
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; };
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; };
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B252205C7D8000BC04DC /* AppDelegate.swift */; };
B082B255205C7D8000BC04DC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B254205C7D8000BC04DC /* ViewController.swift */; };
B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B082B256205C7D8000BC04DC /* Assets.xcassets */; };
B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B082B258205C7D8000BC04DC /* Main.storyboard */; };
B082B266205C7D8000BC04DC /* MTMRTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B265205C7D8000BC04DC /* MTMRTests.swift */; };
B082B271205C7D8000BC04DC /* MTMRUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B270205C7D8000BC04DC /* MTMRUITests.swift */; };
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */; };
B09EB1E6207C0F8E00D5C1E0 /* MultitouchSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */; };
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */; };
B0A8BF9E207B84160086F74D /* weather.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0A8BF9D207B84160086F74D /* weather.scpt */; };
B0C1CFCA205C97D30021C862 /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C1CFC9205C97D30021C862 /* WindowController.swift */; };
B0B17431207D6B590004B740 /* PlaySmart.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17427207D6B580004B740 /* PlaySmart.scpt */; };
B0B17432207D6B590004B740 /* Weather.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17428207D6B580004B740 /* Weather.scpt */; };
B0B17433207D6B590004B740 /* Finder.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17429207D6B580004B740 /* Finder.scpt */; };
B0B17434207D6B590004B740 /* Battery.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742A207D6B580004B740 /* Battery.scpt */; };
B0B17435207D6B590004B740 /* Spotify.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742B207D6B590004B740 /* Spotify.next.scpt */; };
B0B17436207D6B590004B740 /* iTunes.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742C207D6B590004B740 /* iTunes.next.scpt */; };
B0B17437207D6B590004B740 /* Spotify.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742D207D6B590004B740 /* Spotify.nowPlaying.scpt */; };
B0B17438207D6B590004B740 /* Vox.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742E207D6B590004B740 /* Vox.next.scpt */; };
B0B17439207D6B590004B740 /* Vox.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */; };
B0B1743A207D6B590004B740 /* iTunes.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */; };
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */; };
B0F8771D207AD35400D6E430 /* battery.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0F8771C207AD35400D6E430 /* battery.scpt */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -35,45 +54,56 @@
remoteGlobalIDString = B082B24E205C7D8000BC04DC;
remoteInfo = MTMR;
};
B082B26D205C7D8000BC04DC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B082B247205C7D8000BC04DC /* Project object */;
proxyType = 1;
remoteGlobalIDString = B082B24E205C7D8000BC04DC;
remoteInfo = MTMR;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewItem.swift; sourceTree = "<group>"; };
36BDC22F207CDA8600FCFEBE /* TECHNICAL_DEBT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TECHNICAL_DEBT.md; sourceTree = "<group>"; };
36C2ECD2207B3B1D003CDA33 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeTouchBarItem.swift; sourceTree = "<group>"; };
36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptTouchBarItem.swift; sourceTree = "<group>"; };
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsParsing.swift; sourceTree = "<group>"; };
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigTests.swift; sourceTree = "<group>"; };
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = defaultPreset.json; sourceTree = "<group>"; };
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrightnessViewController.swift; sourceTree = "<group>"; };
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VolumeViewController.swift; sourceTree = "<group>"; };
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScrubberTouchBarItem.swift; sourceTree = "<group>"; };
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeprecatedCarbonAPI.h; sourceTree = "<group>"; };
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = DeprecatedCarbonAPI.c; sourceTree = "<group>"; };
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 /* TouchBarItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarItems.swift; sourceTree = "<group>"; };
B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButtonTouchBarItem.swift; sourceTree = "<group>"; };
B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarPrivateApi.h; sourceTree = "<group>"; };
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TouchBarPrivateApi-Bridging.h"; sourceTree = "<group>"; };
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DFRFoundation.framework; path = ../../../../../System/Library/PrivateFrameworks/DFRFoundation.framework; sourceTree = "<group>"; };
B082B24F205C7D8000BC04DC /* MTMR.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MTMR.app; sourceTree = BUILT_PRODUCTS_DIR; };
B082B252205C7D8000BC04DC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
B082B254205C7D8000BC04DC /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
B082B256205C7D8000BC04DC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B082B259205C7D8000BC04DC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
B082B25B205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B082B25C205C7D8000BC04DC /* MTMR.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MTMR.entitlements; sourceTree = "<group>"; };
B082B261205C7D8000BC04DC /* MTMRTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MTMRTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B082B265205C7D8000BC04DC /* MTMRTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTMRTests.swift; sourceTree = "<group>"; };
B082B267205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B082B26C205C7D8000BC04DC /* MTMRUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MTMRUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B082B270205C7D8000BC04DC /* MTMRUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTMRUITests.swift; sourceTree = "<group>"; };
B082B272205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticFeedback.swift; sourceTree = "<group>"; };
B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = ../../../../../System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = "<group>"; };
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPress.swift; sourceTree = "<group>"; };
B0A8BF9D207B84160086F74D /* weather.scpt */ = {isa = PBXFileReference; lastKnownFileType = text; path = weather.scpt; sourceTree = "<group>"; };
B0C1CFC9205C97D30021C862 /* WindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowController.swift; sourceTree = "<group>"; };
B0B17427207D6B580004B740 /* PlaySmart.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PlaySmart.scpt; sourceTree = "<group>"; };
B0B17428207D6B580004B740 /* Weather.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Weather.scpt; sourceTree = "<group>"; };
B0B17429207D6B580004B740 /* Finder.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Finder.scpt; sourceTree = "<group>"; };
B0B1742A207D6B580004B740 /* Battery.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Battery.scpt; sourceTree = "<group>"; };
B0B1742B207D6B590004B740 /* Spotify.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Spotify.next.scpt; sourceTree = "<group>"; };
B0B1742C207D6B590004B740 /* iTunes.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iTunes.next.scpt; sourceTree = "<group>"; };
B0B1742D207D6B590004B740 /* Spotify.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Spotify.nowPlaying.scpt; sourceTree = "<group>"; };
B0B1742E207D6B590004B740 /* Vox.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Vox.next.scpt; sourceTree = "<group>"; };
B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Vox.nowPlaying.scpt; sourceTree = "<group>"; };
B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iTunes.nowPlaying.scpt; sourceTree = "<group>"; };
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TouchBarSupport.m; sourceTree = "<group>"; };
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarSupport.h; sourceTree = "<group>"; };
B0F8771C207AD35400D6E430 /* battery.scpt */ = {isa = PBXFileReference; lastKnownFileType = text; path = battery.scpt; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -93,13 +123,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B082B269205C7D8000BC04DC /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -116,9 +139,9 @@
isa = PBXGroup;
children = (
36C2ECD2207B3B1D003CDA33 /* README.md */,
36BDC22F207CDA8600FCFEBE /* TECHNICAL_DEBT.md */,
B082B251205C7D8000BC04DC /* MTMR */,
B082B264205C7D8000BC04DC /* MTMRTests */,
B082B26F205C7D8000BC04DC /* MTMRUITests */,
B082B250205C7D8000BC04DC /* Products */,
B059D62B205F11E8006E6B86 /* Frameworks */,
);
@@ -129,7 +152,6 @@
children = (
B082B24F205C7D8000BC04DC /* MTMR.app */,
B082B261205C7D8000BC04DC /* MTMRTests.xctest */,
B082B26C205C7D8000BC04DC /* MTMRUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -137,25 +159,24 @@
B082B251205C7D8000BC04DC /* MTMR */ = {
isa = PBXGroup;
children = (
B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */,
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */,
B082B252205C7D8000BC04DC /* AppDelegate.swift */,
B082B254205C7D8000BC04DC /* ViewController.swift */,
B0B88A07208CD12000A2C160 /* Widgets */,
B0B1743B207D6ED40004B740 /* CBridge */,
B0B17426207D6AFE0004B740 /* AppleScripts */,
B082B256205C7D8000BC04DC /* Assets.xcassets */,
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */,
B059D623205E04F3006E6B86 /* TouchBarItems.swift */,
36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */,
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
B059D621205E03F5006E6B86 /* TouchBarController.swift */,
B0C1CFC9205C97D30021C862 /* WindowController.swift */,
B082B258205C7D8000BC04DC /* Main.storyboard */,
B082B25B205C7D8000BC04DC /* Info.plist */,
B082B25C205C7D8000BC04DC /* MTMR.entitlements */,
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */,
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */,
B0F8771C207AD35400D6E430 /* battery.scpt */,
B0A8BF9D207B84160086F74D /* weather.scpt */,
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */,
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 */,
B05600D22083E9BB00EB218D /* CustomSlider.swift */,
);
path = MTMR;
sourceTree = "<group>";
@@ -163,19 +184,55 @@
B082B264205C7D8000BC04DC /* MTMRTests */ = {
isa = PBXGroup;
children = (
B082B265205C7D8000BC04DC /* MTMRTests.swift */,
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */,
B082B267205C7D8000BC04DC /* Info.plist */,
);
path = MTMRTests;
sourceTree = "<group>";
};
B082B26F205C7D8000BC04DC /* MTMRUITests */ = {
B0B17426207D6AFE0004B740 /* AppleScripts */ = {
isa = PBXGroup;
children = (
B082B270205C7D8000BC04DC /* MTMRUITests.swift */,
B082B272205C7D8000BC04DC /* Info.plist */,
B0B1742A207D6B580004B740 /* Battery.scpt */,
B0B17429207D6B580004B740 /* Finder.scpt */,
B0B1742C207D6B590004B740 /* iTunes.next.scpt */,
B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */,
B0B17427207D6B580004B740 /* PlaySmart.scpt */,
B0B1742B207D6B590004B740 /* Spotify.next.scpt */,
B0B1742D207D6B590004B740 /* Spotify.nowPlaying.scpt */,
B0B1742E207D6B590004B740 /* Vox.next.scpt */,
B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */,
B0B17428207D6B580004B740 /* Weather.scpt */,
);
path = MTMRUITests;
path = AppleScripts;
sourceTree = "<group>";
};
B0B1743B207D6ED40004B740 /* CBridge */ = {
isa = PBXGroup;
children = (
B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */,
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */,
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */,
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */,
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */,
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */,
);
path = CBridge;
sourceTree = "<group>";
};
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 */
@@ -216,24 +273,6 @@
productReference = B082B261205C7D8000BC04DC /* MTMRTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
B082B26B205C7D8000BC04DC /* MTMRUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = B082B27B205C7D8000BC04DC /* Build configuration list for PBXNativeTarget "MTMRUITests" */;
buildPhases = (
B082B268205C7D8000BC04DC /* Sources */,
B082B269205C7D8000BC04DC /* Frameworks */,
B082B26A205C7D8000BC04DC /* Resources */,
);
buildRules = (
);
dependencies = (
B082B26E205C7D8000BC04DC /* PBXTargetDependency */,
);
name = MTMRUITests;
productName = MTMRUITests;
productReference = B082B26C205C7D8000BC04DC /* MTMRUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -246,7 +285,7 @@
TargetAttributes = {
B082B24E205C7D8000BC04DC = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
ProvisioningStyle = Manual;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 0;
@@ -255,13 +294,6 @@
};
B082B260205C7D8000BC04DC = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
TestTargetID = B082B24E205C7D8000BC04DC;
};
B082B26B205C7D8000BC04DC = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
TestTargetID = B082B24E205C7D8000BC04DC;
};
};
};
@@ -280,7 +312,6 @@
targets = (
B082B24E205C7D8000BC04DC /* MTMR */,
B082B260205C7D8000BC04DC /* MTMRTests */,
B082B26B205C7D8000BC04DC /* MTMRUITests */,
);
};
/* End PBXProject section */
@@ -290,10 +321,19 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B0A8BF9E207B84160086F74D /* weather.scpt in Resources */,
B0B17434207D6B590004B740 /* Battery.scpt in Resources */,
B0B1743A207D6B590004B740 /* iTunes.nowPlaying.scpt in Resources */,
B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */,
B0B17437207D6B590004B740 /* Spotify.nowPlaying.scpt in Resources */,
B0B17436207D6B590004B740 /* iTunes.next.scpt in Resources */,
B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */,
B0F8771D207AD35400D6E430 /* battery.scpt in Resources */,
B0B17435207D6B590004B740 /* Spotify.next.scpt in Resources */,
B0B17433207D6B590004B740 /* Finder.scpt in Resources */,
B0B17439207D6B590004B740 /* Vox.nowPlaying.scpt in Resources */,
B0B17438207D6B590004B740 /* Vox.next.scpt in Resources */,
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */,
B0B17432207D6B590004B740 /* Weather.scpt in Resources */,
B0B17431207D6B590004B740 /* PlaySmart.scpt in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -304,13 +344,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B082B26A205C7D8000BC04DC /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -319,15 +352,25 @@
buildActionMask = 2147483647;
files = (
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */,
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */,
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */,
B082B255205C7D8000BC04DC /* ViewController.swift in Sources */,
B0C1CFCA205C97D30021C862 /* WindowController.swift in Sources */,
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */,
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */,
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */,
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
B059D624205E04F3006E6B86 /* TouchBarItems.swift in Sources */,
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
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;
};
@@ -335,15 +378,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B082B266205C7D8000BC04DC /* MTMRTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B082B268205C7D8000BC04DC /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B082B271205C7D8000BC04DC /* MTMRUITests.swift in Sources */,
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */,
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -355,11 +391,6 @@
target = B082B24E205C7D8000BC04DC /* MTMR */;
targetProxy = B082B262205C7D8000BC04DC /* PBXContainerItemProxy */;
};
B082B26E205C7D8000BC04DC /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B082B24E205C7D8000BC04DC /* MTMR */;
targetProxy = B082B26D205C7D8000BC04DC /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -405,7 +436,6 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -488,9 +518,8 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
@@ -499,7 +528,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMR;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/TouchBarPrivateApi-Bridging.h";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/CBridge/TouchBarPrivateApi-Bridging.h";
SWIFT_VERSION = 4.0;
};
name = Debug;
@@ -508,7 +538,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
FRAMEWORK_SEARCH_PATHS = (
@@ -519,7 +549,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMR;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/TouchBarPrivateApi-Bridging.h";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/CBridge/TouchBarPrivateApi-Bridging.h";
SWIFT_VERSION = 4.0;
};
name = Release;
@@ -528,16 +559,12 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
INFOPLIST_FILE = MTMRTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MTMR.app/Contents/MacOS/MTMR";
};
name = Debug;
};
@@ -545,48 +572,14 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = MTMRTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MTMR.app/Contents/MacOS/MTMR";
};
name = Release;
};
B082B27C205C7D8000BC04DC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
INFOPLIST_FILE = MTMRUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TEST_TARGET_NAME = MTMR;
};
name = Debug;
};
B082B27D205C7D8000BC04DC /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
INFOPLIST_FILE = MTMRUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TEST_TARGET_NAME = MTMR;
};
name = Release;
};
@@ -620,15 +613,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B082B27B205C7D8000BC04DC /* Build configuration list for PBXNativeTarget "MTMRUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B082B27C205C7D8000BC04DC /* Debug */,
B082B27D205C7D8000BC04DC /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = B082B247205C7D8000BC04DC /* Project object */;
@@ -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>
+79 -4
View File
@@ -10,16 +10,91 @@ import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
private var fileSystemSource: DispatchSourceFileSystemObject?
func applicationDidFinishLaunching(_ aNotification: Notification) {
TouchBarController.shared.setupControlStripPresence()
// Insert code here to initialize your application
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?) {
let task = Process()
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
let presetPath = appSupportDirectory.appending("/items.json")
task.launchPath = "/usr/bin/open"
task.arguments = [presetPath]
task.launch()
}
@objc func updatePreset(_ sender: Any?) {
TouchBarController.shared.createAndUpdatePreset()
}
@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()
}
}
+27 -14
View File
@@ -1,26 +1,28 @@
import Foundation
class AppleScriptTouchBarItem: NSCustomTouchBarItem {
private let script: NSAppleScript!
private let button = NSButton(title: "", target: nil, action: nil)
class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
private var script: NSAppleScript!
private let interval: TimeInterval
private var forceHideConstraint: NSLayoutConstraint!
init?(identifier: NSTouchBarItem.Identifier, appleScript: String, interval: TimeInterval) {
guard let script = NSAppleScript(source: appleScript) else {
return nil
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, onTap: @escaping ()->(), onLongTap: @escaping ()->()) {
self.interval = interval
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"
return
}
self.script = script
self.interval = interval
super.init(identifier: identifier)
self.view = button
button.bezelColor = .clear
button.title = "compile"
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,10 +35,13 @@ class AppleScriptTouchBarItem: NSCustomTouchBarItem {
}
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
self.forceHideConstraint.isActive = scriptResult == ""
}
DispatchQueue.main.asyncAfter(deadline: .now() + self.interval) { [weak self] in
self?.refreshAndSchedule()
@@ -50,6 +55,14 @@ class AppleScriptTouchBarItem: NSCustomTouchBarItem {
print(error)
return "error"
}
return output.stringValue ?? "empty value"
return output.stringValue ?? ""
}
}
extension SourceProtocol {
var appleScript: NSAppleScript? {
guard let source = self.string else { return nil }
return NSAppleScript(source: source)
}
}
@@ -3,27 +3,27 @@ set percent to word 6 of theList's item 2
set charge to word 7 of theList's item 2
if (charge = "charging") then
set iconC to "⚡️"
set iconC to "⚡️"
else
set iconC to ""
set iconC to ""
end if
set remainingRaw to my split(theList's item 2, " ")
set remainingTime to remainingRaw's item 5
if (remainingTime = "(no") then
set strTime to " (?)"
set strTime to " (?)"
else if (remainingTime = "0:00") then
set strTime to ""
set strTime to ""
else
set strTime to " (" & remainingTime & ")"
set strTime to " (" & remainingTime & ")"
end if
return iconC & percent & "%" & strTime
to split(someText, delimiter)
set AppleScript's text item delimiters to delimiter
set someText to someText's text items
set AppleScript's text item delimiters to {""}
return someText
set AppleScript's text item delimiters to delimiter
set someText to someText's text items
set AppleScript's text item delimiters to {""}
return someText
end split
+5
View File
@@ -0,0 +1,5 @@
tell application "Finder"
make new Finder window
set target of front window to path to home folder as string
activate
end tell
+11
View File
@@ -0,0 +1,11 @@
if application "iTunes" is running then
tell application "iTunes" to playpause
end if
if application "Spotify" is running then
tell application "Spotify" to playpause
end if
if application "VOX" is running then
tell application "VOX" to playpause
end if
+7
View File
@@ -0,0 +1,7 @@
if application "Spotify" is running then
tell application "Spotify"
if player state is playing then
next track
end if
end tell
end if
+10
View File
@@ -0,0 +1,10 @@
if application "Spotify" is running then
tell application "Spotify"
if player state is playing then
return (get artist of current track) & " – " & (get name of current track)
else
return ""
end if
end tell
end if
return ""
+7
View File
@@ -0,0 +1,7 @@
if application "VOX" is running then
tell application "VOX"
if player state is 1 then
next
end if
end tell
end if
+10
View File
@@ -0,0 +1,10 @@
if application "VOX" is running then
tell application "VOX"
if player state is 1 then
return (get artist) & " – " & (get track)
else
return ""
end if
end tell
end if
return ""
+32
View File
@@ -0,0 +1,32 @@
# This script requires two libs. Download them:
# https://itunes.apple.com/ru/app/json-helper-for-applescript/id453114608?l=en&mt=12
# https://itunes.apple.com/ru/app/location-helper-for-applescript/id488536386?mt=12
tell application "Location Helper"
set clocation_coords to get location coordinates
tell application "JSON Helper"
set weather to fetch JSON from "http://api.openweathermap.org/data/2.5/weather?lat=" & item 1 of clocation_coords & "&lon=" & item 2 of clocation_coords & "&units=metric&appid=32c4256d09a4c52b38aecddba7a078f6"
set temp to temp of main of weather as string
set cond_icon to icon of item 1 of weather of weather as string
if cond_icon is in ["01d", "01n"] then
set cond to "☀️"
else if cond_icon is in ["02d", "02n"] then
set cond to "⛅️"
else if cond_icon is in ["03d", "03n", "04d", "04n"] then
set cond to "☁️"
else if cond_icon is in ["09d", "09n"] then
set cond to "🌧"
else if cond_icon is in ["10d", "10n"] then
set cond to "🌦"
else if cond_icon is in ["11d", "11n"] then
set cond to "🌩"
else if cond_icon is in ["13d", "13n"] then
set cond to "❄️"
else if cond_icon is in ["50d", "50n"] then
set cond to "🌫"
else
set cond to ""
end if
set temp_round to round (temp * 1.0)
return cond & " " & temp_round & "°C"
end tell
end tell
+7
View File
@@ -0,0 +1,7 @@
if application "iTunes" is running then
tell application "iTunes"
if player state is playing then
next track
end if
end tell
end if
+10
View File
@@ -0,0 +1,10 @@
if application "iTunes" is running then
tell application "iTunes"
if player state is playing then
return (get artist of current track) & " – " & (get name of current track)
else
return ""
end if
end tell
end if
return ""
+24
View File
@@ -0,0 +1,24 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "StatusImage.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,24 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "brightnessDown.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

@@ -0,0 +1,24 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "brightnessUp.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

+2 -47
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Application-->
@@ -680,50 +680,5 @@
</objects>
<point key="canvasLocation" x="75" y="0.0"/>
</scene>
<!--Window Controller-->
<scene sceneID="R2V-B0-nI4">
<objects>
<windowController id="B8D-0N-5wS" customClass="WindowController" customModule="MTMR" customModuleProvider="target" sceneMemberID="viewController">
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1028"/>
<connections>
<outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
</connections>
</window>
<connections>
<segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
</connections>
</windowController>
<customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="250"/>
</scene>
<!--View Controller-->
<scene sceneID="hIz-AP-VOD">
<objects>
<viewController id="XfG-lQ-9wD" customClass="ViewController" customModule="MTMR" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" wantsLayer="YES" id="m2S-Jp-Qdl">
<rect key="frame" x="0.0" y="0.0" width="178" height="57"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bso-ZY-Qqn">
<rect key="frame" x="18" y="20" width="142" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="My TouchBar. My rules" id="cmP-Ef-Jrj">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
</viewController>
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="74" y="613"/>
</scene>
</scenes>
</document>
+45
View File
@@ -0,0 +1,45 @@
//
// DeprecatedCarbonAPI.c
//
// This file is part of TouchDock
// Copyright (C) 2017 Xander Deng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "DeprecatedCarbonAPI.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
CFStringRef kPidKey = CFSTR("pid");
pid_t pidFromASN(void const *asn) {
pid_t pid = -1;
ProcessSerialNumber psn = {kNoProcess, kNoProcess};
if (CFGetTypeID(asn) == _LSASNGetTypeID()) {
_LSASNExtractHighAndLowParts(asn, &psn.highLongOfPSN, &psn.lowLongOfPSN);
CFDictionaryRef processInfo = ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask);
if (processInfo) {
CFNumberRef pidNumber = CFDictionaryGetValue(processInfo, kPidKey);
if (pidNumber) {
CFNumberGetValue(pidNumber, kCFNumberSInt32Type, &pid);
}
CFRelease(processInfo);
}
}
return pid;
}
#pragma GCC diagnostic pop
+27
View File
@@ -0,0 +1,27 @@
//
// DeprecatedCarbonAPI.h
//
// This file is part of TouchDock
// Copyright (C) 2017 Xander Deng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#import <Carbon/Carbon.h>
extern CFArrayRef _LSCopyApplicationArrayInFrontToBackOrder(uint32_t sessionID);
extern void _LSASNExtractHighAndLowParts(void const* asn, UInt32* psnHigh, UInt32* psnLow);
extern CFTypeID _LSASNGetTypeID(void);
pid_t pidFromASN(void const *asn);
@@ -8,6 +8,7 @@
#import "TouchBarPrivateApi.h"
#import "TouchBarSupport.h"
#import "DeprecatedCarbonAPI.h"
NS_ASSUME_NONNULL_BEGIN
+121
View File
@@ -0,0 +1,121 @@
//
// TouchBarItems.swift
// MTMR
//
// Created by Anton Palgunov on 18/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
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 () -> (), onLongTap callbackLong: @escaping () -> (), bezelColor: NSColor? = .clear) {
self.tapClosure = callback
self.longTapClosure = callbackLong
super.init(identifier: identifier)
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")
}
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()
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
}
}
+108
View File
@@ -0,0 +1,108 @@
//
// CustomSlider.swift
// MTMR
//
// Created by Anton Palgunov on 15/04/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Foundation
class CustomSliderCell: NSSliderCell {
var knobImage:NSImage!
private var _currentKnobRect:NSRect!
private var _barRect:NSRect!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init() {
super.init()
}
init(knob:NSImage?) {
knobImage = knob;
super.init()
}
override func drawKnob(_ knobRect: NSRect) {
if (knobImage == nil) {
super.drawKnob(knobRect)
return;
}
_currentKnobRect = knobRect;
drawBar(inside: _barRect, flipped: true)
let x = (knobRect.origin.x * (_barRect.size.width - (knobImage.size.width - knobRect.size.width)) / _barRect.size.width)+1;
let y = knobRect.origin.y+3
knobImage.draw(
at: NSPoint(x: x, y: y),
from: NSZeroRect,
operation: NSCompositingOperation.sourceOver,
fraction: 1
)
}
override func drawBar(inside aRect: NSRect, flipped: Bool) {
_barRect = aRect
let barRadius = CGFloat(2)
var bgRect = aRect
bgRect.size.height = CGFloat(4)
let bg = NSBezierPath(roundedRect: bgRect, xRadius: barRadius, yRadius: barRadius)
NSColor.lightGray.setFill()
bg.fill()
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()
}
}
class CustomSlider:NSSlider {
var currentValue:CGFloat = 0
override func setNeedsDisplay(_ invalidRect: NSRect) {
super.setNeedsDisplay(invalidRect)
}
override func awakeFromNib() {
super.awakeFromNib()
if ((self.cell?.isKind(of: CustomSliderCell.self)) == false) {
let cell:CustomSliderCell = CustomSliderCell()
self.cell = cell
}
}
convenience init(knob:NSImage) {
self.init()
self.cell = CustomSliderCell(knob: knob)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
func knobImage() -> NSImage {
let cell = self.cell as! CustomSliderCell
return cell.knobImage
}
func setKnobImage(image:NSImage) {
let cell = self.cell as! CustomSliderCell
cell.knobImage = image
}
}
+20 -20
View File
@@ -12,44 +12,44 @@ class HapticFeedback {
private var actuatorRef: CFTypeRef?
private var deviceID: UInt64 = 0x200000001000000
private var error: IOReturn = 0
// Don't know how to do strong is enum one of
// 1 like backClick
// 2 (like Click)
// 3 (week)
// 4 (medium)
// 5 (week medium)
// 6 (strong)
// 15 (nothing)
// 16 (nothing)
// 1 like back Click
// 2 like Click
// 3 week
// 4 medium
// 5 week medium
// 6 strong
// 15 nothing
// 16 nothing
// you can get a plist `otool -s __TEXT __tpad_act_plist /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/Current/MultitouchSupport|tail -n +3|awk -F'\t' '{print $2}'|xxd -r -p`
func tap(strong:Int32) -> Void {
let actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue()
// TODO: Need to fix warning
actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue()
guard actuatorRef != nil else {
print("guard actuatorRef == nil")
return
}
error = MTActuatorOpen(actuatorRef)
error = MTActuatorOpen(actuatorRef!)
guard error == kIOReturnSuccess else {
print("guard MTActuatorOpen")
return
}
error = MTActuatorActuate(actuatorRef, strong, 0, 0.0, 0.0)
error = MTActuatorActuate(actuatorRef!, strong, 0, 0.0, 0.0)
guard error == kIOReturnSuccess else {
print("guard MTActuatorActuate")
return
}
error = MTActuatorClose(actuatorRef)
error = MTActuatorClose(actuatorRef!)
guard error == kIOReturnSuccess else {
print("guard MTActuatorClose")
return
}
return
}
}
+9 -1
View File
@@ -17,13 +17,21 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>0.14</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2018 Anton Palgunov. All rights reserved.</string>
<key>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>
+497
View File
@@ -0,0 +1,497 @@
import Foundation
import AppKit
extension Data {
func barItemDefinitions() -> [BarItemDefinition]? {
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, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) {
self.type = type
self.action = action
self.longAction = longAction
self.additionalParameters = additionalParameters
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type)
var additionalParameters = try GeneralParameters(from: decoder).parameters
if let result = try? parametersDecoder(decoder),
case let (itemType, action, longAction, parameters) = result {
parameters.forEach { additionalParameters[$0] = $1 }
self.init(type: itemType, action: action, longAction: longAction, additionalParameters: additionalParameters)
} else {
self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, additionalParameters: additionalParameters)
}
}
}
class SupportedTypesHolder {
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), 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), longAction: .none, parameters: [.image: imageParameter])
},
"brightnessDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
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), 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), 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), 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), 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), longAction: .none, parameters: [.image: imageParameter])
},
"weather": { decoder in
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 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, 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, longAction: .none, parameters: [.image: .image(source: img)])
} else {
return (item: .volume(), action: .none, longAction: .none, parameters: [:])
}
},
"brightness": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval; case image }
let container = try decoder.container(keyedBy: CodingKeys.self)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
if var img = try container.decodeIfPresent(Source.self, forKey: .image) {
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [.image: .image(source: img)])
} else {
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [:])
}
},
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), longAction: .none, parameters: [:]) },
"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), longAction: try LongActionType(from: decoder), parameters: [:])
}
}
func register(typename: String, decoder: @escaping ParametersDecoder) {
supportedTypes[typename] = decoder
}
func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) {
register(typename: typename) { _ in
return (item: item, action: action, longAction: longAction, parameters: [:])
}
}
}
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 {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(ItemTypeRaw.self, forKey: .type)
switch type {
case .appleScriptTitledButton:
let source = try container.decode(Source.self, forKey: .source)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
self = .appleScriptTitledButton(source: source, refreshInterval: interval)
case .staticButton:
let title = try container.decode(String.self, forKey: .title)
self = .staticButton(title: title)
case .timeButton:
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
self = .timeButton(formatTemplate: template)
case .battery:
self = .battery()
case .dock:
self = .dock()
case .volume:
self = .volume()
case .brightness:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
self = .brightness(refreshInterval: interval)
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()
}
}
}
enum ActionType: Decodable {
case none
case hidKey(keycode: Int)
case keyPress(keycode: Int)
case appleSctipt(source: SourceProtocol)
case shellScript(executable: String, parameters: [String])
case custom(closure: ()->())
case openUrl(url: String)
private enum CodingKeys: String, CodingKey {
case action
case keycode
case actionAppleScript
case executablePath
case shellArguments
case url
}
private enum ActionTypeRaw: 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 type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
switch type {
case .some(.hidKey):
let keycode = try container.decode(Int.self, forKey: .keycode)
self = .hidKey(keycode: keycode)
case .some(.keyPress):
let keycode = try container.decode(Int.self, forKey: .keycode)
self = .keyPress(keycode: keycode)
case .some(.appleScript):
let source = try container.decode(Source.self, forKey: .actionAppleScript)
self = .appleSctipt(source: source)
case .some(.shellScript):
let executable = try container.decode(String.self, forKey: .executablePath)
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
self = .shellScript(executable: executable, parameters: parameters)
case .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
}
}
}
extension ItemType: Equatable {}
func ==(lhs: ItemType, rhs: ItemType) -> Bool {
switch (lhs, rhs) {
case let (.staticButton(a), .staticButton(b)):
return a == b
case let (.appleScriptTitledButton(a, b), .appleScriptTitledButton(c, d)):
return a == c && b == d
default:
return false
}
}
extension ActionType: Equatable {}
func ==(lhs: ActionType, rhs: ActionType) -> Bool {
switch (lhs, rhs) {
case (.none, .none):
return true
case let (.hidKey(a), .hidKey(b)),
let (.keyPress(a), .keyPress(b)):
return a == b
case let (.appleSctipt(a), .appleSctipt(b)):
return a == b
case let (.shellScript(a, b), .shellScript(c, d)):
return a == c && b == d
case let (.openUrl(a), .openUrl(b)):
return a == b
default:
return false
}
}
extension LongActionType: Equatable {}
func ==(lhs: LongActionType, rhs: LongActionType) -> Bool {
switch (lhs, rhs) {
case (.none, .none):
return true
case let (.hidKey(a), .hidKey(b)),
let (.keyPress(a), .keyPress(b)):
return a == b
case let (.appleSctipt(a), .appleSctipt(b)):
return a == b
case let (.shellScript(a, b), .shellScript(c, d)):
return a == c && b == d
case let (.openUrl(a), .openUrl(b)):
return a == b
default:
return false
}
}
enum GeneralParameter {
case width(_: CGFloat)
case image(source: SourceProtocol)
case align(_: Align)
case bordered(_: Bool)
case background(_:NSColor)
}
struct GeneralParameters: Decodable {
let parameters: [GeneralParameters.CodingKeys: GeneralParameter]
enum CodingKeys: String, CodingKey {
case width
case image
case align
case bordered
case background
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var result: [GeneralParameters.CodingKeys: GeneralParameter] = [:]
if let value = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
result[.width] = .width(value)
}
if let imageSource = try container.decodeIfPresent(Source.self, forKey: .image) {
result[.image] = .image(source: imageSource)
}
let align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center
result[.align] = .align(align)
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
}
}
protocol SourceProtocol {
var data: Data? { get }
var string: String? { get }
var image: NSImage? { get }
}
struct Source: Decodable, SourceProtocol {
let filePath: String?
let base64: String?
let inline: String?
private enum CodingKeys: String, CodingKey {
case filePath
case base64
case inline
}
var data: Data? {
return base64?.base64Data ?? inline?.data(using: .utf8) ?? filePath?.fileData
}
var string: String? {
return inline ?? self.data?.utf8string
}
var image: NSImage? {
return data?.image
}
private init(filePath: String?, base64: String?, inline: String?) {
self.filePath = filePath
self.base64 = base64
self.inline = inline
}
init(filePath: String) {
self.init(filePath: filePath, base64: nil, inline: nil)
}
}
extension NSImage: SourceProtocol {
var data: Data? {
return nil
}
var string: String? {
return nil
}
var image: NSImage? {
return self
}
}
extension SourceProtocol where Self: Equatable {}
func ==(left: SourceProtocol, right: SourceProtocol) -> Bool {
return left.data == right.data
}
extension String {
var base64Data: Data? {
return Data(base64Encoded: self)
}
var fileData: Data? {
return try? Data(contentsOf: URL(fileURLWithPath: self))
}
}
extension Data {
var utf8string: String? {
return String(data: self, encoding: .utf8)
}
var image: NSImage? {
return NSImage(data: self)?.resize(maxSize: NSSize(width: 24, height: 24))
}
}
enum Align: String, Decodable {
case left
case center
case right
}
+6 -13
View File
@@ -13,6 +13,10 @@ protocol KeyPress {
func send()
}
struct GenericKeyPress: KeyPress {
var keyCode: CGKeyCode
}
extension KeyPress {
func send () {
let src = CGEventSource(stateID: .hidSystemState)
@@ -25,19 +29,6 @@ extension KeyPress {
}
}
struct ESCKeyPress: KeyPress {
let keyCode: CGKeyCode = 53
}
struct BrightnessUpPress: KeyPress {
let keyCode: CGKeyCode = 113
}
struct BrightnessDownPress: KeyPress {
let keyCode: CGKeyCode = 107
}
func doKey(_ key: Int, down: Bool) {
let flags = NSEvent.ModifierFlags(rawValue: down ? 0xa00 : 0xb00)
let data1 = (key << 16) | ((down ? 0xa : 0xb) << 8)
@@ -67,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
@@ -78,3 +70,4 @@ let NX_KEYTYPE_PREVIOUS = 18
+21
View File
@@ -0,0 +1,21 @@
import Foundation
class ScrollViewItem: NSCustomTouchBarItem {
init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem]) {
super.init(identifier: identifier)
let views = items.compactMap { $0.view }
let stackView = NSStackView(views: views)
stackView.spacing = 1
stackView.orientation = .horizontal
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
scrollView.documentView = stackView
// scrollView.documentView?.bounds.origin = CGPoint(x: 0.0, y: -2.5)
self.view = scrollView
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
+106
View File
@@ -0,0 +1,106 @@
//
// SupportHelpers.swift
// MTMR
//
// Created by Anton Palgunov on 13/04/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Foundation
extension String {
func trim() -> String {
return self.trimmingCharacters(in: NSCharacterSet.whitespaces)
}
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 {
func resize(maxSize:NSSize) -> NSImage {
var ratio:Float = 0.0
let imageWidth = Float(self.size.width)
let imageHeight = Float(self.size.height)
let maxWidth = Float(maxSize.width)
let maxHeight = Float(maxSize.height)
// Get ratio (landscape or portrait)
if (imageWidth > imageHeight) {
// Landscape
ratio = maxWidth / imageWidth;
}
else {
// Portrait
ratio = maxHeight / imageHeight;
}
// Calculate new size based on the ratio
let newWidth = imageWidth * ratio
let newHeight = imageHeight * ratio
// Create a new NSSize object with the newly calculated size
let newSize:NSSize = NSSize(width: Int(newWidth), height: Int(newHeight))
// Cast the NSImage to a CGImage
var imageRect:NSRect = NSMakeRect(0, 0, self.size.width, self.size.height)
let imageRef = self.cgImage(forProposedRect: &imageRect, context: nil, hints: nil)
// Create NSImage from the CGImage using the new size
let imageWithNewSize = NSImage(cgImage: imageRef!, size: newSize)
// Return the new image
return imageWithNewSize
}
func rotateByDegreess(degrees:CGFloat) -> NSImage {
var imageBounds = NSZeroRect ; imageBounds.size = self.size
let pathBounds = NSBezierPath(rect: imageBounds)
var transform = NSAffineTransform()
transform.rotate(byDegrees: degrees)
pathBounds.transform(using: transform as AffineTransform)
let rotatedBounds:NSRect = NSMakeRect(NSZeroPoint.x, NSZeroPoint.y , self.size.width, self.size.height )
let rotatedImage = NSImage(size: rotatedBounds.size)
//Center the image within the rotated bounds
imageBounds.origin.x = NSMidX(rotatedBounds) - (NSWidth(imageBounds) / 2)
imageBounds.origin.y = NSMidY(rotatedBounds) - (NSHeight(imageBounds) / 2)
// Start a new transform
transform = NSAffineTransform()
// Move coordinate system to the center (since we want to rotate around the center)
transform.translateX(by: +(NSWidth(rotatedBounds) / 2 ), yBy: +(NSHeight(rotatedBounds) / 2))
transform.rotate(byDegrees: degrees)
// Move the coordinate system bak to normal
transform.translateX(by: -(NSWidth(rotatedBounds) / 2 ), yBy: -(NSHeight(rotatedBounds) / 2))
// Draw the original image, rotated, into the new image
rotatedImage.lockFocus()
transform.concat()
self.draw(in: imageBounds, from: NSZeroRect, operation: NSCompositingOperation.copy, fraction: 1.0)
rotatedImage.unlockFocus()
return rotatedImage
}
}
+303 -89
View File
@@ -8,125 +8,339 @@
import Cocoa
struct ExactItem {
let identifier: NSTouchBarItem.Identifier
let presetItem: BarItemDefinition
}
extension ItemType {
var identifierBase: String {
switch self {
case .staticButton(title: _):
return "com.toxblh.mtmr.staticButton."
case .appleScriptTitledButton(source: _):
return "com.toxblh.mtmr.appleScriptButton."
case .timeButton(formatTemplate: _):
return "com.toxblh.mtmr.timeButton."
case .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."
}
}
}
extension NSTouchBarItem.Identifier {
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
}
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()}), longAction: .none)
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 = []
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 = [
.escButton,
.dismissButton,
.brightDown,
.brightUp,
.flexibleSpace,
.prev,
.play,
.next,
.sleep,
.weather,
.volumeDown,
.volumeUp,
.battery,
.time,
]
touchBar.defaultItemIdentifiers = []
touchBar.defaultItemIdentifiers = self.leftIdentifiers + [centerScrollArea] + self.rightIdentifiers
self.presentTouchBar()
}
func setupControlStripPresence() {
func readConfig() -> [BarItemDefinition]? {
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
let presetPath = appSupportDirectory.appending("/items.json")
if !FileManager.default.fileExists(atPath: presetPath),
let defaultPreset = Bundle.main.path(forResource: "defaultPreset", ofType: "json") {
try? FileManager.default.createDirectory(atPath: appSupportDirectory, withIntermediateDirectories: true, attributes: nil)
try? FileManager.default.copyItem(atPath: defaultPreset, toPath: presetPath)
}
let jsonData = presetPath.fileData
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(time + "--" + UUID().uuidString)
let identifier = NSTouchBarItem.Identifier(identifierString)
itemDefinitions[identifier] = item
if item.align == .left {
leftIdentifiers.append(identifier)
}
if item.align == .right {
rightIdentifiers.append(identifier)
}
if item.align == .center {
centerIdentifiers.append(identifier)
}
}
}
func createItems() {
for (identifier, definition) in self.itemDefinitions {
self.items[identifier] = self.createItem(forIdentifier: identifier, definition: definition)
}
}
@objc func setupControlStripPresence() {
DFRSystemModalShowsCloseBoxWhenFrontMost(false)
let item = NSCustomTouchBarItem(identifier: .controlStripItem)
item.view = NSButton(image: #imageLiteral(resourceName: "Strip"), target: self, action: #selector(presentTouchBar))
NSTouchBarItem.addSystemTrayItem(item)
DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, true)
}
func updateControlStripPresence() {
DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, true)
}
@objc private func presentTouchBar() {
NSTouchBar.presentSystemModalFunctionBar(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
}
@objc private func dismissTouchBar() {
NSTouchBar.minimizeSystemModalFunctionBar(touchBar)
}
@objc func goToSleep() {
let task = Process()
task.launchPath = "/usr/bin/pmset"
task.arguments = ["sleepnow"]
task.launch()
}
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
switch identifier {
case .escButton:
return CustomButtonTouchBarItem(identifier: identifier, title: "esc", key: ESCKeyPress())
case .dismissButton:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "exit", target: self, action: #selector(dismissTouchBar))
return item
case .brightUp:
return CustomButtonTouchBarItem(identifier: identifier, title: "🔆", key: BrightnessUpPress())
case .brightDown:
return CustomButtonTouchBarItem(identifier: identifier, title: "🔅", key: BrightnessDownPress())
case .volumeDown:
return CustomButtonTouchBarItem(identifier: identifier, title: "🔉", HIDKeycode: NX_KEYTYPE_SOUND_DOWN)
case .volumeUp:
return CustomButtonTouchBarItem(identifier: identifier, title: "🔊", HIDKeycode: NX_KEYTYPE_SOUND_UP)
case .weather:
let url = Bundle.main.url(forResource: "weather", withExtension: "scpt")!
let script = try! String.init(contentsOf: url)
return AppleScriptTouchBarItem(identifier: identifier, appleScript: script, interval: 600)
case .sleep:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "☕️", target: self, action: #selector(goToSleep))
return item
case .prev:
return CustomButtonTouchBarItem(identifier: identifier, title: "⏪", HIDKeycode: NX_KEYTYPE_PREVIOUS)
case .play:
return CustomButtonTouchBarItem(identifier: identifier, title: "⏯", HIDKeycode: NX_KEYTYPE_PLAY)
case .next:
return CustomButtonTouchBarItem(identifier: identifier, title: "⏩", HIDKeycode: NX_KEYTYPE_NEXT)
case .battery:
let url = Bundle.main.url(forResource: "battery", withExtension: "scpt")!
let script = try! String.init(contentsOf: url)
return AppleScriptTouchBarItem(identifier: identifier, appleScript: script, interval: 60)
case .time:
return TimeTouchBarItem(identifier: identifier, formatTemplate: "HH:mm")
default:
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
if identifier == centerScrollArea {
return self.scrollArea
}
guard let item = self.items[identifier],
let definition = self.itemDefinitions[identifier],
definition.align != .center else {
return nil
}
return item
}
func 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, onLongTap: longAction, bezelColor: NSColor.controlColor)
case .appleScriptTitledButton(source: let source, refreshInterval: let interval):
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action, onLongTap: longAction)
case .timeButton(formatTemplate: let 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:
if case .image(let source)? = item.additionalParameters[.image] {
barItem = VolumeViewController(identifier: identifier, image: source.image)
} else {
barItem = VolumeViewController(identifier: identifier)
}
case .brightness(refreshInterval: let interval):
if case .image(let source)? = item.additionalParameters[.image] {
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval, image: source.image)
} else {
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
}
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.imageScaling = .scaleProportionallyDown
button.imagePosition = .imageLeading
if (button.title == "") {
button.imagePosition = .imageOnly
}
button.imageHugsTitle = true
button.image = source.image
}
return barItem
}
}
extension CustomButtonTouchBarItem {
convenience init(identifier: NSTouchBarItem.Identifier, title: String, HIDKeycode: Int) {
self.init(identifier: identifier, title: title) { _ in
HIDPostAuxKey(HIDKeycode)
func action(forItem item: BarItemDefinition) -> ()->() {
switch item.action {
case .hidKey(keycode: let keycode):
return { HIDPostAuxKey(keycode) }
case .keyPress(keycode: let keycode):
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
case .appleSctipt(source: let source):
guard let appleScript = source.appleScript else {
print("cannot create apple script for item \(item)")
return {}
}
return {
var error: NSDictionary?
appleScript.executeAndReturnError(&error)
if let error = error {
print("error \(error) when handling \(item) ")
}
}
case .shellScript(executable: let executable, parameters: let parameters):
return {
let task = Process()
task.launchPath = executable
task.arguments = parameters
task.launch()
}
case .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 {}
}
}
convenience init(identifier: NSTouchBarItem.Identifier, title: String, key: KeyPress) {
self.init(identifier: identifier, title: title) { _ in
key.send()
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 {
func setWidth(value: CGFloat)
}
extension NSCustomTouchBarItem: CanSetWidth {
func setWidth(value: CGFloat) {
self.view.widthAnchor.constraint(equalToConstant: value).isActive = true
}
}
extension BarItemDefinition {
var align: Align {
if case .align(let result)? = self.additionalParameters[.align] {
return result
}
return .center
}
}
-57
View File
@@ -1,57 +0,0 @@
//
// TouchBarItems.swift
// MTMR
//
// Created by Anton Palgunov on 18/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
extension NSTouchBarItem.Identifier {
static let escButton = NSTouchBarItem.Identifier("com.toxblh.mtmr.escButton")
static let dismissButton = NSTouchBarItem.Identifier("com.toxblh.mtmr.dismissButton")
// Volume
static let volumeUp = NSTouchBarItem.Identifier("com.toxblh.mtmr.volumeUp")
static let volumeDown = NSTouchBarItem.Identifier("com.toxblh.mtmr.volumeDown")
// Brightness
static let brightUp = NSTouchBarItem.Identifier("com.toxblh.mtmr.brightUp")
static let brightDown = NSTouchBarItem.Identifier("com.toxblh.mtmr.brightDown")
// Music
static let prev = NSTouchBarItem.Identifier("com.toxblh.mtmr.prev")
static let next = NSTouchBarItem.Identifier("com.toxblh.mtmr.next")
static let play = NSTouchBarItem.Identifier("com.toxblh.mtmr.play")
// Plugins
static let sleep = NSTouchBarItem.Identifier("com.toxblh.mtmr.sleep")
static let weather = NSTouchBarItem.Identifier("com.toxblh.mtmr.weather")
static let time = NSTouchBarItem.Identifier("com.toxblh.mtmr.time")
static let battery = NSTouchBarItem.Identifier("com.toxblh.mtmr.battery")
static let nowPlaying = NSTouchBarItem.Identifier("com.toxblh.mtmr.nowPlaying")
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
}
class CustomButtonTouchBarItem: NSCustomTouchBarItem {
let tapClosure: (NSCustomTouchBarItem) -> ()
init(identifier: NSTouchBarItem.Identifier, title: String, onTap callback: @escaping (NSCustomTouchBarItem) -> ()) {
self.tapClosure = callback
super.init(identifier: identifier)
self.view = NSButton(title: title, target: self, action: #selector(didTapped))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func didTapped() {
self.tapClosure(self)
let hf: HapticFeedback = HapticFeedback()
hf.tap(strong: 6)
}
}
-27
View File
@@ -1,27 +0,0 @@
//
// ViewController.swift
// MTMR
//
// Created by Anton Palgunov on 16/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
+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
}
}
@@ -0,0 +1,63 @@
import Cocoa
import AppKit
import AVFoundation
import CoreAudio
class BrightnessViewController: NSCustomTouchBarItem {
private(set) var sliderItem: CustomSlider!
init(identifier: NSTouchBarItem.Identifier, refreshInterval: Double, image: NSImage? = nil) {
super.init(identifier: identifier)
if (image == nil) {
sliderItem = CustomSlider()
} else {
sliderItem = CustomSlider(knob: image!)
}
sliderItem.target = self
sliderItem.action = #selector(BrightnessViewController.sliderValueChanged(_:))
sliderItem.minValue = 0.0
sliderItem.maxValue = 100.0
sliderItem.floatValue = getBrightness()*100
self.view = sliderItem
let timer = Timer.scheduledTimer(timeInterval: refreshInterval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true)
RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
sliderItem.unbind(NSBindingName.value)
}
@objc func updateBrightnessSlider() {
DispatchQueue.main.async {
self.sliderItem.floatValue = self.getBrightness() * 100
}
}
@objc func sliderValueChanged(_ sender: Any) {
if let sliderItem = sender as? NSSlider {
setBrightness(level: Float32(sliderItem.intValue)/100.0)
}
}
private func getBrightness() -> Float32 {
var level: Float32 = 0.5
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
IODisplayGetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, &level)
return level
}
private func setBrightness(level: Float) {
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
IODisplaySetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, level)
IOObjectRelease(service)
}
}
+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
+102
View File
@@ -0,0 +1,102 @@
import Cocoa
import AppKit
import AVFoundation
import CoreAudio
class VolumeViewController: NSCustomTouchBarItem {
private(set) var sliderItem: CustomSlider!
init(identifier: NSTouchBarItem.Identifier, image: NSImage? = nil) {
super.init(identifier: identifier)
var forPropertyAddress = AudioObjectPropertyAddress(
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster)
AudioObjectAddPropertyListenerBlock(defaultDeviceID, &forPropertyAddress, nil, audioObjectPropertyListenerBlock)
if (image == nil) {
sliderItem = CustomSlider()
} else {
sliderItem = CustomSlider(knob: image!)
}
sliderItem.target = self
sliderItem.action = #selector(VolumeViewController.sliderValueChanged(_:))
sliderItem.minValue = 0.0
sliderItem.maxValue = 100.0
sliderItem.floatValue = getInputGain()*100
self.view = sliderItem
}
func audioObjectPropertyListenerBlock (numberAddresses: UInt32, addresses: UnsafePointer<AudioObjectPropertyAddress>) {
DispatchQueue.main.async {
self.sliderItem.floatValue = self.getInputGain() * 100
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
sliderItem.unbind(NSBindingName.value)
}
@objc func sliderValueChanged(_ sender: Any) {
if let sliderItem = sender as? NSSlider {
_ = setInputGain(Float32(sliderItem.intValue)/100.0)
}
}
private var defaultDeviceID: AudioObjectID {
var deviceID: AudioObjectID = AudioObjectID(0)
var size: UInt32 = UInt32(MemoryLayout<AudioObjectID>.size)
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
address.mSelector = AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice)
address.mScope = AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal)
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &size, &deviceID)
return deviceID
}
private func getInputGain() -> Float32 {
var volume: Float32 = 0.5
var size: UInt32 = UInt32(MemoryLayout.size(ofValue: volume))
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
address.mSelector = AudioObjectPropertySelector(kAudioHardwareServiceDeviceProperty_VirtualMasterVolume)
address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput)
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
AudioObjectGetPropertyData(defaultDeviceID, &address, 0, nil, &size, &volume)
return volume
}
private func setInputGain(_ volume: Float32) -> OSStatus {
var inputVolume: Float32 = volume
if inputVolume == 0.0 {
_ = setMute( mute: 1)
} else {
_ = setMute( mute: 0)
}
let size: UInt32 = UInt32(MemoryLayout.size(ofValue: inputVolume))
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput)
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
address.mSelector = AudioObjectPropertySelector(kAudioHardwareServiceDeviceProperty_VirtualMasterVolume)
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &inputVolume)
}
private func setMute( mute: Int) -> OSStatus {
var muteVal: Int = mute
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
address.mSelector = AudioObjectPropertySelector(kAudioDevicePropertyMute)
let size: UInt32 = UInt32(MemoryLayout.size(ofValue: muteVal))
address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput)
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &muteVal)
}
}
+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()
}
}
-17
View File
@@ -1,17 +0,0 @@
//
// WindowController.swift
// MTMR
//
// Created by Anton Palgunov on 17/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
class WindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
}
}
+79
View File
@@ -0,0 +1,79 @@
[
{ "type": "escape", "align": "left" },
{ "type": "exitTouchbar", "width": 44, "align": "left" },
{ "type": "brightnessDown", "width": 44, "align": "left" },
{
"type": "brightness",
"width": 60,
"align": "left",
"image": {
"base64":
"iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAGUExURffLOPfLNyaSVzUAAAACdFJOU/kBxOqnWgAAAbJJREFUSMfVljtyhDAQBVulQCFH4CgcDR1NR9ERFBKoeA5GfGddtkNvwFINFKP5tED22+Zxwviv6QVKfIEc/iNoF5gkpLIeYI8SUp4PsAUJiekADQntF6isQjvxCTrhAJlFqMMBeIH9BMsD7DAb2BhvYbIyNAOCZIWqYKGTpDZJFQu9EKVd44RxQRq3IrULWD62C8wSssWUZEsR0k6wcDOrJZmoBpMKI+s5qkBQCQOUJADVOECdOsDS0gDbsgHMfT4rVwHSrZQFIN5ABka8BgDgAeZ+BztBgvUEnSgVlhNsTFJjvoF5HAZorBpdYKAiSRbqNyBIUr6AjZMdPwO72R40MElS+wZUWA+wQ6LAYkFvdIhkmA+wQSDDdIAGAZ6A34H0x0fca11gBZZsIHSIfnE/5+NjCn/OuiuUB+/aunZwDeNayjXdTpDN0wlY+r1PfWu75nfj8RogN2JuCN2Y5qgMwTI0wGPUnQw6Qarx0sVNKA5Mn6VUL22lIbZoYitDbPmlvocc9Umfl2D7adz1reC3pF8av4m+DCenp/ndZuG3E7fhuC3pH2+vnz8V3MfE+bnxBTXuuIMTrLWHAAAAAElFTkSuQmCC"
}
},
{ "type": "brightnessUp", "width": 44, "align": "left" },
{
"type": "appleScriptTitledButton",
"source": {
"inline":
"if application \"iTunes\" is running then\rtell application \"iTunes\"\rif player state is playing then\rreturn (get artist of current track) & \" – \" & (get name of current track)\relse\rreturn \"\"\rend if\rend tell\rend if\rreturn \"\"\r"
},
"action": "appleScript",
"actionAppleScript": {
"inline":
"if application \"iTunes\" is running then\rtell application \"iTunes\"\rif player state is playing then\rnext track\rend if\rend tell\rend if\r"
},
"refreshInterval": 1,
"image": {
"base64":
"iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADAUExURUdwTOVVZCzB+3qc0gkDBgEBAgcKEwAAAA4EBP5aVU2V95iJv7V3rtBOvH5W6jaOyclScKZGX3wuQCMuUqZN7+NQYXtDvFd9/sxYni2z6UhBhyhvnIp7sf38/PXz9ePm69/k6fHv8/j29+3q7/v6+ufq7uvu8fTw8+1egOFki/dbboVj/HNy/T+j/dNtnEul81vC8Vmf3OeRqOBVofK4xZfF7sDb7PLe6LxU1KKK79PL6vrW3fh4g5Zi4bi16daa0A3Qc90AAAAddFJOUwD3/v0uOlYNG/z+/v7998OYYztt/Le2/eDqi5jEo2rNTwAABMtJREFUWMPdl1tjqjoQhauC0Hqvta26JVxUQBQFRPBS/f//as8kISBqd/twXs5KjBDyrZkEFfP09D+WoigqFxz+mlbVXncweEZVBoNuD1x+E1vtMVZoURn01J/mob52KRNuj4k5mZjJcRueFotFpfv6EwdVRfy0tSdXSranheN0/zkRSB7x42Q6udExdBy3972Fog4o/kBg4X6bhPJaeX7eTqeT6b0MJtPpdOu6n49XQgX+dOQ8vpWEvYn/2AHiL052PpoROcsOtND17ztQ3rwTuCQz9O/moKiMf6BkG/puKBzurKQ64PmbU2bDzUxk3Uql4lZcl3Vpvt9VbxLoLZwjY7E1WcNZoB0XpbELie/3Sg6KVHG2jGPs1LTCE2UXFfgIgtBgyq8d/E/pehJq1zmZGc7kAPsMX4Ec932T25uX5vUklFcHJlDU1OT4wllkvOtn9lrSbF7dCUggNEtaMOXhQZq4WkpBcksJQBCOnyjvM4P8KqQgFW9BFJka2NMKB1gw+VMxvN9smnwI1EuzpxS+g9FWYySjsTpOtnq+H162iW01m/wyXLUPzT/5HKQoSjQmU8vE6TAElvWggbhuNRpScQa002bVtJmBCz9qNusWBkJmoyHmoHajC4yybVujhR26mJVha7lDo2FrhnA4N0aq+BpE24zjgsMoEsfU0AADaKCwemiIRZA+o6N9oygyMi/EAWk0DMNgFvCmN/5IwqCV3PCGzzIwbINrzgwykVq2iorUalm2UTZotXKWqVYz5uBjzDUoxrxWyzKQWy061LZsNIJ3PAMDIcbVauwdGmxrNblgYNnCgStN54ylBSsYoAxerwwgJsCWTS0sepym0Mdp1gYBw5lmwgDXIDEoaeHLYE36BafzuQWFQ9RASM/XQPpMD5YQ2gA/AwPArQJyZWDsgo64C+/pBRkDG4s31hdmwFNAGz1mBjPukBs8qdSgLDBA1LJm1lw/14IgWAdAQ5nhax4HY/FR7qfpHQMPUChzS0c6eFmv17MZo7HZBP3MQJHTNCnzxPMgOFUteAGtMwMmfRPIijBgc+AmNK6+9zw+9Iw05YsG8aaT/7Kro7eUcoSWw3n/1W57SxgGOZCYxl+VDDabcf6LpNTf3g6IQ4XY7TbiyyUBmhqsViswWK02cE7ITIdmtxnWlcJz6f1tPyMsha+2R4UGyJPdywotwGSHOKHdm+IMYA40BRhtkXOb42DAh8crppjw8CyB4nMBlvFtD/0WSfZebkDHguLNar2JdyTXptqRrx6OmMKZkISQc4Yv9yyDXHiiEx1qXL1OAFdhBJPAAQeRQDEiRZEm+kwnu2p1XHo64yQ8j47bL1kCZ87pDKWxuW4mQJ9O9ba31xE5Y/rnA4VoTCJQwvnyBNgk+pkDi8sSJjlKRPxhX7r3Lytz0LPMi1H1Qv7VuzwuAzh4h1ukKFi/YV9+9E8THZbne2Ezxd/xsNGQ6u+wgoeH4SH9Tl367t+2Ko/acA8Oj/DhWP7X/30Zkvj4WMYlj10MOISXf7DlkPvvH6g43u0oCzDS1U5f/sHWC3d7cn3UAQf4HeHfwxXQY4yu/HTDKNXro3Gngw4vw2FnPKrXJfUXu0fqIdeFZOnXm08FTRSxcf391pW7oNGT8vRf6i9jqljwYzAm6AAAAABJRU5ErkJggg=="
}
},
{
"type": "appleScriptTitledButton",
"source": {
"inline":
"if application \"Spotify\" is running then\rtell application \"Spotify\"\rif player state is playing then\rreturn (get artist of current track) & \" – \" & (get name of current track)\relse\rreturn \"\"\rend if\rend tell\rend if\rreturn \"\"\r"
},
"action": "appleScript",
"actionAppleScript": {
"inline":
"if application \"Spotify\" is running then\rtell application \"Spotify\"\rif player state is playing then\rnext track\rend if\rend tell\rend if\r"
},
"refreshInterval": 1,
"image": {
"base64":
"iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAYUExURUdwTB3WXx3UXh3VXx7XYBkXFRpVLRyURmIaeAQAAAAEdFJOUwDDO3fSqUUkAAABbklEQVRIx61VbW6DMAztoAeYNA7ApB6gkzhAWS24wIAL0HABaK6/pHFNEhy8TXu/kPzkPD/8cTj8K7KPAqB+K5NhQPCUrABCXe7HOUYYZxgVRLiG8RfY4DUgFFtC7cffAfZTFBwBdhWEKfgEq4ocEjgj4ZQifO6/QG9kkETp1dDeVWfRKx3XYSW0LoqY5kCElXDrQkyeCCuh6WL0M4nIWQIyzqixdfKU1koFDKvyCA8NJMzU4xiD+b4kfHRpsIyKc6hBwjVptFHVY51EMAINNDFGJITKDNQcdpX74Hz0CQ3rY5qwMp4EIxrlafzrsYZ2Veb0DkRgfNCUok4Y1fqEijfyi2b8RE9beWqa48Y/uvCNMcH9btfUi+/CGLR1vhL6Zz9N/vBlaCU+7lwY/cmJ67Ryen/2tj23PLqJBodZH8vgj544vOL4pxfI5acrSFxi8hrkU9TSKr78ZpnL50A8KPJJEo+afBblwyqf5j/iGys5j6ScrST2AAAAAElFTkSuQmCC"
}
},
{
"type": "appleScriptTitledButton",
"source": {
"inline":
"if application \"VOX\" is running then\rtell application \"VOX\"\rif player state is 1 then\rreturn (get artist) & \" – \" & (get track)\relse\rreturn \"\"\rend if\rend tell\rend if\rreturn \"\"\r"
},
"action": "appleScript",
"actionAppleScript": {
"inline":
"if application \"VOX\" is running then\rtell application \"VOX\"\rif player state is 1 then\rnext\rend if\rend tell\rend if\r"
},
"refreshInterval": 1,
"image": {
"base64":
"iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADAUExURUdwTHd7gqeqtKClrd/k7n+Di7e7xI6TnNHX4cPI0Xp9hJibpOfs9u/1/vX7/rq/yZiborK3wEpOUxgYFlFXYiIhHycpLD9ESjI0NgsLCj09O4SKk19lcbBkALhoAHJ3gT8rAzMvJJugqKhcAL5xAOSxB4I/AJ5SAGpweXuAiZJJAG8zAeXr9WNiYM+NANqeAMd9APHACfr9/e7JPrO5w6mrsA4bKC0gCHhtTyAYBFcpAZduKK2JP/fost/Eb5SUlFFfi9AAAAASdFJOUwDJE9FgWjb+/vyHppTXtbn9mDupvZ0AAAdoSURBVFjDlZcLQ6O6FoWrtgI+Owl5EGgp2DrEgnV6wD4c7f//V3ftAE7nzOj17NGxpayPtXfSZGcw+CjOh1c3N9eIm5ur4fngv8Xp8KZE+F2U5bq8GZ5+WU5qP6jzsRdTeF7e1AEoN8Ovya/LMsg9mxZJErpIksJarwnK8nr4FblfQ52EDOH0zL1KUuvVfnn9eTVOryCPoVZQuGj/FOApMAhx9Uktzq/L2ksT3FuQhIVVVVRVRa+SogA1KbzgExPD0s/xdBOmKdOqODR95IdEaZba0LAkbfzyg0rA/pgekxZKV4cmtxXTEmFUEufNodIKYJjLkcbf9UEM90mqZJU3qeJRxDkXAv/hlbJNXkmVJjCBNP5CGPq1TZSyiWB5XomIi+woRBSJKj8YUVgFQ4E//FMfQM8s40VTyIhDpLVRNIJKaWLwSNqm4mEMl/YPwrkP/9AbER+UkxtWbbcvLrbbkBlCcDa23BABHn4bi1PSM2a1PFgZwbwJty+TybSLyeRlyxQlIuNY6FgpDKd/PB+uXP2tFHEheCtvhW3Q65dtiEyQRixMrBjG4uo4gTpNFApvU9Kr6sWp77qY3BFj8lLBBJdpykOLwaqPkgioAEnFi1RCz7Z3uJ2U37sgCBB3W5ZJLm0VJQmVIXgfgSAvQpYK1ACDx7bTOT2dlA8uWsYEl0EQ3KDUKcOECvqRCGgGpApXf9M/PNx3QQwyMZ9uGWZWiGelsFAHvQFUMEx4WAku2HZC+k6+vF/ip0U4wmSrBJcFZgss9xZuYYAlWhaGC1PhMb1++R6EaAl3lRFcFUI5C7duDlAFwpCzSogsfCH93fdWvm+jRYCASs5fwkyIinGsVrBAc2FYxwVLlKhgQG3n8+kv/WwjhNhsZvveQ5+EqrhKWOHVlMNtgyUkhHkpdPjy1CZA+pl4bkOIfWcBQ/EEC1xWWmKxsi6HussA9WUwQBV0BmbPz3y32+x2nD8/z/okpvM5LAjGeEg51JiFtQeAEqHhWUIGJq0B0m9mLjYdgSxMnQVhGHIIkcP5YNjEAGjJJDfF5AnzzRnY/9K3hP3SjSUAk0oLzaQOwyJGEb41FgWVuOIymHYZCKfPGFaxzBHEcQ5cYrXD3LHNt8E3WkdDbpBY8vMdMHsW0Jui9v26MCDAggNMAfgZoghGdIDbPMXewY0RWfHzqQMsN2QgC/MAkVcZWZi1DuYAJJlQBlVM0vx2cDZOw4QAPEt/AcTzDgkkh3w8Hh8SPZvtKIc2haefKapo2mE4G5x5nQOuLQA0jwggCcAqF1SFnZQEwEwCwGZ0P+sAXQqa69gBJvgeLCUBhMS6arSWogV0NWwB2gHG5KAggDYO0OXQATghJP4QIPsFiJHCEYB2YgBE5gBzNxEyjVHYcOxKKDfquRE6u39wFXh6dADJaccG4HLsANIIEb89PnaEvSYLG2kYAG0G+3YMnh4fDxbfe4wkAS47gAAyShsA2iSWUgsiYHfJMCF2ZKDVA9AkET4WbhgvB6OxxeoiuZZRVT/2BFjQfOemMX53qNn+e5fA42OtAOCYiYXNR4PznL4LJoLA1G8rQjjCTCmxc9+GzU4oNXvonr9avTUZPc7QdyHH0j4mgIqQVZQHq1VvAgRKf7MRKISZPWAG0ONXq1Vg6WYahMIbYz24pJ6ECYIyf7EiBBGm3/da0c6KX/ifTnv9P74iu66G3iUAI4+KoCNpeNS8rha9ifnkfmlcLO+n816+WL3mEVVcY2ew4xEtqsgBjUWEQkWsfFssWhdgPM0xoBg8iKl2JF8s3kpngCtMo3jsNljkUDAmYQFVWC9+tAgHeQ93AR/8WKw9d6MMWWFdBn0OJsJsjERZLn4QomOsWmmrpk9Kn0eYxpGiMfBG7dZ0FrcW8B2N1PoV9zkEUehn1b2hy68lxg/F0owVaXzW7Y0j6kwZuiqhUIaW8M54F9Olcq0irF0R1vAkfTdAFmwaKhjDUgfCur39n+NwF9ZrFhnMWm5YiM737L0/GHkxtYD4RKIWILz1+jf86/Vv69I4fSTRTNojAxgIdPUgCE7VjXhzhHAEJ1/nUUSl4qTHKeDyuMnCycCzjiCwyUYsWK9fofzhxIC8rtcBi2SlqPeEnu7/rekGwcutwhTHhKoYHlWvEa8u6FVN17D/Or3Ncfu/Wu5R7I2bmNECRiYYRpuaYtKiBa+0u0j9K7aguMFZZvTvVvUCFuoce5ygSmA9xlcRDR8iyzaZqVq50Coc1zBw8WezfBEfmqBGX+8QXLs1HY9tV3aMMeQSp4A6aP6qdx4aNHyMWnxBHbpwzTIW9vadkGTf/1BPdYhxsqrR7zuGoD4/6pp+iKVkaY3zWGxHHx55TuJxXZa1RSmwIyAyeoGVlf4wG9CBLD755Nx1ehHHub9e+3lBpxUh+0DuuO4ef/H5AXR0gkpg+EofjQed1ej8VsTuEk6T8cno/54cRyc2zms3Bcr+4Esvazz9C/IOYWlESNseoWtU3n5V3tYCDEA8tAce5j3enFyc/scT/Pno4qSLi9HHhf8f8eOIRf9lFswAAAAASUVORK5CYII="
}
},
{ "type": "previous", "width": 44 },
{ "type": "play", "width": 44 },
{ "type": "next", "width": 44 },
{ "type": "weather", "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" }
]
-32
View File
@@ -1,32 +0,0 @@
# This script requires two libs. Download them:
# https://itunes.apple.com/ru/app/json-helper-for-applescript/id453114608?l=en&mt=12
# https://itunes.apple.com/ru/app/location-helper-for-applescript/id488536386?mt=12
tell application "Location Helper"
set clocation_coords to get location coordinates
tell application "JSON Helper"
set weather to fetch JSON from "http://api.openweathermap.org/data/2.5/weather?lat=" & item 1 of clocation_coords & "&lon=" & item 2 of clocation_coords & "&units=metric&appid=32c4256d09a4c52b38aecddba7a078f6"
set temp to temp of main of weather as string
set cond_icon to icon of item 1 of weather of weather as string
if cond_icon is in ["01d", "01n"] then
set cond to "☀️"
else if cond_icon is in ["02d", "02n"] then
set cond to "⛅️"
else if cond_icon is in ["03d", "03n", "04d", "04n"] then
set cond to "☁️"
else if cond_icon is in ["09d", "09n"] then
set cond to "🌧"
else if cond_icon is in ["10d", "10n"] then
set cond to "🌦"
else if cond_icon is in ["11d", "11n"] then
set cond to "🌩"
else if cond_icon is in ["13d", "13n"] then
set cond to "❄️"
else if cond_icon is in ["50d", "50n"] then
set cond to "🌫"
else
set cond to ""
end if
set temp_round to round (temp * 1.0)
return cond & " " & temp_round & "°C"
end tell
end tell
-36
View File
@@ -1,36 +0,0 @@
//
// MTMRTests.swift
// MTMRTests
//
// Created by Anton Palgunov on 16/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import XCTest
@testable import MTMR
class MTMRTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
+33
View File
@@ -0,0 +1,33 @@
import XCTest
@testable import MTMR
class ParseConfig: XCTestCase {
func testButtonNoAction() {
let buttonNoActionFixture = """
[ { "type": "staticButton", "title": "Pew" } ]
""".data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
XCTAssertEqual(result?.first?.type, .staticButton(title: "Pew"))
XCTAssertEqual(result?.first?.action, .some(.none))
}
func testButtonKeyCodeAction() {
let buttonKeycodeFixture = """
[ { "type": "staticButton", "title": "Pew", "action": "hidKey", "keycode": 123} ]
""".data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture)
XCTAssertEqual(result?.first?.type, .staticButton(title: "Pew"))
XCTAssertEqual(result?.first?.action, .hidKey(keycode: 123))
}
func testPredefinedItem() {
let buttonKeycodeFixture = """
[ { "type": "brightnessUp" } ]
""".data(using: .utf8)!
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture)
XCTAssertEqual(result?.first?.type, .staticButton(title: "🔆"))
XCTAssertEqual(result?.first?.action, .keyPress(keycode: 113))
}
}
-22
View File
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
-36
View File
@@ -1,36 +0,0 @@
//
// MTMRUITests.swift
// MTMRUITests
//
// Created by Anton Palgunov on 16/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import XCTest
class MTMRUITests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
XCUIApplication().launch()
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// Use recording to get started writing UI tests.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
}
+221 -9
View File
@@ -1,11 +1,14 @@
<p align="center">
<img src="Resources/logo.png" width="120">
</p>
# 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.3.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 :)
@@ -17,17 +20,22 @@ My the idea is to create the program like a platform for plugins for customizati
- [x] Time in touchbar!
- [x] First the weather plugin
- [x] Find how to open full-screen TouchBar without the cross and stripe menu
- [ ] Find how to add haptic feedback
- [ ] JSON or another approch for save preset and plugins maybe in `~/mtmr`
- [ ] Layout: [always left, NSSliderView for center, always right]
- [ ] Custom buttons size, actions by click in settings
- [ ] Add icon and menu in StatusBar
- [ ] Hide from Dock
- [ ] Status menu: "preferences", "quit"
- [x] Find how to add haptic feedback
- [x] Add icon and menu in StatusBar
- [x] Hide from Dock
- [x] Status menu: "preferences", "quit"
- [x] JSON or another approch for save preset, maybe in `~/Library/Application Support/MTMR/`
- [x] Custom buttons size, actions by click
- [x] Layout: [always left, NSSliderView for center, always right]
- [ ] Overwrite default values from item types (e.g. title for brightness)
- [ ] Custom settings for paddings and margins for buttons
- [ ] XPC Service for scripts
- [ ] UI for settings
- [ ] Import config from BTT
- [ ] System for autoupdate (maybe https://sparkle-project.org/)
Settings:
- [ ] Intarface for plugins and export like presets
- [ ] Interface for plugins and export like presets
- [ ] Startup at login
- [ ] Show on/off in Dock
- [ ] Show on/off in StatusBar
@@ -35,3 +43,207 @@ Settings:
Maybe:
- [ ] Refactoring the application on packages (AppleScript, JavaScript? and Swift?)
## Installation
- Download last [release](https://github.com/Toxblh/MTMR/releases)
- Or via Homebrew `brew cask install mtmr`
## Preset
File for customize your preset for MTMR: `open ~/Library/Application\ Support/MTMR/items.json`
## Built-in button types:
- escape
- exitTouchbar
- brightnessUp
- brightnessDown
- volumeDown
- volumeUp
- mute
- dock
> Native Plugins
- battery
- currency
- weather
- inputsource
> Media Keys
- previous
- play
- next
> AppleScript plugins
- sleep
- displaySleep
## Built-in slider types:
- brightness
- volume
### You can also make a custom buttons using these types
- `staticButton`
```json
"type": "staticButton",
"title": "esc",
```
- `appleScriptTitledButton`
```js
"type": "appleScriptTitledButton",
"refreshInterval": 60, //optional
"source": {
"filePath": "/Users/toxblh/Library/Application Support/MTMR/iTunes.nowPlaying.scpt",
// or
"inline": "tell application \"Finder\"\rmake new Finder window\rset target of front window to path to home folder as string\ractivate\rend tell",
// or
"base64": "StringInbase64"
},
```
- `timeButton`
```js
"type": "timeButton",
"formatTemplate": "HH:mm" //optional
```
## 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,
```
- `keyPress`
```json
"action": "keyPress",
"keycode": 1,
```
- `appleScript`
```js
"action": "appleScript",
"actionAppleScript": {
"inline": "tell application \"Finder\"\rmake new Finder window\rset target of front window to path to home folder as string\ractivate\rend tell"
// "filePath" or "base64" will work as well
},
```
- `shellScript`
```js
"action": "shellScript",
"executablePath": "/usr/bin/pmset",
"shellArguments": ["sleepnow"], // optional
```
- `openUrl`
```js
"action": "openUrl",
"url": "https://google.com",
```
## Additional parameters:
- `width` allow to restrict how much room a particular button will take
```json
"width": 34
```
- `align` can stick the item to the side. default is center
```js
"align": "left" //or "right" or "center"
```
## Example configuration:
```json
[
{ "type": "escape", "width": 110 },
{ "type": "exitTouchbar", "align": "left" },
{
"type": "brightnessUp",
"align": "left",
"width": 36
},
{
"type": "staticButton",
"align": "left",
"title": "🔆",
"action": "keyPress",
"keycode": 113,
"width": 36
},
{
"type": "appleScriptTitledButton",
"source": {
"filePath": "/Users/toxblh/Library/Application Support/MTMR/iTunes.nowPlaying.scpt"
},
"refreshInterval": 1
},
{
"type": "staticButton",
"align": "left",
"image": { "base64" : "%base64Finder%"},
"action": "appleScript",
"actionAppleScript": {
"inline": "tell application \"Finder\"\rmake new Finder window\rset target of front window to path to home folder as string\ractivate\rend tell"
},
"width": 36
},
{
"type": "appleScriptTitledButton",
"source": {
"inline": "if application \"Safari\" is running then\r\ttell application \"Safari\"\r\t\trepeat with t in tabs of windows\r\t\t\ttell t\r\t\t\t\tif URL starts with \"https:\/\/music.yandex.ru\" and name does not end with \"на Яндекс.Музыке\" then\r\t\t\t\t\treturn name of t as text\r\t\t\t\tend if\r\t\t\tend tell\r\t\tend repeat\r\tend tell\rend if\rreturn \"\""
},
"refreshInterval": 1
},
{ "type": "previous", "width": 36, "align": "right" },
{ "type": "play", "width": 36, "align": "right" },
{ "type": "next", "width": 36, "align": "right" },
{ "type": "sleep", "width": 36 , "align": "right"},
{ "type": "displaySleep", "align": "right" },
{ "type": "weather", "refreshInterval": 1800, "width": 70, "align": "right" },
{ "type": "volumeDown", "width": 36 , "align": "right"},
{ "type": "volumeUp", "width": 36 , "align": "right"},
{ "type": "battery", "refreshInterval": 60 , "align": "right"},
{ "type": "appleScriptTitledButton", "refreshInterval": 1800, "source": { "filePath": "/Users/redetection/Library/Application Support/MTMR/Weather.scpt"} , "align": "right"},
{ "type": "timeButton", "formatTemplate": "HH:mm", "width": 64, "align": "right" }
]
```
### Author's presets
[@Toxblh preset](Resources/toxblh.json)
[@ReDetection preset](Resources/ReDetection.json)
## Credits
Built by [@Toxblh](https://patreon.com/toxblh) and [@ReDetection](http://patreon.com/ReDetection).
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because one or more lines are too long
+5
View File
@@ -0,0 +1,5 @@
## TODOs
* try view controllers on `NSCustomTouchBarItem` instead of subclassing item itself
* try move away from enums when parse preset – enums are hard to extend
* find better way to hide bar items
Executable
+24
View File
@@ -0,0 +1,24 @@
NAME='MTMR'
rm -r Release 2>/dev/null
xcodebuild archive \
-scheme "$NAME" \
-archivePath Release/App.xcarchive
xcodebuild \
-exportArchive \
-archivePath Release/App.xcarchive \
-exportOptionsPlist export-options.plist \
-exportPath Release
cd Release
rm -r App.xcarchive
# Prerequisite: npm i -g create-dmg
create-dmg "${NAME}.app"
VERSION=`mdls -raw -name kMDItemVersion ${NAME}.app`
echo $VERSION
zip -r "${NAME}v${VERSION}.zip" "${NAME}.app"
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>teamID</key>
<string>D6D8BR2QNB</string>
<key>method</key>
<string>mac-application</string>
</dict>
</plist>