mirror of
https://github.com/Toxblh/MTMR.git
synced 2026-10-31 18:38:35 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d1d666986 | ||
|
|
560d8ed508 | ||
|
|
06d6f06adf | ||
|
|
0fafe3912e | ||
|
|
319241d4d4 | ||
|
|
61d5e8f77e | ||
|
|
e62530d7ab | ||
|
|
43675027d5 | ||
|
|
b70c040111 | ||
|
|
e795f77ba1 | ||
|
|
37b0462bc1 | ||
|
|
d2d33dee3b | ||
|
|
c0fc40b1c9 | ||
|
|
47a2027443 | ||
|
|
b3c1d6b323 | ||
|
|
a15cc6506f | ||
|
|
35a6ceae07 | ||
|
|
be1c439867 | ||
|
|
9f84b38084 | ||
|
|
86954a7981 | ||
|
|
8554dfeb5e | ||
|
|
bd2cd6d0b7 | ||
|
|
06ecdfe016 | ||
|
|
85d54e4f53 | ||
|
|
b368a21f62 |
@@ -69,3 +69,5 @@ fastlane/report.xml
|
|||||||
fastlane/Preview.html
|
fastlane/Preview.html
|
||||||
fastlane/screenshots
|
fastlane/screenshots
|
||||||
fastlane/test_output
|
fastlane/test_output
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
|||||||
@@ -38,6 +38,8 @@
|
|||||||
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; };
|
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; };
|
||||||
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; };
|
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; };
|
||||||
B0679BC1215AE73F000FC6B4 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */; };
|
B0679BC1215AE73F000FC6B4 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */; };
|
||||||
|
B08126EF217BD0B900A98970 /* PomodoroBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08126EE217BD0B900A98970 /* PomodoroBarItem.swift */; };
|
||||||
|
B08126F1217BE19000A98970 /* WidgetProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08126F0217BE19000A98970 /* WidgetProtocol.swift */; };
|
||||||
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08173262135F02B005D4908 /* NightShiftBarItem.swift */; };
|
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08173262135F02B005D4908 /* NightShiftBarItem.swift */; };
|
||||||
B081732A2135F354005D4908 /* CoreBrightness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B08173292135F354005D4908 /* CoreBrightness.framework */; };
|
B081732A2135F354005D4908 /* CoreBrightness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B08173292135F354005D4908 /* CoreBrightness.framework */; };
|
||||||
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B081732B213739FE005D4908 /* DnDBarItem.swift */; };
|
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B081732B213739FE005D4908 /* DnDBarItem.swift */; };
|
||||||
@@ -110,6 +112,8 @@
|
|||||||
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TouchBarPrivateApi-Bridging.h"; sourceTree = "<group>"; };
|
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TouchBarPrivateApi-Bridging.h"; sourceTree = "<group>"; };
|
||||||
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DFRFoundation.framework; path = ../../../../../System/Library/PrivateFrameworks/DFRFoundation.framework; sourceTree = "<group>"; };
|
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DFRFoundation.framework; path = ../../../../../System/Library/PrivateFrameworks/DFRFoundation.framework; sourceTree = "<group>"; };
|
||||||
B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = "<group>"; };
|
B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = "<group>"; };
|
||||||
|
B08126EE217BD0B900A98970 /* PomodoroBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PomodoroBarItem.swift; sourceTree = "<group>"; };
|
||||||
|
B08126F0217BE19000A98970 /* WidgetProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetProtocol.swift; sourceTree = "<group>"; };
|
||||||
B08173262135F02B005D4908 /* NightShiftBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightShiftBarItem.swift; sourceTree = "<group>"; };
|
B08173262135F02B005D4908 /* NightShiftBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightShiftBarItem.swift; sourceTree = "<group>"; };
|
||||||
B08173282135F128005D4908 /* CBBlueLightClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CBBlueLightClient.h; sourceTree = "<group>"; };
|
B08173282135F128005D4908 /* CBBlueLightClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CBBlueLightClient.h; sourceTree = "<group>"; };
|
||||||
B08173292135F354005D4908 /* CoreBrightness.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBrightness.framework; path = ../../../../../System/Library/PrivateFrameworks/CoreBrightness.framework; sourceTree = "<group>"; };
|
B08173292135F354005D4908 /* CoreBrightness.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBrightness.framework; path = ../../../../../System/Library/PrivateFrameworks/CoreBrightness.framework; sourceTree = "<group>"; };
|
||||||
@@ -284,6 +288,8 @@
|
|||||||
60669B4220AD8FA80074E817 /* GroupBarItem.swift */,
|
60669B4220AD8FA80074E817 /* GroupBarItem.swift */,
|
||||||
B08173262135F02B005D4908 /* NightShiftBarItem.swift */,
|
B08173262135F02B005D4908 /* NightShiftBarItem.swift */,
|
||||||
B081732B213739FE005D4908 /* DnDBarItem.swift */,
|
B081732B213739FE005D4908 /* DnDBarItem.swift */,
|
||||||
|
B08126EE217BD0B900A98970 /* PomodoroBarItem.swift */,
|
||||||
|
B08126F0217BE19000A98970 /* WidgetProtocol.swift */,
|
||||||
);
|
);
|
||||||
path = Widgets;
|
path = Widgets;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -334,7 +340,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0920;
|
LastSwiftUpdateCheck = 0920;
|
||||||
LastUpgradeCheck = 1000;
|
LastUpgradeCheck = 1010;
|
||||||
ORGANIZATIONNAME = "Anton Palgunov";
|
ORGANIZATIONNAME = "Anton Palgunov";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
B082B24E205C7D8000BC04DC = {
|
B082B24E205C7D8000BC04DC = {
|
||||||
@@ -432,9 +438,11 @@
|
|||||||
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */,
|
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */,
|
||||||
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */,
|
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */,
|
||||||
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */,
|
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */,
|
||||||
|
B08126F1217BE19000A98970 /* WidgetProtocol.swift in Sources */,
|
||||||
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */,
|
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */,
|
||||||
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */,
|
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */,
|
||||||
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
|
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
|
||||||
|
B08126EF217BD0B900A98970 /* PomodoroBarItem.swift in Sources */,
|
||||||
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */,
|
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */,
|
||||||
60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */,
|
60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */,
|
||||||
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
|
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
|
||||||
@@ -618,7 +626,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = D6D8BR2QNB;
|
DEVELOPMENT_TEAM = D6D8BR2QNB;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1000"
|
LastUpgradeVersion = "1010"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1000"
|
LastUpgradeVersion = "1010"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
+40
-19
@@ -11,16 +11,18 @@ import Sparkle
|
|||||||
|
|
||||||
@NSApplicationMain
|
@NSApplicationMain
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
|
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
|
||||||
|
var isBlockedApp: Bool = false
|
||||||
|
|
||||||
private var fileSystemSource: DispatchSourceFileSystemObject?
|
private var fileSystemSource: DispatchSourceFileSystemObject?
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_: Notification) {
|
||||||
// Configure Sparkle
|
// Configure Sparkle
|
||||||
SUUpdater.shared().automaticallyDownloadsUpdates = false
|
SUUpdater.shared().automaticallyDownloadsUpdates = false
|
||||||
SUUpdater.shared().automaticallyChecksForUpdates = true
|
SUUpdater.shared().automaticallyChecksForUpdates = true
|
||||||
SUUpdater.shared().checkForUpdatesInBackground()
|
SUUpdater.shared().checkForUpdatesInBackground()
|
||||||
|
|
||||||
let _ = AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true] as NSDictionary)
|
AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true] as NSDictionary)
|
||||||
|
|
||||||
TouchBarController.shared.setupControlStripPresence()
|
TouchBarController.shared.setupControlStripPresence()
|
||||||
|
|
||||||
@@ -30,13 +32,29 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
createMenu()
|
createMenu()
|
||||||
|
|
||||||
reloadOnDefaultConfigChanged()
|
reloadOnDefaultConfigChanged()
|
||||||
|
|
||||||
|
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(updateIsBlockedApp), name: NSWorkspace.didLaunchApplicationNotification, object: nil)
|
||||||
|
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(updateIsBlockedApp), name: NSWorkspace.didTerminateApplicationNotification, object: nil)
|
||||||
|
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(updateIsBlockedApp), name: NSWorkspace.didActivateApplicationNotification, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ aNotification: Notification) {
|
func applicationWillTerminate(_: Notification) {}
|
||||||
|
|
||||||
|
@objc func updateIsBlockedApp() {
|
||||||
|
var blacklistAppIdentifiers: [String] = []
|
||||||
|
if let blackListed = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.blackListedApps") {
|
||||||
|
blacklistAppIdentifiers = blackListed
|
||||||
|
}
|
||||||
|
var frontmostApplicationIdentifier: String? {
|
||||||
|
guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil }
|
||||||
|
return frontmostId
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openPreferences(_ sender: Any?) {
|
isBlockedApp = blacklistAppIdentifiers.index(of: frontmostApplicationIdentifier!) != nil
|
||||||
|
createMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func openPreferences(_: Any?) {
|
||||||
let task = Process()
|
let task = Process()
|
||||||
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
||||||
let presetPath = appSupportDirectory.appending("/items.json")
|
let presetPath = appSupportDirectory.appending("/items.json")
|
||||||
@@ -45,13 +63,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
task.launch()
|
task.launch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func toggleControlStrip(_ sender: Any?) {
|
@objc func toggleControlStrip(_: Any?) {
|
||||||
TouchBarController.shared.controlStripState = !TouchBarController.shared.controlStripState
|
TouchBarController.shared.showControlStripState = !TouchBarController.shared.showControlStripState
|
||||||
createMenu()
|
|
||||||
TouchBarController.shared.resetControlStrip()
|
TouchBarController.shared.resetControlStrip()
|
||||||
|
createMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func toggleBlackListedApp(_ sender: Any?) {
|
@objc func toggleBlackListedApp(_: Any?) {
|
||||||
let appIdentifier = TouchBarController.shared.frontmostApplicationIdentifier
|
let appIdentifier = TouchBarController.shared.frontmostApplicationIdentifier
|
||||||
if appIdentifier != nil {
|
if appIdentifier != nil {
|
||||||
if let index = TouchBarController.shared.blacklistAppIdentifiers.index(of: appIdentifier!) {
|
if let index = TouchBarController.shared.blacklistAppIdentifiers.index(of: appIdentifier!) {
|
||||||
@@ -64,11 +82,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
UserDefaults.standard.synchronize()
|
UserDefaults.standard.synchronize()
|
||||||
|
|
||||||
TouchBarController.shared.updateActiveApp()
|
TouchBarController.shared.updateActiveApp()
|
||||||
|
updateIsBlockedApp()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openPreset(_ sender: Any?) {
|
@objc func openPreset(_: Any?) {
|
||||||
let dialog = NSOpenPanel();
|
let dialog = NSOpenPanel()
|
||||||
|
|
||||||
dialog.title = "Choose a items.json file"
|
dialog.title = "Choose a items.json file"
|
||||||
dialog.showsResizeIndicator = true
|
dialog.showsResizeIndicator = true
|
||||||
@@ -84,7 +103,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func toggleStartAtLogin(_ sender: Any?) {
|
@objc func toggleStartAtLogin(_: Any?) {
|
||||||
LaunchAtLoginController().setLaunchAtLogin(!LaunchAtLoginController().launchAtLogin, for: NSURL.fileURL(withPath: Bundle.main.bundlePath))
|
LaunchAtLoginController().setLaunchAtLogin(!LaunchAtLoginController().launchAtLogin, for: NSURL.fileURL(withPath: Bundle.main.bundlePath))
|
||||||
createMenu()
|
createMenu()
|
||||||
}
|
}
|
||||||
@@ -95,8 +114,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
let startAtLogin = NSMenuItem(title: "Start at login", action: #selector(toggleStartAtLogin(_:)), keyEquivalent: "L")
|
let startAtLogin = NSMenuItem(title: "Start at login", action: #selector(toggleStartAtLogin(_:)), keyEquivalent: "L")
|
||||||
startAtLogin.state = LaunchAtLoginController().launchAtLogin ? .on : .off
|
startAtLogin.state = LaunchAtLoginController().launchAtLogin ? .on : .off
|
||||||
|
|
||||||
|
let toggleBlackList = NSMenuItem(title: "Toggle current app in blacklist", action: #selector(toggleBlackListedApp(_:)), keyEquivalent: "B")
|
||||||
|
toggleBlackList.state = isBlockedApp ? .on : .off
|
||||||
|
|
||||||
let hideControlStrip = NSMenuItem(title: "Hide Control Strip", action: #selector(toggleControlStrip(_:)), keyEquivalent: "T")
|
let hideControlStrip = NSMenuItem(title: "Hide Control Strip", action: #selector(toggleControlStrip(_:)), keyEquivalent: "T")
|
||||||
hideControlStrip.state = TouchBarController.shared.controlStripState ? .on : .off
|
hideControlStrip.state = TouchBarController.shared.showControlStripState ? .off : .on
|
||||||
|
|
||||||
let settingSeparator = NSMenuItem(title: "Settings", action: nil, keyEquivalent: "")
|
let settingSeparator = NSMenuItem(title: "Settings", action: nil, keyEquivalent: "")
|
||||||
settingSeparator.isEnabled = false
|
settingSeparator.isEnabled = false
|
||||||
@@ -108,7 +130,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
menu.addItem(settingSeparator)
|
menu.addItem(settingSeparator)
|
||||||
menu.addItem(hideControlStrip)
|
menu.addItem(hideControlStrip)
|
||||||
menu.addItem(withTitle: "Toggle current app in blacklist" , action: #selector(toggleBlackListedApp(_:)), keyEquivalent: "B")
|
menu.addItem(toggleBlackList)
|
||||||
menu.addItem(startAtLogin)
|
menu.addItem(startAtLogin)
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
|
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
|
||||||
@@ -120,20 +142,19 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
|
|
||||||
let fd = open(file.path, O_EVTONLY)
|
let fd = open(file.path, O_EVTONLY)
|
||||||
|
|
||||||
self.fileSystemSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fd, eventMask: .write, queue: DispatchQueue(label: "DefaultConfigChanged"))
|
fileSystemSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fd, eventMask: .write, queue: DispatchQueue(label: "DefaultConfigChanged"))
|
||||||
|
|
||||||
self.fileSystemSource?.setEventHandler(handler: {
|
fileSystemSource?.setEventHandler(handler: {
|
||||||
print("Config changed, reloading...")
|
print("Config changed, reloading...")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
TouchBarController.shared.reloadPreset(path: file.path)
|
TouchBarController.shared.reloadPreset(path: file.path)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
self.fileSystemSource?.setCancelHandler(handler: {
|
fileSystemSource?.setCancelHandler(handler: {
|
||||||
close(fd)
|
close(fd)
|
||||||
})
|
})
|
||||||
|
|
||||||
self.fileSystemSource?.resume()
|
fileSystemSource?.resume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
|||||||
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval) {
|
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval) {
|
||||||
self.interval = interval
|
self.interval = interval
|
||||||
super.init(identifier: identifier, title: "⏳")
|
super.init(identifier: identifier, title: "⏳")
|
||||||
self.forceHideConstraint = self.view.widthAnchor.constraint(equalToConstant: 0)
|
forceHideConstraint = view.widthAnchor.constraint(equalToConstant: 0)
|
||||||
guard let script = source.appleScript else {
|
guard let script = source.appleScript else {
|
||||||
self.title = "no script"
|
title = "no script"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.script = script
|
self.script = script
|
||||||
self.isBordered = false
|
isBordered = false
|
||||||
DispatchQueue.appleScriptQueue.async {
|
DispatchQueue.appleScriptQueue.async {
|
||||||
var error: NSDictionary?
|
var error: NSDictionary?
|
||||||
guard script.compileAndReturnError(&error) else {
|
guard script.compileAndReturnError(&error) else {
|
||||||
@@ -30,15 +30,15 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshAndSchedule() {
|
func refreshAndSchedule() {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
print("refresh happened (interval \(self.interval)), self \(self.identifier.rawValue))")
|
print("refresh happened (interval \(interval)), self \(identifier.rawValue))")
|
||||||
#endif
|
#endif
|
||||||
let scriptResult = self.execute()
|
let scriptResult = execute()
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.title = scriptResult
|
self.title = scriptResult
|
||||||
self.forceHideConstraint.isActive = scriptResult == ""
|
self.forceHideConstraint.isActive = scriptResult == ""
|
||||||
@@ -46,7 +46,7 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
|||||||
print("did set new script result title \(scriptResult)")
|
print("did set new script result title \(scriptResult)")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
DispatchQueue.appleScriptQueue.asyncAfter(deadline: .now() + self.interval) { [weak self] in
|
DispatchQueue.appleScriptQueue.asyncAfter(deadline: .now() + interval) { [weak self] in
|
||||||
self?.refreshAndSchedule()
|
self?.refreshAndSchedule()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,6 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
|||||||
}
|
}
|
||||||
return output.stringValue ?? ""
|
return output.stringValue ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension DispatchQueue {
|
extension DispatchQueue {
|
||||||
|
|||||||
@@ -9,15 +9,15 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
||||||
var tapClosure: (() -> ())?
|
var tapClosure: (() -> Void)?
|
||||||
var longTapClosure: (() -> ())?
|
var longTapClosure: (() -> Void)?
|
||||||
private var button: NSButton!
|
private var button: NSButton!
|
||||||
|
|
||||||
private var singleClick: NSClickGestureRecognizer!
|
private var singleClick: NSClickGestureRecognizer!
|
||||||
private var longClick: NSPressGestureRecognizer!
|
private var longClick: NSPressGestureRecognizer!
|
||||||
|
|
||||||
init(identifier: NSTouchBarItem.Identifier, title: String) {
|
init(identifier: NSTouchBarItem.Identifier, title: String) {
|
||||||
self.attributedTitle = title.defaultTouchbarAttributedString
|
attributedTitle = title.defaultTouchbarAttributedString
|
||||||
|
|
||||||
super.init(identifier: identifier)
|
super.init(identifier: identifier)
|
||||||
button = CustomHeightButton(title: title, target: nil, action: nil)
|
button = CustomHeightButton(title: title, target: nil, action: nil)
|
||||||
@@ -34,7 +34,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
|||||||
button.attributedTitle = attributedTitle
|
button.attributedTitle = attributedTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,17 +52,17 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
|||||||
|
|
||||||
var title: String {
|
var title: String {
|
||||||
get {
|
get {
|
||||||
return self.attributedTitle.string
|
return attributedTitle.string
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
self.attributedTitle = newValue.defaultTouchbarAttributedString
|
attributedTitle = newValue.defaultTouchbarAttributedString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var attributedTitle: NSAttributedString {
|
var attributedTitle: NSAttributedString {
|
||||||
didSet {
|
didSet {
|
||||||
self.button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
|
button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
|
||||||
self.button?.attributedTitle = attributedTitle
|
button?.attributedTitle = attributedTitle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,12 +88,12 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
|||||||
button.imageScaling = .scaleProportionallyDown
|
button.imageScaling = .scaleProportionallyDown
|
||||||
button.imageHugsTitle = true
|
button.imageHugsTitle = true
|
||||||
button.attributedTitle = title
|
button.attributedTitle = title
|
||||||
self.button?.imagePosition = title.length > 0 ? .imageLeading : .imageOnly
|
button?.imagePosition = title.length > 0 ? .imageLeading : .imageOnly
|
||||||
button.image = image
|
button.image = image
|
||||||
self.view = button
|
view = button
|
||||||
|
|
||||||
self.view.addGestureRecognizer(longClick)
|
view.addGestureRecognizer(longClick)
|
||||||
self.view.addGestureRecognizer(singleClick)
|
view.addGestureRecognizer(singleClick)
|
||||||
}
|
}
|
||||||
|
|
||||||
func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool {
|
func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool {
|
||||||
@@ -108,7 +108,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
|||||||
switch gr.state {
|
switch gr.state {
|
||||||
case .ended:
|
case .ended:
|
||||||
hf.tap(strong: 2)
|
hf.tap(strong: 2)
|
||||||
self.tapClosure?()
|
tapClosure?()
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@@ -130,19 +130,16 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
|||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomHeightButton : NSButton {
|
class CustomHeightButton: NSButton {
|
||||||
|
|
||||||
override var intrinsicContentSize: NSSize {
|
override var intrinsicContentSize: NSSize {
|
||||||
var size = super.intrinsicContentSize
|
var size = super.intrinsicContentSize
|
||||||
size.height = 30
|
size.height = 30
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomButtonCell: NSButtonCell {
|
class CustomButtonCell: NSButtonCell {
|
||||||
@@ -155,32 +152,30 @@ class CustomButtonCell: NSButtonCell {
|
|||||||
|
|
||||||
override func highlight(_ flag: Bool, withFrame cellFrame: NSRect, in controlView: NSView) {
|
override func highlight(_ flag: Bool, withFrame cellFrame: NSRect, in controlView: NSView) {
|
||||||
super.highlight(flag, withFrame: cellFrame, in: controlView)
|
super.highlight(flag, withFrame: cellFrame, in: controlView)
|
||||||
if !self.isBordered {
|
if !isBordered {
|
||||||
if flag {
|
if flag {
|
||||||
self.setAttributedTitle(self.attributedTitle, withColor: .lightGray)
|
setAttributedTitle(attributedTitle, withColor: .lightGray)
|
||||||
} else if let parentItem = self.parentItem {
|
} else if let parentItem = self.parentItem {
|
||||||
self.attributedTitle = parentItem.attributedTitle
|
attributedTitle = parentItem.attributedTitle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init(coder: NSCoder) {
|
required init(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAttributedTitle(_ title: NSAttributedString, withColor color: NSColor) {
|
func setAttributedTitle(_ title: NSAttributedString, withColor color: NSColor) {
|
||||||
let attrTitle = NSMutableAttributedString(attributedString: title)
|
let attrTitle = NSMutableAttributedString(attributedString: title)
|
||||||
attrTitle.addAttributes([.foregroundColor: color], range: NSRange(location: 0, length: attrTitle.length))
|
attrTitle.addAttributes([.foregroundColor: color], range: NSRange(location: 0, length: attrTitle.length))
|
||||||
self.attributedTitle = attrTitle
|
attributedTitle = attrTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
var defaultTouchbarAttributedString: NSAttributedString {
|
var defaultTouchbarAttributedString: NSAttributedString {
|
||||||
let attrTitle = NSMutableAttributedString(string: self, attributes: [.foregroundColor: NSColor.white, .font: NSFont.systemFont(ofSize: 15, weight: .regular), .baselineOffset: 1])
|
let attrTitle = NSMutableAttributedString(string: self, attributes: [.foregroundColor: NSColor.white, .font: NSFont.systemFont(ofSize: 15, weight: .regular), .baselineOffset: 1])
|
||||||
attrTitle.setAlignment(.center, range: NSRange(location: 0, length: self.count))
|
attrTitle.setAlignment(.center, range: NSRange(location: 0, length: count))
|
||||||
return attrTitle
|
return attrTitle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-20
@@ -9,9 +9,9 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class CustomSliderCell: NSSliderCell {
|
class CustomSliderCell: NSSliderCell {
|
||||||
var knobImage:NSImage!
|
var knobImage: NSImage!
|
||||||
private var _currentKnobRect:NSRect!
|
private var _currentKnobRect: NSRect!
|
||||||
private var _barRect:NSRect!
|
private var _barRect: NSRect!
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
required init(coder aDecoder: NSCoder) {
|
||||||
super.init(coder: aDecoder)
|
super.init(coder: aDecoder)
|
||||||
@@ -21,22 +21,22 @@ class CustomSliderCell: NSSliderCell {
|
|||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(knob:NSImage?) {
|
init(knob: NSImage?) {
|
||||||
knobImage = knob;
|
knobImage = knob
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func drawKnob(_ knobRect: NSRect) {
|
override func drawKnob(_ knobRect: NSRect) {
|
||||||
if (knobImage == nil) {
|
if knobImage == nil {
|
||||||
super.drawKnob(knobRect)
|
super.drawKnob(knobRect)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentKnobRect = knobRect;
|
_currentKnobRect = knobRect
|
||||||
drawBar(inside: _barRect, flipped: true)
|
drawBar(inside: _barRect, flipped: true)
|
||||||
|
|
||||||
let x = (knobRect.origin.x * (_barRect.size.width - (knobImage.size.width - knobRect.size.width)) / _barRect.size.width)+1;
|
let x = (knobRect.origin.x * (_barRect.size.width - (knobImage.size.width - knobRect.size.width)) / _barRect.size.width) + 1
|
||||||
let y = knobRect.origin.y+3
|
let y = knobRect.origin.y + 3
|
||||||
|
|
||||||
knobImage.draw(
|
knobImage.draw(
|
||||||
at: NSPoint(x: x, y: y),
|
at: NSPoint(x: x, y: y),
|
||||||
@@ -46,7 +46,7 @@ class CustomSliderCell: NSSliderCell {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func drawBar(inside aRect: NSRect, flipped: Bool) {
|
override func drawBar(inside aRect: NSRect, flipped _: Bool) {
|
||||||
_barRect = aRect
|
_barRect = aRect
|
||||||
|
|
||||||
let barRadius = CGFloat(2)
|
let barRadius = CGFloat(2)
|
||||||
@@ -60,16 +60,15 @@ class CustomSliderCell: NSSliderCell {
|
|||||||
|
|
||||||
var activeRect = bgRect
|
var activeRect = bgRect
|
||||||
|
|
||||||
activeRect.size.width = CGFloat((Double(bgRect.size.width) / (self.maxValue - self.minValue)) * self.doubleValue)
|
activeRect.size.width = CGFloat((Double(bgRect.size.width) / (maxValue - minValue)) * doubleValue)
|
||||||
let active = NSBezierPath(roundedRect: activeRect, xRadius: barRadius, yRadius: barRadius)
|
let active = NSBezierPath(roundedRect: activeRect, xRadius: barRadius, yRadius: barRadius)
|
||||||
NSColor.darkGray.setFill()
|
NSColor.darkGray.setFill()
|
||||||
active.fill()
|
active.fill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomSlider:NSSlider {
|
class CustomSlider: NSSlider {
|
||||||
|
var currentValue: CGFloat = 0
|
||||||
var currentValue:CGFloat = 0
|
|
||||||
|
|
||||||
override func setNeedsDisplay(_ invalidRect: NSRect) {
|
override func setNeedsDisplay(_ invalidRect: NSRect) {
|
||||||
super.setNeedsDisplay(invalidRect)
|
super.setNeedsDisplay(invalidRect)
|
||||||
@@ -77,15 +76,15 @@ class CustomSlider:NSSlider {
|
|||||||
|
|
||||||
override func awakeFromNib() {
|
override func awakeFromNib() {
|
||||||
super.awakeFromNib()
|
super.awakeFromNib()
|
||||||
if ((self.cell?.isKind(of: CustomSliderCell.self)) == false) {
|
if (cell?.isKind(of: CustomSliderCell.self)) == false {
|
||||||
let cell:CustomSliderCell = CustomSliderCell()
|
let cell: CustomSliderCell = CustomSliderCell()
|
||||||
self.cell = cell
|
self.cell = cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
convenience init(knob:NSImage) {
|
convenience init(knob: NSImage) {
|
||||||
self.init()
|
self.init()
|
||||||
self.cell = CustomSliderCell(knob: knob)
|
cell = CustomSliderCell(knob: knob)
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@@ -101,7 +100,7 @@ class CustomSlider:NSSlider {
|
|||||||
return cell.knobImage
|
return cell.knobImage
|
||||||
}
|
}
|
||||||
|
|
||||||
func setKnobImage(image:NSImage) {
|
func setKnobImage(image: NSImage) {
|
||||||
let cell = self.cell as! CustomSliderCell
|
let cell = self.cell as! CustomSliderCell
|
||||||
cell.knobImage = image
|
cell.knobImage = image
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ import Foundation
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
|
|
||||||
var ifNotEmpty: String? {
|
var ifNotEmpty: String? {
|
||||||
return self.count > 0 ? self : nil
|
return count > 0 ? self : nil
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import IOKit
|
|||||||
|
|
||||||
class HapticFeedback {
|
class HapticFeedback {
|
||||||
private var actuatorRef: CFTypeRef?
|
private var actuatorRef: CFTypeRef?
|
||||||
private var deviceID: UInt64 = 0x200000001000000
|
private var deviceID: UInt64 = 0x200_0000_0100_0000
|
||||||
private var error: IOReturn = 0
|
private var error: IOReturn = 0
|
||||||
|
|
||||||
// Don't know how to do strong is enum one of
|
// Don't know how to do strong is enum one of
|
||||||
@@ -24,7 +24,7 @@ class HapticFeedback {
|
|||||||
// 16 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`
|
// 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 {
|
func tap(strong: Int32) {
|
||||||
actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue()
|
actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue()
|
||||||
|
|
||||||
guard actuatorRef != nil else {
|
guard actuatorRef != nil else {
|
||||||
@@ -53,5 +53,3 @@ class HapticFeedback {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -17,9 +17,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.18.4</string>
|
<string>0.19.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>35</string>
|
<string>125</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.utilities</string>
|
<string>public.app-category.utilities</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
|||||||
+180
-68
@@ -1,9 +1,9 @@
|
|||||||
import Foundation
|
|
||||||
import AppKit
|
import AppKit
|
||||||
|
import Foundation
|
||||||
|
|
||||||
extension Data {
|
extension Data {
|
||||||
func barItemDefinitions() -> [BarItemDefinition]? {
|
func barItemDefinitions() -> [BarItemDefinition]? {
|
||||||
return try? JSONDecoder().decode([BarItemDefinition].self, from: self.utf8string!.stripComments().data(using: .utf8)!)
|
return try? JSONDecoder().decode([BarItemDefinition].self, from: utf8string!.stripComments().data(using: .utf8)!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ struct BarItemDefinition: Decodable {
|
|||||||
case type
|
case type
|
||||||
}
|
}
|
||||||
|
|
||||||
init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) {
|
init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]) {
|
||||||
self.type = type
|
self.type = type
|
||||||
self.action = action
|
self.action = action
|
||||||
self.longAction = longAction
|
self.longAction = longAction
|
||||||
@@ -38,68 +38,133 @@ struct BarItemDefinition: Decodable {
|
|||||||
self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, additionalParameters: additionalParameters)
|
self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, additionalParameters: additionalParameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SupportedTypesHolder {
|
typealias ParametersDecoder = (Decoder) throws -> (
|
||||||
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, longAction: LongActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter])
|
item: ItemType,
|
||||||
private var supportedTypes: [String: ParametersDecoder] = [
|
action: ActionType,
|
||||||
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), longAction: .none, parameters: [.align: .align(.left)]) },
|
longAction: LongActionType,
|
||||||
|
parameters: [GeneralParameters.CodingKeys: GeneralParameter]
|
||||||
|
)
|
||||||
|
|
||||||
"delete": { _ in return (item: .staticButton(title: "del"), action: .keyPress(keycode: 117), longAction: .none, parameters: [:])},
|
class SupportedTypesHolder {
|
||||||
|
private var supportedTypes: [String: ParametersDecoder] = [
|
||||||
|
"escape": { _ in (
|
||||||
|
item: .staticButton(title: "esc"),
|
||||||
|
action: .keyPress(keycode: 53),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.align: .align(.left)]
|
||||||
|
) },
|
||||||
|
|
||||||
|
"delete": { _ in (
|
||||||
|
item: .staticButton(title: "del"),
|
||||||
|
action: .keyPress(keycode: 117),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
) },
|
||||||
|
|
||||||
"brightnessUp": { _ in
|
"brightnessUp": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
|
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
|
||||||
return (item: .staticButton(title: ""), action: .keyPress(keycode: 144), longAction: .none, parameters: [.image: imageParameter])
|
return (
|
||||||
|
item: .staticButton(title: ""),
|
||||||
|
action: .keyPress(keycode: 144),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: imageParameter]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"brightnessDown": { _ in
|
"brightnessDown": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
|
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
|
||||||
return (item: .staticButton(title: ""), action: .keyPress(keycode: 145), longAction: .none, parameters: [.image: imageParameter])
|
return (
|
||||||
|
item: .staticButton(title: ""),
|
||||||
|
action: .keyPress(keycode: 145),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: imageParameter]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"illuminationUp": { _ in
|
"illuminationUp": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_up"))
|
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_up"))
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_UP), longAction: .none, parameters: [.image: imageParameter])
|
return (
|
||||||
|
item: .staticButton(title: ""),
|
||||||
|
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_UP),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: imageParameter]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"illuminationDown": { _ in
|
"illuminationDown": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_down"))
|
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_down"))
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_DOWN), longAction: .none, parameters: [.image: imageParameter])
|
return (
|
||||||
|
item: .staticButton(title: ""),
|
||||||
|
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_DOWN),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: imageParameter]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"volumeDown": { _ in
|
"volumeDown": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeDownTemplateName)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeDownTemplateName)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), longAction: .none, parameters: [.image: imageParameter])
|
return (
|
||||||
|
item: .staticButton(title: ""),
|
||||||
|
action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: imageParameter]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"volumeUp": { _ in
|
"volumeUp": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeUpTemplateName)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeUpTemplateName)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), longAction: .none, parameters: [.image: imageParameter])
|
return (
|
||||||
|
item: .staticButton(title: ""),
|
||||||
|
action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: imageParameter]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"mute": { _ in
|
"mute": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarAudioOutputMuteTemplateName)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarAudioOutputMuteTemplateName)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), longAction: .none, parameters: [.image: imageParameter])
|
return (
|
||||||
|
item: .staticButton(title: ""),
|
||||||
|
action: .hidKey(keycode: NX_KEYTYPE_MUTE),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: imageParameter]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"previous": { _ in
|
"previous": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarRewindTemplateName)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarRewindTemplateName)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), longAction: .none, parameters: [.image: imageParameter])
|
return (
|
||||||
|
item: .staticButton(title: ""),
|
||||||
|
action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: imageParameter]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"play": { _ in
|
"play": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarPlayPauseTemplateName)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarPlayPauseTemplateName)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), longAction: .none, parameters: [.image: imageParameter])
|
return (
|
||||||
|
item: .staticButton(title: ""),
|
||||||
|
action: .hidKey(keycode: NX_KEYTYPE_PLAY),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: imageParameter]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"next": { _ in
|
"next": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarFastForwardTemplateName)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarFastForwardTemplateName)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), longAction: .none, parameters: [.image: imageParameter])
|
return (
|
||||||
|
item: .staticButton(title: ""),
|
||||||
|
action: .hidKey(keycode: NX_KEYTYPE_NEXT),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: imageParameter]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"weather": { decoder in
|
"weather": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key ; case icon_type }
|
enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key; case icon_type }
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||||
let units = try container.decodeIfPresent(String.self, forKey: .units)
|
let units = try container.decodeIfPresent(String.self, forKey: .units)
|
||||||
@@ -107,35 +172,66 @@ class SupportedTypesHolder {
|
|||||||
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type)
|
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type)
|
||||||
let action = try ActionType(from: decoder)
|
let action = try ActionType(from: decoder)
|
||||||
let longAction = try LongActionType(from: decoder)
|
let longAction = try LongActionType(from: decoder)
|
||||||
return (item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"), action: action, longAction: longAction, parameters: [:])
|
return (
|
||||||
|
item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"),
|
||||||
|
action,
|
||||||
|
longAction,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"currency": { decoder in
|
"currency": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to }
|
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to; case full }
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||||
let from = try container.decodeIfPresent(String.self, forKey: .from)
|
let from = try container.decodeIfPresent(String.self, forKey: .from)
|
||||||
let to = try container.decodeIfPresent(String.self, forKey: .to)
|
let to = try container.decodeIfPresent(String.self, forKey: .to)
|
||||||
|
let full = try container.decodeIfPresent(Bool.self, forKey: .full)
|
||||||
let action = try ActionType(from: decoder)
|
let action = try ActionType(from: decoder)
|
||||||
let longAction = try LongActionType(from: decoder)
|
let longAction = try LongActionType(from: decoder)
|
||||||
return (item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), action: action, longAction: longAction, parameters: [:])
|
return (
|
||||||
|
item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD", full: full ?? false),
|
||||||
|
action,
|
||||||
|
longAction,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"dock": { decoder in
|
"dock": { _ in
|
||||||
return (item: .dock(), action: .none, longAction: .none, parameters: [:])
|
(
|
||||||
|
item: .dock(),
|
||||||
|
action: .none,
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"inputsource": { decoder in
|
"inputsource": { _ in
|
||||||
return (item: .inputsource(), action: .none, longAction: .none, parameters: [:])
|
(
|
||||||
|
item: .inputsource(),
|
||||||
|
action: .none,
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"volume": { decoder in
|
"volume": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case image }
|
enum CodingKeys: String, CodingKey { case image }
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
if var img = try container.decodeIfPresent(Source.self, forKey: .image) {
|
if var img = try container.decodeIfPresent(Source.self, forKey: .image) {
|
||||||
return (item: .volume(), action: .none, longAction: .none, parameters: [.image: .image(source: img)])
|
return (
|
||||||
|
item: .volume(),
|
||||||
|
action: .none,
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: .image(source: img)]
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return (item: .volume(), action: .none, longAction: .none, parameters: [:])
|
return (
|
||||||
|
item: .volume(),
|
||||||
|
action: .none,
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -144,15 +240,35 @@ class SupportedTypesHolder {
|
|||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||||
if var img = try container.decodeIfPresent(Source.self, forKey: .image) {
|
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)])
|
return (
|
||||||
|
item: .brightness(refreshInterval: interval ?? 0.5),
|
||||||
|
action: .none,
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [.image: .image(source: img)]
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [:])
|
return (
|
||||||
|
item: .brightness(refreshInterval: interval ?? 0.5),
|
||||||
|
action: .none,
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), longAction: .none, parameters: [:]) },
|
"sleep": { _ in (
|
||||||
|
item: .staticButton(title: "☕️"),
|
||||||
|
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
) },
|
||||||
|
|
||||||
"displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), longAction: .none, parameters: [:])},
|
"displaySleep": { _ in (
|
||||||
|
item: .staticButton(title: "☕️"),
|
||||||
|
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]),
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
) },
|
||||||
|
|
||||||
"music": { decoder in
|
"music": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval }
|
enum CodingKeys: String, CodingKey { case refreshInterval }
|
||||||
@@ -177,33 +293,17 @@ class SupportedTypesHolder {
|
|||||||
parameters: [:]
|
parameters: [:]
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
"nightShift": {_ in
|
|
||||||
return (
|
|
||||||
item: .nightShift(),
|
|
||||||
action: .none,
|
|
||||||
longAction: .none,
|
|
||||||
parameters: [:]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
"dnd": { _ in
|
|
||||||
return (
|
|
||||||
item: .dnd(),
|
|
||||||
action: .none,
|
|
||||||
longAction: .none,
|
|
||||||
parameters: [:]
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
|
||||||
static let sharedInstance = SupportedTypesHolder()
|
static let sharedInstance = SupportedTypesHolder()
|
||||||
|
|
||||||
func lookup(by type: String) -> ParametersDecoder {
|
func lookup(by type: String) -> ParametersDecoder {
|
||||||
return supportedTypes[type] ?? { decoder in
|
return supportedTypes[type] ?? { decoder in (
|
||||||
return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), longAction: try LongActionType(from: decoder), parameters: [:])
|
item: try ItemType(from: decoder),
|
||||||
}
|
action: try ActionType(from: decoder),
|
||||||
|
longAction: try LongActionType(from: decoder),
|
||||||
|
parameters: [:]
|
||||||
|
)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func register(typename: String, decoder: @escaping ParametersDecoder) {
|
func register(typename: String, decoder: @escaping ParametersDecoder) {
|
||||||
@@ -212,7 +312,12 @@ class SupportedTypesHolder {
|
|||||||
|
|
||||||
func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) {
|
func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) {
|
||||||
register(typename: typename) { _ in
|
register(typename: typename) { _ in
|
||||||
return (item: item, action: action, longAction: longAction, parameters: [:])
|
(
|
||||||
|
item: item,
|
||||||
|
action,
|
||||||
|
longAction,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,12 +331,13 @@ enum ItemType: Decodable {
|
|||||||
case volume()
|
case volume()
|
||||||
case brightness(refreshInterval: Double)
|
case brightness(refreshInterval: Double)
|
||||||
case weather(interval: Double, units: String, api_key: String, icon_type: String)
|
case weather(interval: Double, units: String, api_key: String, icon_type: String)
|
||||||
case currency(interval: Double, from: String, to: String)
|
case currency(interval: Double, from: String, to: String, full: Bool)
|
||||||
case inputsource()
|
case inputsource()
|
||||||
case music(interval: Double)
|
case music(interval: Double)
|
||||||
case groupBar(items: [BarItemDefinition])
|
case groupBar(items: [BarItemDefinition])
|
||||||
case nightShift()
|
case nightShift()
|
||||||
case dnd()
|
case dnd()
|
||||||
|
case pomodoro(workTime: Double, restTime: Double)
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case type
|
case type
|
||||||
@@ -240,6 +346,7 @@ enum ItemType: Decodable {
|
|||||||
case refreshInterval
|
case refreshInterval
|
||||||
case from
|
case from
|
||||||
case to
|
case to
|
||||||
|
case full
|
||||||
case units
|
case units
|
||||||
case api_key
|
case api_key
|
||||||
case icon_type
|
case icon_type
|
||||||
@@ -248,6 +355,8 @@ enum ItemType: Decodable {
|
|||||||
case url
|
case url
|
||||||
case longUrl
|
case longUrl
|
||||||
case items
|
case items
|
||||||
|
case workTime
|
||||||
|
case restTime
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ItemTypeRaw: String, Decodable {
|
enum ItemTypeRaw: String, Decodable {
|
||||||
@@ -265,13 +374,13 @@ enum ItemType: Decodable {
|
|||||||
case groupBar
|
case groupBar
|
||||||
case nightShift
|
case nightShift
|
||||||
case dnd
|
case dnd
|
||||||
|
case pomodoro
|
||||||
}
|
}
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
let type = try container.decode(ItemTypeRaw.self, forKey: .type)
|
let type = try container.decode(ItemTypeRaw.self, forKey: .type)
|
||||||
switch type {
|
switch type {
|
||||||
|
|
||||||
case .appleScriptTitledButton:
|
case .appleScriptTitledButton:
|
||||||
let source = try container.decode(Source.self, forKey: .source)
|
let source = try container.decode(Source.self, forKey: .source)
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||||
@@ -309,7 +418,8 @@ enum ItemType: Decodable {
|
|||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
|
||||||
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
|
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
|
||||||
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
|
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
|
||||||
self = .currency(interval: interval, from: from, to: to)
|
let full = try container.decodeIfPresent(Bool.self, forKey: .full) ?? false
|
||||||
|
self = .currency(interval: interval, from: from, to: to, full: full)
|
||||||
|
|
||||||
case .inputsource:
|
case .inputsource:
|
||||||
self = .inputsource()
|
self = .inputsource()
|
||||||
@@ -327,6 +437,11 @@ enum ItemType: Decodable {
|
|||||||
|
|
||||||
case .dnd:
|
case .dnd:
|
||||||
self = .dnd()
|
self = .dnd()
|
||||||
|
|
||||||
|
case .pomodoro:
|
||||||
|
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime) ?? 1500.0
|
||||||
|
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime) ?? 600.0
|
||||||
|
self = .pomodoro(workTime: workTime, restTime: restTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,7 +452,7 @@ enum ActionType: Decodable {
|
|||||||
case keyPress(keycode: Int)
|
case keyPress(keycode: Int)
|
||||||
case appleScript(source: SourceProtocol)
|
case appleScript(source: SourceProtocol)
|
||||||
case shellScript(executable: String, parameters: [String])
|
case shellScript(executable: String, parameters: [String])
|
||||||
case custom(closure: ()->())
|
case custom(closure: () -> Void)
|
||||||
case openUrl(url: String)
|
case openUrl(url: String)
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
@@ -389,14 +504,13 @@ enum ActionType: Decodable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum LongActionType: Decodable {
|
enum LongActionType: Decodable {
|
||||||
case none
|
case none
|
||||||
case hidKey(keycode: Int32)
|
case hidKey(keycode: Int32)
|
||||||
case keyPress(keycode: Int)
|
case keyPress(keycode: Int)
|
||||||
case appleScript(source: SourceProtocol)
|
case appleScript(source: SourceProtocol)
|
||||||
case shellScript(executable: String, parameters: [String])
|
case shellScript(executable: String, parameters: [String])
|
||||||
case custom(closure: ()->())
|
case custom(closure: () -> Void)
|
||||||
case openUrl(url: String)
|
case openUrl(url: String)
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
@@ -448,14 +562,13 @@ enum LongActionType: Decodable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum GeneralParameter {
|
enum GeneralParameter {
|
||||||
case width(_: CGFloat)
|
case width(_: CGFloat)
|
||||||
case image(source: SourceProtocol)
|
case image(source: SourceProtocol)
|
||||||
case align(_: Align)
|
case align(_: Align)
|
||||||
case bordered(_: Bool)
|
case bordered(_: Bool)
|
||||||
case background(_:NSColor)
|
case background(_: NSColor)
|
||||||
case title(_:String)
|
case title(_: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GeneralParameters: Decodable {
|
struct GeneralParameters: Decodable {
|
||||||
@@ -532,7 +645,7 @@ struct Source: Decodable, SourceProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var appleScript: NSAppleScript? {
|
var appleScript: NSAppleScript? {
|
||||||
return filePath?.fileURL.appleScript ?? self.string?.appleScript
|
return filePath?.fileURL.appleScript ?? string?.appleScript
|
||||||
}
|
}
|
||||||
|
|
||||||
private init(filePath: String?, base64: String?, inline: String?) {
|
private init(filePath: String?, base64: String?, inline: String?) {
|
||||||
@@ -577,7 +690,6 @@ extension String {
|
|||||||
var encoding: String.Encoding = .utf8
|
var encoding: String.Encoding = .utf8
|
||||||
return try? String(contentsOfFile: self, usedEncoding: &encoding)
|
return try? String(contentsOfFile: self, usedEncoding: &encoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Data {
|
extension Data {
|
||||||
@@ -608,7 +720,7 @@ extension String {
|
|||||||
|
|
||||||
extension URL {
|
extension URL {
|
||||||
var appleScript: NSAppleScript? {
|
var appleScript: NSAppleScript? {
|
||||||
guard FileManager.default.fileExists(atPath: self.path) else { return nil }
|
guard FileManager.default.fileExists(atPath: path) else { return nil }
|
||||||
return NSAppleScript(contentsOf: self, error: nil)
|
return NSAppleScript(contentsOf: self, error: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ struct GenericKeyPress: KeyPress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension KeyPress {
|
extension KeyPress {
|
||||||
func send () {
|
func send() {
|
||||||
let src = CGEventSource(stateID: .hidSystemState)
|
let src = CGEventSource(stateID: .hidSystemState)
|
||||||
let keyDown = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: true)
|
let keyDown = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: true)
|
||||||
let keyUp = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: false)
|
let keyUp = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: false)
|
||||||
|
|||||||
@@ -12,20 +12,20 @@ class ScrollViewItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
|||||||
stackView.orientation = .horizontal
|
stackView.orientation = .horizontal
|
||||||
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
|
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
|
||||||
scrollView.documentView = stackView
|
scrollView.documentView = stackView
|
||||||
self.view = scrollView
|
view = scrollView
|
||||||
|
|
||||||
let twofingers = NSPanGestureRecognizer(target: self, action: #selector(twofingersHandler(_:)))
|
let twofingers = NSPanGestureRecognizer(target: self, action: #selector(twofingersHandler(_:)))
|
||||||
twofingers.allowedTouchTypes = .direct
|
twofingers.allowedTouchTypes = .direct
|
||||||
twofingers.numberOfTouchesRequired = 2
|
twofingers.numberOfTouchesRequired = 2
|
||||||
self.view.addGestureRecognizer(twofingers)
|
view.addGestureRecognizer(twofingers)
|
||||||
|
|
||||||
let threefingers = NSPanGestureRecognizer(target: self, action: #selector(threefingersHandler(_:)))
|
let threefingers = NSPanGestureRecognizer(target: self, action: #selector(threefingersHandler(_:)))
|
||||||
threefingers.allowedTouchTypes = .direct
|
threefingers.allowedTouchTypes = .direct
|
||||||
threefingers.numberOfTouchesRequired = 3
|
threefingers.numberOfTouchesRequired = 3
|
||||||
self.view.addGestureRecognizer(threefingers)
|
view.addGestureRecognizer(threefingers)
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ class ScrollViewItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
|||||||
case .began:
|
case .began:
|
||||||
twofingersPrev = position
|
twofingersPrev = position
|
||||||
case .changed:
|
case .changed:
|
||||||
if (((position-twofingersPrev) > 10) || ((twofingersPrev-position) > 10)) {
|
if ((position - twofingersPrev) > 10) || ((twofingersPrev - position) > 10) {
|
||||||
if position > twofingersPrev {
|
if position > twofingersPrev {
|
||||||
HIDPostAuxKey(NX_KEYTYPE_SOUND_UP)
|
HIDPostAuxKey(NX_KEYTYPE_SOUND_UP)
|
||||||
} else if position < twofingersPrev {
|
} else if position < twofingersPrev {
|
||||||
@@ -58,7 +58,7 @@ class ScrollViewItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
|||||||
case .began:
|
case .began:
|
||||||
threefingersPrev = position
|
threefingersPrev = position
|
||||||
case .changed:
|
case .changed:
|
||||||
if (((position-threefingersPrev) > 15) || ((threefingersPrev-position) > 15)) {
|
if ((position - threefingersPrev) > 15) || ((threefingersPrev - position) > 15) {
|
||||||
if position > threefingersPrev {
|
if position > threefingersPrev {
|
||||||
GenericKeyPress(keyCode: CGKeyCode(144)).send()
|
GenericKeyPress(keyCode: CGKeyCode(144)).send()
|
||||||
} else if position < threefingersPrev {
|
} else if position < threefingersPrev {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
func presentSystemModal(_ touchBar: NSTouchBar!, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) -> Void {
|
func presentSystemModal(_ touchBar: NSTouchBar!, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) {
|
||||||
if #available(OSX 10.14, *) {
|
if #available(OSX 10.14, *) {
|
||||||
NSTouchBar.presentSystemModalTouchBar(touchBar, systemTrayItemIdentifier: identifier)
|
NSTouchBar.presentSystemModalTouchBar(touchBar, systemTrayItemIdentifier: identifier)
|
||||||
} else {
|
} else {
|
||||||
@@ -14,7 +14,7 @@ func presentSystemModal(_ touchBar: NSTouchBar!, systemTrayItemIdentifier identi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentSystemModal(_ touchBar: NSTouchBar!, placement: Int64, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) -> Void {
|
func presentSystemModal(_ touchBar: NSTouchBar!, placement: Int64, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) {
|
||||||
if #available(OSX 10.14, *) {
|
if #available(OSX 10.14, *) {
|
||||||
NSTouchBar.presentSystemModalTouchBar(touchBar, placement: placement, systemTrayItemIdentifier: identifier)
|
NSTouchBar.presentSystemModalTouchBar(touchBar, placement: placement, systemTrayItemIdentifier: identifier)
|
||||||
} else {
|
} else {
|
||||||
@@ -22,7 +22,7 @@ func presentSystemModal(_ touchBar: NSTouchBar!, placement: Int64, systemTrayIte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func minimizeSystemModal(_ touchBar: NSTouchBar!) -> Void {
|
func minimizeSystemModal(_ touchBar: NSTouchBar!) {
|
||||||
if #available(OSX 10.14, *) {
|
if #available(OSX 10.14, *) {
|
||||||
NSTouchBar.minimizeSystemModalTouchBar(touchBar)
|
NSTouchBar.minimizeSystemModalTouchBar(touchBar)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ extension ItemType {
|
|||||||
return "com.toxblh.mtmr.brightness"
|
return "com.toxblh.mtmr.brightness"
|
||||||
case .weather(interval: _, units: _, api_key: _, icon_type: _):
|
case .weather(interval: _, units: _, api_key: _, icon_type: _):
|
||||||
return "com.toxblh.mtmr.weather"
|
return "com.toxblh.mtmr.weather"
|
||||||
case .currency(interval: _, from: _, to: _):
|
case .currency(interval: _, from: _, to: _, full: _):
|
||||||
return "com.toxblh.mtmr.currency"
|
return "com.toxblh.mtmr.currency"
|
||||||
case .inputsource():
|
case .inputsource():
|
||||||
return "com.toxblh.mtmr.inputsource."
|
return "com.toxblh.mtmr.inputsource."
|
||||||
@@ -48,6 +48,8 @@ extension ItemType {
|
|||||||
return "com.toxblh.mtmr.nightShift."
|
return "com.toxblh.mtmr.nightShift."
|
||||||
case .dnd(items: _):
|
case .dnd(items: _):
|
||||||
return "com.toxblh.mtmr.dnd."
|
return "com.toxblh.mtmr.dnd."
|
||||||
|
case .pomodoro(interval: _):
|
||||||
|
return PomodoroBarItem.identifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,17 +76,15 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
var scrollArea: NSCustomTouchBarItem?
|
var scrollArea: NSCustomTouchBarItem?
|
||||||
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
||||||
|
|
||||||
var controlStripState: Bool {
|
var showControlStripState: Bool {
|
||||||
get {
|
get {
|
||||||
return UserDefaults.standard.bool(forKey: "com.toxblh.mtmr.settings.showControlStrip")
|
return UserDefaults.standard.bool(forKey: "com.toxblh.mtmr.settings.showControlStrip")
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
UserDefaults.standard.set(!controlStripState, forKey: "com.toxblh.mtmr.settings.showControlStrip")
|
UserDefaults.standard.set(newValue, forKey: "com.toxblh.mtmr.settings.showControlStrip")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var touchbarNeedRefresh: Bool = true
|
|
||||||
|
|
||||||
var blacklistAppIdentifiers: [String] = []
|
var blacklistAppIdentifiers: [String] = []
|
||||||
var frontmostApplicationIdentifier: String? {
|
var frontmostApplicationIdentifier: String? {
|
||||||
get {
|
get {
|
||||||
@@ -150,11 +150,9 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
|
|
||||||
func updateActiveApp() {
|
func updateActiveApp() {
|
||||||
if self.blacklistAppIdentifiers.index(of: self.frontmostApplicationIdentifier!) != nil {
|
if self.blacklistAppIdentifiers.index(of: self.frontmostApplicationIdentifier!) != nil {
|
||||||
DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, false)
|
dismissTouchBar()
|
||||||
self.touchbarNeedRefresh = true
|
|
||||||
} else {
|
} else {
|
||||||
presentTouchBar()
|
presentTouchBar()
|
||||||
self.touchbarNeedRefresh = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +170,6 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
func reloadPreset(path: String) {
|
func reloadPreset(path: String) {
|
||||||
lastPresetPath = path
|
lastPresetPath = path
|
||||||
let items = path.fileData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
|
let items = path.fileData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
|
||||||
touchbarNeedRefresh = true
|
|
||||||
createAndUpdatePreset(newJsonItems: items)
|
createAndUpdatePreset(newJsonItems: items)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +204,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
let item = NSCustomTouchBarItem(identifier: .controlStripItem)
|
let item = NSCustomTouchBarItem(identifier: .controlStripItem)
|
||||||
item.view = NSButton(image: #imageLiteral(resourceName: "StatusImage"), target: self, action: #selector(presentTouchBar))
|
item.view = NSButton(image: #imageLiteral(resourceName: "StatusImage"), target: self, action: #selector(presentTouchBar))
|
||||||
NSTouchBarItem.addSystemTrayItem(item)
|
NSTouchBarItem.addSystemTrayItem(item)
|
||||||
DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, true)
|
updateControlStripPresence()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateControlStripPresence() {
|
func updateControlStripPresence() {
|
||||||
@@ -215,18 +212,17 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func presentTouchBar() {
|
@objc private func presentTouchBar() {
|
||||||
if touchbarNeedRefresh {
|
if self.showControlStripState {
|
||||||
if self.controlStripState {
|
updateControlStripPresence()
|
||||||
presentSystemModal(touchBar, systemTrayItemIdentifier: .controlStripItem)
|
presentSystemModal(touchBar, systemTrayItemIdentifier: .controlStripItem)
|
||||||
} else {
|
} else {
|
||||||
presentSystemModal(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
|
presentSystemModal(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func dismissTouchBar() {
|
@objc private func dismissTouchBar() {
|
||||||
self.touchbarNeedRefresh = true
|
|
||||||
minimizeSystemModal(touchBar)
|
minimizeSystemModal(touchBar)
|
||||||
|
updateControlStripPresence()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func resetControlStrip() {
|
@objc func resetControlStrip() {
|
||||||
@@ -275,8 +271,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
}
|
}
|
||||||
case .weather(interval: let interval, units: let units, api_key: let api_key, icon_type: let icon_type):
|
case .weather(interval: let interval, units: let units, api_key: let api_key, icon_type: let icon_type):
|
||||||
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type)
|
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type)
|
||||||
case .currency(interval: let interval, from: let from, to: let to):
|
case .currency(interval: let interval, from: let from, to: let to, full: let full):
|
||||||
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to)
|
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, full: full)
|
||||||
case .inputsource():
|
case .inputsource():
|
||||||
barItem = InputSourceBarItem(identifier: identifier)
|
barItem = InputSourceBarItem(identifier: identifier)
|
||||||
case .music(interval: let interval):
|
case .music(interval: let interval):
|
||||||
@@ -287,6 +283,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
barItem = NightShiftBarItem(identifier: identifier)
|
barItem = NightShiftBarItem(identifier: identifier)
|
||||||
case .dnd():
|
case .dnd():
|
||||||
barItem = DnDBarItem(identifier: identifier)
|
barItem = DnDBarItem(identifier: identifier)
|
||||||
|
case .pomodoro(workTime: let workTime, restTime: let restTime):
|
||||||
|
barItem = PomodoroBarItem(identifier: identifier, workTime: workTime, restTime: restTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let action = self.action(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
|
if let action = self.action(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrubberDataSource {
|
class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrubberDataSource {
|
||||||
|
|
||||||
private var scrubber: NSScrubber!
|
private var scrubber: NSScrubber!
|
||||||
|
|
||||||
private let hf: HapticFeedback = HapticFeedback()
|
private let hf: HapticFeedback = HapticFeedback()
|
||||||
@@ -23,22 +22,20 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
private var runningAppsIdentifiers: [String] = []
|
private var runningAppsIdentifiers: [String] = []
|
||||||
|
|
||||||
private var frontmostApplicationIdentifier: String? {
|
private var frontmostApplicationIdentifier: String? {
|
||||||
get {
|
|
||||||
guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil }
|
guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil }
|
||||||
return frontmostId
|
return frontmostId
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private var applications: [DockItem] = []
|
private var applications: [DockItem] = []
|
||||||
|
|
||||||
override init(identifier: NSTouchBarItem.Identifier) {
|
override init(identifier: NSTouchBarItem.Identifier) {
|
||||||
super.init(identifier: identifier)
|
super.init(identifier: identifier)
|
||||||
|
|
||||||
scrubber = NSScrubber();
|
scrubber = NSScrubber()
|
||||||
scrubber.delegate = self
|
scrubber.delegate = self
|
||||||
scrubber.dataSource = self
|
scrubber.dataSource = self
|
||||||
scrubber.mode = .free // .fixed
|
scrubber.mode = .free // .fixed
|
||||||
let layout = NSScrubberFlowLayout();
|
let layout = NSScrubberFlowLayout()
|
||||||
layout.itemSize = NSSize(width: 36, height: 32)
|
layout.itemSize = NSSize(width: 36, height: 32)
|
||||||
layout.itemSpacing = 2
|
layout.itemSpacing = 2
|
||||||
scrubber.scrubberLayout = layout
|
scrubber.scrubberLayout = layout
|
||||||
@@ -54,21 +51,21 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didActivateApplicationNotification, 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") {
|
if let persistent = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.dock.persistent") {
|
||||||
self.persistentAppIdentifiers = persistent
|
persistentAppIdentifiers = persistent
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRunningApplication()
|
updateRunningApplication()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func activeApplicationChanged(n: Notification) {
|
@objc func activeApplicationChanged(n _: Notification) {
|
||||||
updateRunningApplication()
|
updateRunningApplication()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
override func observeValue(forKeyPath _: String?, of _: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
|
||||||
updateRunningApplication()
|
updateRunningApplication()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +83,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
scrubber.selectedIndex = index ?? 0
|
scrubber.selectedIndex = index ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public func numberOfItems(for scrubber: NSScrubber) -> Int {
|
public func numberOfItems(for _: NSScrubber) -> Int {
|
||||||
return applications.count
|
return applications.count
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +102,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
|
|
||||||
let dotView = NSView(frame: .zero)
|
let dotView = NSView(frame: .zero)
|
||||||
dotView.wantsLayer = true
|
dotView.wantsLayer = true
|
||||||
if self.runningAppsIdentifiers.contains(app.bundleIdentifier!) {
|
if runningAppsIdentifiers.contains(app.bundleIdentifier!) {
|
||||||
dotView.layer?.backgroundColor = NSColor.white.cgColor
|
dotView.layer?.backgroundColor = NSColor.white.cgColor
|
||||||
} else {
|
} else {
|
||||||
dotView.layer?.backgroundColor = NSColor.black.cgColor
|
dotView.layer?.backgroundColor = NSColor.black.cgColor
|
||||||
@@ -118,44 +115,44 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
public func didBeginInteracting(with scrubber: NSScrubber) {
|
public func didBeginInteracting(with _: NSScrubber) {
|
||||||
stopTimer()
|
stopTimer()
|
||||||
self.ticks = 0
|
ticks = 0
|
||||||
self.timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(checkTimer), userInfo: nil, repeats: true)
|
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(checkTimer), userInfo: nil, repeats: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func checkTimer() {
|
@objc private func checkTimer() {
|
||||||
self.ticks += 1
|
ticks += 1
|
||||||
|
|
||||||
if (self.ticks == minTicks) {
|
if ticks == minTicks {
|
||||||
hf.tap(strong: 2)
|
hf.tap(strong: 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.ticks > maxTicks) {
|
if ticks > maxTicks {
|
||||||
stopTimer()
|
stopTimer()
|
||||||
hf.tap(strong: 6)
|
hf.tap(strong: 6)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stopTimer() {
|
private func stopTimer() {
|
||||||
self.timer?.invalidate()
|
timer?.invalidate()
|
||||||
self.timer = nil
|
timer = nil
|
||||||
self.lastSelected = 0
|
lastSelected = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public func didCancelInteracting(with scrubber: NSScrubber) {
|
public func didCancelInteracting(with _: NSScrubber) {
|
||||||
stopTimer()
|
stopTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func didFinishInteracting(with scrubber: NSScrubber) {
|
public func didFinishInteracting(with scrubber: NSScrubber) {
|
||||||
stopTimer()
|
stopTimer()
|
||||||
|
|
||||||
if (ticks == 0) {
|
if ticks == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.ticks >= minTicks && scrubber.selectedIndex > 0) {
|
if ticks >= minTicks && scrubber.selectedIndex > 0 {
|
||||||
self.longPress(selected: scrubber.selectedIndex)
|
longPress(selected: scrubber.selectedIndex)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +173,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
private func longPress(selected: Int) {
|
private func longPress(selected: Int) {
|
||||||
let bundleIdentifier = applications[selected].bundleIdentifier
|
let bundleIdentifier = applications[selected].bundleIdentifier
|
||||||
|
|
||||||
if (self.ticks > maxTicks) {
|
if ticks > maxTicks {
|
||||||
if let processIdentifier = applications[selected].pid {
|
if let processIdentifier = applications[selected].pid {
|
||||||
if !(NSRunningApplication(processIdentifier: processIdentifier)?.terminate())! {
|
if !(NSRunningApplication(processIdentifier: processIdentifier)?.terminate())! {
|
||||||
NSRunningApplication(processIdentifier: processIdentifier)?.forceTerminate()
|
NSRunningApplication(processIdentifier: processIdentifier)?.forceTerminate()
|
||||||
@@ -185,26 +182,26 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
} else {
|
} else {
|
||||||
hf.tap(strong: 6)
|
hf.tap(strong: 6)
|
||||||
if let index = self.persistentAppIdentifiers.index(of: bundleIdentifier!) {
|
if let index = self.persistentAppIdentifiers.index(of: bundleIdentifier!) {
|
||||||
self.persistentAppIdentifiers.remove(at: index)
|
persistentAppIdentifiers.remove(at: index)
|
||||||
} else {
|
} else {
|
||||||
self.persistentAppIdentifiers.append(bundleIdentifier!)
|
persistentAppIdentifiers.append(bundleIdentifier!)
|
||||||
}
|
}
|
||||||
|
|
||||||
UserDefaults.standard.set(self.persistentAppIdentifiers, forKey: "com.toxblh.mtmr.dock.persistent")
|
UserDefaults.standard.set(persistentAppIdentifiers, forKey: "com.toxblh.mtmr.dock.persistent")
|
||||||
UserDefaults.standard.synchronize()
|
UserDefaults.standard.synchronize()
|
||||||
}
|
}
|
||||||
self.ticks = 0
|
ticks = 0
|
||||||
updateRunningApplication()
|
updateRunningApplication()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func launchedApplications() -> [DockItem] {
|
private func launchedApplications() -> [DockItem] {
|
||||||
self.runningAppsIdentifiers = []
|
runningAppsIdentifiers = []
|
||||||
var returnable: [DockItem] = []
|
var returnable: [DockItem] = []
|
||||||
for app in NSWorkspace.shared.runningApplications {
|
for app in NSWorkspace.shared.runningApplications {
|
||||||
guard app.activationPolicy == NSApplication.ActivationPolicy.regular else { continue }
|
guard app.activationPolicy == NSApplication.ActivationPolicy.regular else { continue }
|
||||||
guard let bundleIdentifier = app.bundleIdentifier else { continue }
|
guard let bundleIdentifier = app.bundleIdentifier else { continue }
|
||||||
|
|
||||||
self.runningAppsIdentifiers.append(bundleIdentifier)
|
runningAppsIdentifiers.append(bundleIdentifier)
|
||||||
|
|
||||||
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier), pid: app.processIdentifier)
|
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier), pid: app.processIdentifier)
|
||||||
returnable.append(dockItem)
|
returnable.append(dockItem)
|
||||||
@@ -212,7 +209,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
return returnable
|
return returnable
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getIcon(forBundleIdentifier bundleIdentifier: String? = nil, orPath path: String? = nil, orType type: String? = nil) -> NSImage {
|
public func getIcon(forBundleIdentifier bundleIdentifier: String? = nil, orPath path: String? = nil, orType _: String? = nil) -> NSImage {
|
||||||
if bundleIdentifier != nil {
|
if bundleIdentifier != nil {
|
||||||
if let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleIdentifier!) {
|
if let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleIdentifier!) {
|
||||||
return NSWorkspace.shared.icon(forFile: appPath)
|
return NSWorkspace.shared.icon(forFile: appPath)
|
||||||
@@ -227,12 +224,11 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
|||||||
return genericIcon ?? NSImage(size: .zero)
|
return genericIcon ?? NSImage(size: .zero)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public func getDockPersistentAppsList() -> [DockItem] {
|
public func getDockPersistentAppsList() -> [DockItem] {
|
||||||
var returnable: [DockItem] = []
|
var returnable: [DockItem] = []
|
||||||
|
|
||||||
for bundleIdentifier in persistentAppIdentifiers {
|
for bundleIdentifier in persistentAppIdentifiers {
|
||||||
if !self.runningAppsIdentifiers.contains(bundleIdentifier) {
|
if !runningAppsIdentifiers.contains(bundleIdentifier) {
|
||||||
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier))
|
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier))
|
||||||
returnable.append(dockItem)
|
returnable.append(dockItem)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import IOKit.ps
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import IOKit.ps
|
||||||
|
|
||||||
class BatteryBarItem: CustomButtonTouchBarItem {
|
class BatteryBarItem: CustomButtonTouchBarItem {
|
||||||
private let batteryInfo = BatteryInfo()
|
private let batteryInfo = BatteryInfo()
|
||||||
@@ -18,15 +18,15 @@ class BatteryBarItem: CustomButtonTouchBarItem {
|
|||||||
batteryInfo.start { [weak self] in
|
batteryInfo.start { [weak self] in
|
||||||
self?.refresh()
|
self?.refresh()
|
||||||
}
|
}
|
||||||
self.refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func refresh() {
|
func refresh() {
|
||||||
self.attributedTitle = self.batteryInfo.formattedInfo()
|
attributedTitle = batteryInfo.formattedInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@@ -42,14 +42,14 @@ class BatteryInfo: NSObject {
|
|||||||
var isCharging: Bool = false
|
var isCharging: Bool = false
|
||||||
var ACPower: String = ""
|
var ACPower: String = ""
|
||||||
var timeRemaining: String = ""
|
var timeRemaining: String = ""
|
||||||
var notifyBlock: ()->() = {}
|
var notifyBlock: () -> Void = {}
|
||||||
var loop:CFRunLoopSource?
|
var loop: CFRunLoopSource?
|
||||||
|
|
||||||
func start(notifyBlock: @escaping ()->()) {
|
func start(notifyBlock: @escaping () -> Void) {
|
||||||
self.notifyBlock = notifyBlock
|
self.notifyBlock = notifyBlock
|
||||||
let opaque = Unmanaged.passRetained(self).toOpaque()
|
let opaque = Unmanaged.passRetained(self).toOpaque()
|
||||||
let context = UnsafeMutableRawPointer(opaque)
|
let context = UnsafeMutableRawPointer(opaque)
|
||||||
loop = IOPSNotificationCreateRunLoopSource({ (context) in
|
loop = IOPSNotificationCreateRunLoopSource({ context in
|
||||||
guard let ctx = context else {
|
guard let ctx = context else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ class BatteryInfo: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func stop() {
|
func stop() {
|
||||||
self.notifyBlock = {}
|
notifyBlock = {}
|
||||||
guard let loop = self.loop else {
|
guard let loop = self.loop else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -75,41 +75,35 @@ class BatteryInfo: NSObject {
|
|||||||
|
|
||||||
for ps in psList {
|
for ps in psList {
|
||||||
if let psDesc = IOPSGetPowerSourceDescription(psInfo, ps).takeUnretainedValue() as? [String: Any] {
|
if let psDesc = IOPSGetPowerSourceDescription(psInfo, ps).takeUnretainedValue() as? [String: Any] {
|
||||||
let current = psDesc[kIOPSCurrentCapacityKey]
|
if let current = psDesc[kIOPSCurrentCapacityKey] as? Int {
|
||||||
if (current != nil) {
|
self.current = current
|
||||||
self.current = current as! Int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let timeToEmpty = psDesc[kIOPSTimeToEmptyKey]
|
if let timeToEmpty = psDesc[kIOPSTimeToEmptyKey] as? Int {
|
||||||
if (timeToEmpty != nil) {
|
self.timeToEmpty = timeToEmpty
|
||||||
self.timeToEmpty = timeToEmpty as! Int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let timeToFull = psDesc[kIOPSTimeToFullChargeKey]
|
if let timeToFull = psDesc[kIOPSTimeToFullChargeKey] as? Int {
|
||||||
if (timeToFull != nil) {
|
self.timeToFull = timeToFull
|
||||||
self.timeToFull = timeToFull as! Int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let isCharged = psDesc[kIOPSIsChargedKey]
|
if let isCharged = psDesc[kIOPSIsChargedKey] as? Bool {
|
||||||
if (isCharged != nil) {
|
self.isCharged = isCharged
|
||||||
self.isCharged = isCharged as! Bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let isCharging = psDesc[kIOPSIsChargingKey]
|
if let isCharging = psDesc[kIOPSIsChargingKey] as? Bool {
|
||||||
if (isCharging != nil) {
|
self.isCharging = isCharging
|
||||||
self.isCharging = isCharging as! Bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ACPower = psDesc[kIOPSPowerSourceStateKey]
|
if let ACPower = psDesc[kIOPSPowerSourceStateKey] as? String {
|
||||||
if (ACPower != nil) {
|
self.ACPower = ACPower
|
||||||
self.ACPower = ACPower as! String
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFormattedTime(time: Int) -> String {
|
func getFormattedTime(time: Int) -> String {
|
||||||
if (time > 0) {
|
if time > 0 {
|
||||||
let timeFormatted = NSString(format: " %d:%02d", time / 60, time % 60) as String
|
let timeFormatted = NSString(format: " %d:%02d", time / 60, time % 60) as String
|
||||||
return timeFormatted
|
return timeFormatted
|
||||||
}
|
}
|
||||||
@@ -119,7 +113,7 @@ class BatteryInfo: NSObject {
|
|||||||
|
|
||||||
public func formattedInfo() -> NSAttributedString {
|
public func formattedInfo() -> NSAttributedString {
|
||||||
var title = ""
|
var title = ""
|
||||||
self.getPSInfo()
|
getPSInfo()
|
||||||
|
|
||||||
if ACPower == "AC Power" {
|
if ACPower == "AC Power" {
|
||||||
if current < 100 {
|
if current < 100 {
|
||||||
@@ -143,5 +137,4 @@ class BatteryInfo: NSObject {
|
|||||||
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
|
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
|
||||||
return newTitle
|
return newTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Cocoa
|
|
||||||
import AppKit
|
import AppKit
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
import Cocoa
|
||||||
import CoreAudio
|
import CoreAudio
|
||||||
|
|
||||||
class BrightnessViewController: NSCustomTouchBarItem {
|
class BrightnessViewController: NSCustomTouchBarItem {
|
||||||
@@ -9,7 +9,7 @@ class BrightnessViewController: NSCustomTouchBarItem {
|
|||||||
init(identifier: NSTouchBarItem.Identifier, refreshInterval: Double, image: NSImage? = nil) {
|
init(identifier: NSTouchBarItem.Identifier, refreshInterval: Double, image: NSImage? = nil) {
|
||||||
super.init(identifier: identifier)
|
super.init(identifier: identifier)
|
||||||
|
|
||||||
if (image == nil) {
|
if image == nil {
|
||||||
sliderItem = CustomSlider()
|
sliderItem = CustomSlider()
|
||||||
} else {
|
} else {
|
||||||
sliderItem = CustomSlider(knob: image!)
|
sliderItem = CustomSlider(knob: image!)
|
||||||
@@ -20,13 +20,13 @@ class BrightnessViewController: NSCustomTouchBarItem {
|
|||||||
sliderItem.maxValue = 100.0
|
sliderItem.maxValue = 100.0
|
||||||
sliderItem.floatValue = getBrightness() * 100
|
sliderItem.floatValue = getBrightness() * 100
|
||||||
|
|
||||||
self.view = sliderItem
|
view = sliderItem
|
||||||
|
|
||||||
let timer = Timer.scheduledTimer(timeInterval: refreshInterval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true)
|
let timer = Timer.scheduledTimer(timeInterval: refreshInterval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true)
|
||||||
RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
|
RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,13 +42,13 @@ class BrightnessViewController: NSCustomTouchBarItem {
|
|||||||
|
|
||||||
@objc func sliderValueChanged(_ sender: Any) {
|
@objc func sliderValueChanged(_ sender: Any) {
|
||||||
if let sliderItem = sender as? NSSlider {
|
if let sliderItem = sender as? NSSlider {
|
||||||
setBrightness(level: Float32(sliderItem.intValue)/100.0)
|
setBrightness(level: Float32(sliderItem.intValue) / 100.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getBrightness() -> Float32 {
|
private func getBrightness() -> Float32 {
|
||||||
if #available(OSX 10.13, *) {
|
if #available(OSX 10.13, *) {
|
||||||
return Float32(CoreDisplay_Display_GetUserBrightness(0));
|
return Float32(CoreDisplay_Display_GetUserBrightness(0))
|
||||||
} else {
|
} else {
|
||||||
var level: Float32 = 0.5
|
var level: Float32 = 0.5
|
||||||
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
|
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
|
||||||
@@ -60,13 +60,12 @@ class BrightnessViewController: NSCustomTouchBarItem {
|
|||||||
|
|
||||||
private func setBrightness(level: Float) {
|
private func setBrightness(level: Float) {
|
||||||
if #available(OSX 10.13, *) {
|
if #available(OSX 10.13, *) {
|
||||||
CoreDisplay_Display_SetUserBrightness(0, Double(level));
|
CoreDisplay_Display_SetUserBrightness(0, Double(level))
|
||||||
} else {
|
} else {
|
||||||
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
|
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
|
||||||
|
|
||||||
IODisplaySetFloatParameter(service, 1, kIODisplayBrightnessKey as CFString, level)
|
IODisplaySetFloatParameter(service, 1, kIODisplayBrightnessKey as CFString, level)
|
||||||
IOObjectRelease(service)
|
IOObjectRelease(service)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ import CoreLocation
|
|||||||
class CurrencyBarItem: CustomButtonTouchBarItem {
|
class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||||
private let activity: NSBackgroundActivityScheduler
|
private let activity: NSBackgroundActivityScheduler
|
||||||
private var prefix: String
|
private var prefix: String
|
||||||
|
private var postfix: String
|
||||||
private var from: String
|
private var from: String
|
||||||
private var to: String
|
private var to: String
|
||||||
private var oldValue: Float32!
|
private var oldValue: Float32!
|
||||||
|
private var full: Bool = false
|
||||||
|
|
||||||
private let currencies = [
|
private let currencies = [
|
||||||
"USD": "$",
|
"USD": "$",
|
||||||
@@ -36,16 +38,23 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
|||||||
"ETH": "Ξ",
|
"ETH": "Ξ",
|
||||||
]
|
]
|
||||||
|
|
||||||
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String) {
|
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, full: Bool) {
|
||||||
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||||
activity.interval = interval
|
activity.interval = interval
|
||||||
self.from = from
|
self.from = from
|
||||||
self.to = to
|
self.to = to
|
||||||
|
self.full = full
|
||||||
|
|
||||||
if let prefix = currencies[from] {
|
if let prefix = currencies[from] {
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
} else {
|
} else {
|
||||||
self.prefix = from
|
prefix = from
|
||||||
|
}
|
||||||
|
|
||||||
|
if let postfix = currencies[to] {
|
||||||
|
self.postfix = postfix
|
||||||
|
} else {
|
||||||
|
postfix = to
|
||||||
}
|
}
|
||||||
|
|
||||||
super.init(identifier: identifier, title: "⏳")
|
super.init(identifier: identifier, title: "⏳")
|
||||||
@@ -59,21 +68,21 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
|||||||
updateCurrency()
|
updateCurrency()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func updateCurrency() {
|
@objc func updateCurrency() {
|
||||||
let urlRequest = URLRequest(url: URL(string: "https://api.coinbase.com/v2/exchange-rates?currency=\(from)")!)
|
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
|
let task = URLSession.shared.dataTask(with: urlRequest) { data, _, error in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
do {
|
do {
|
||||||
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
|
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
|
||||||
var value: Float32!
|
var value: Float32!
|
||||||
|
|
||||||
if let data_array = json["data"] as? [String : AnyObject] {
|
if let data_array = json["data"] as? [String: AnyObject] {
|
||||||
if let rates = data_array["rates"] as? [String : AnyObject] {
|
if let rates = data_array["rates"] as? [String: AnyObject] {
|
||||||
if let item = rates["\(self.to)"] as? String {
|
if let item = rates["\(self.to)"] as? String {
|
||||||
value = Float32(item)
|
value = Float32(item)
|
||||||
}
|
}
|
||||||
@@ -103,13 +112,19 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
|||||||
color = NSColor.red
|
color = NSColor.red
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.oldValue = value
|
|
||||||
|
|
||||||
let title = String(format: "%@%.2f", self.prefix, value)
|
oldValue = value
|
||||||
|
|
||||||
let regularFont = self.attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
|
var title = ""
|
||||||
|
if full {
|
||||||
|
title = String(format: "%@‣%.2f%@", prefix, value, postfix)
|
||||||
|
} else {
|
||||||
|
title = String(format: "%@%.2f", prefix, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
let regularFont = 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])
|
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont])
|
||||||
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
|
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
|
||||||
self.attributedTitle = newTitle
|
attributedTitle = newTitle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,22 +8,22 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class DnDBarItem : CustomButtonTouchBarItem {
|
class DnDBarItem: CustomButtonTouchBarItem {
|
||||||
private var timer: Timer!
|
private var timer: Timer!
|
||||||
|
|
||||||
init(identifier: NSTouchBarItem.Identifier) {
|
init(identifier: NSTouchBarItem.Identifier) {
|
||||||
super.init(identifier: identifier, title: "")
|
super.init(identifier: identifier, title: "")
|
||||||
self.isBordered = false
|
isBordered = false
|
||||||
self.setWidth(value: 32)
|
setWidth(value: 32)
|
||||||
|
|
||||||
self.tapClosure = { [weak self] in self?.DnDToggle() }
|
tapClosure = { [weak self] in self?.DnDToggle() }
|
||||||
|
|
||||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||||
|
|
||||||
self.refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ class DnDBarItem : CustomButtonTouchBarItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func refresh() {
|
@objc func refresh() {
|
||||||
self.image = DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off")
|
image = DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ public struct DoNotDisturb {
|
|||||||
commitChanges()
|
commitChanges()
|
||||||
}
|
}
|
||||||
|
|
||||||
static var isEnabled:Bool {
|
static var isEnabled: Bool {
|
||||||
get {
|
get {
|
||||||
return CFPreferencesGetAppBooleanValue("doNotDisturb" as CFString, appId, nil)
|
return CFPreferencesGetAppBooleanValue("doNotDisturb" as CFString, appId, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
|
class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
|
||||||
|
|
||||||
var jsonItems: [BarItemDefinition]
|
var jsonItems: [BarItemDefinition]
|
||||||
|
|
||||||
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
||||||
@@ -23,48 +22,46 @@ class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
|
|||||||
init(identifier: NSTouchBarItem.Identifier, items: [BarItemDefinition]) {
|
init(identifier: NSTouchBarItem.Identifier, items: [BarItemDefinition]) {
|
||||||
jsonItems = items
|
jsonItems = items
|
||||||
super.init(identifier: identifier)
|
super.init(identifier: identifier)
|
||||||
self.popoverTouchBar.delegate = self
|
popoverTouchBar.delegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {}
|
||||||
|
|
||||||
}
|
@objc override func showPopover(_: Any?) {
|
||||||
|
itemDefinitions = [:]
|
||||||
|
items = [:]
|
||||||
|
leftIdentifiers = []
|
||||||
|
centerItems = []
|
||||||
|
rightIdentifiers = []
|
||||||
|
|
||||||
@objc override func showPopover(_ sender: Any?) {
|
loadItemDefinitions(jsonItems: jsonItems)
|
||||||
self.itemDefinitions = [:]
|
createItems()
|
||||||
self.items = [:]
|
|
||||||
self.leftIdentifiers = []
|
|
||||||
self.centerItems = []
|
|
||||||
self.rightIdentifiers = []
|
|
||||||
|
|
||||||
self.loadItemDefinitions(jsonItems: jsonItems)
|
|
||||||
self.createItems()
|
|
||||||
|
|
||||||
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||||
return items[identifier]
|
items[identifier]
|
||||||
})
|
})
|
||||||
|
|
||||||
self.centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
||||||
self.scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
|
scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
|
||||||
|
|
||||||
TouchBarController.shared.touchBar.delegate = self
|
TouchBarController.shared.touchBar.delegate = self
|
||||||
TouchBarController.shared.touchBar.defaultItemIdentifiers = []
|
TouchBarController.shared.touchBar.defaultItemIdentifiers = []
|
||||||
TouchBarController.shared.touchBar.defaultItemIdentifiers = self.leftIdentifiers + [centerScrollArea] + self.rightIdentifiers
|
TouchBarController.shared.touchBar.defaultItemIdentifiers = leftIdentifiers + [centerScrollArea] + rightIdentifiers
|
||||||
|
|
||||||
if TouchBarController.shared.controlStripState {
|
if TouchBarController.shared.showControlStripState {
|
||||||
presentSystemModal(TouchBarController.shared.touchBar, systemTrayItemIdentifier: .controlStripItem)
|
presentSystemModal(TouchBarController.shared.touchBar, systemTrayItemIdentifier: .controlStripItem)
|
||||||
} else {
|
} else {
|
||||||
presentSystemModal(TouchBarController.shared.touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
|
presentSystemModal(TouchBarController.shared.touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
func touchBar(_: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
||||||
if identifier == centerScrollArea {
|
if identifier == centerScrollArea {
|
||||||
return self.scrollArea
|
return scrollArea
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let item = self.items[identifier],
|
guard let item = self.items[identifier],
|
||||||
@@ -96,8 +93,8 @@ class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createItems() {
|
func createItems() {
|
||||||
for (identifier, definition) in self.itemDefinitions {
|
for (identifier, definition) in itemDefinitions {
|
||||||
self.items[identifier] = TouchBarController.shared.createItem(forIdentifier: identifier, definition: definition)
|
items[identifier] = TouchBarController.shared.createItem(forIdentifier: identifier, definition: definition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,33 +9,32 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class InputSourceBarItem: CustomButtonTouchBarItem {
|
class InputSourceBarItem: CustomButtonTouchBarItem {
|
||||||
|
|
||||||
fileprivate var notificationCenter: CFNotificationCenter
|
fileprivate var notificationCenter: CFNotificationCenter
|
||||||
let buttonSize = NSSize(width: 21, height: 21)
|
let buttonSize = NSSize(width: 21, height: 21)
|
||||||
|
|
||||||
init(identifier: NSTouchBarItem.Identifier) {
|
init(identifier: NSTouchBarItem.Identifier) {
|
||||||
notificationCenter = CFNotificationCenterGetDistributedCenter();
|
notificationCenter = CFNotificationCenterGetDistributedCenter()
|
||||||
super.init(identifier: identifier, title: "⏳")
|
super.init(identifier: identifier, title: "⏳")
|
||||||
|
|
||||||
observeIputSourceChangedNotification();
|
observeIputSourceChangedNotification()
|
||||||
textInputSourceDidChange()
|
textInputSourceDidChange()
|
||||||
self.tapClosure = { [weak self] in
|
tapClosure = { [weak self] in
|
||||||
self?.switchInputSource()
|
self?.switchInputSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()));
|
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func textInputSourceDidChange() {
|
@objc public func textInputSourceDidChange() {
|
||||||
let currentSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
|
let currentSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
|
||||||
|
|
||||||
var iconImage: NSImage? = nil
|
var iconImage: NSImage?
|
||||||
|
|
||||||
if let imageURL = currentSource.iconImageURL,
|
if let imageURL = currentSource.iconImageURL,
|
||||||
let image = NSImage(contentsOf: imageURL) {
|
let image = NSImage(contentsOf: imageURL) {
|
||||||
@@ -46,10 +45,10 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
|
|||||||
|
|
||||||
if let iconImage = iconImage {
|
if let iconImage = iconImage {
|
||||||
iconImage.size = buttonSize
|
iconImage.size = buttonSize
|
||||||
self.image = iconImage
|
image = iconImage
|
||||||
self.title = ""
|
title = ""
|
||||||
} else {
|
} else {
|
||||||
self.title = currentSource.name
|
title = currentSource.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,15 +64,15 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
|
|||||||
})
|
})
|
||||||
|
|
||||||
for item in inputSources {
|
for item in inputSources {
|
||||||
if (item.id != currentSource.id) {
|
if item.id != currentSource.id {
|
||||||
TISSelectInputSource(item)
|
TISSelectInputSource(item)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func observeIputSourceChangedNotification(){
|
@objc public func observeIputSourceChangedNotification() {
|
||||||
let callback: CFNotificationCallback = { center, observer, name, object, info in
|
let callback: CFNotificationCallback = { _, observer, _, _, _ in
|
||||||
let mySelf = Unmanaged<InputSourceBarItem>.fromOpaque(observer!).takeUnretainedValue()
|
let mySelf = Unmanaged<InputSourceBarItem>.fromOpaque(observer!).takeUnretainedValue()
|
||||||
mySelf.textInputSourceDidChange()
|
mySelf.textInputSourceDidChange()
|
||||||
}
|
}
|
||||||
@@ -96,7 +95,7 @@ extension TISInputSource {
|
|||||||
|
|
||||||
private func getProperty(_ key: CFString) -> AnyObject? {
|
private func getProperty(_ key: CFString) -> AnyObject? {
|
||||||
let cfType = TISGetInputSourceProperty(self, key)
|
let cfType = TISGetInputSourceProperty(self, key)
|
||||||
if (cfType != nil) {
|
if cfType != nil {
|
||||||
return Unmanaged<AnyObject>.fromOpaque(cfType!).takeUnretainedValue()
|
return Unmanaged<AnyObject>.fromOpaque(cfType!).takeUnretainedValue()
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
@@ -131,4 +130,3 @@ extension TISInputSource {
|
|||||||
return OpaquePointer(TISGetInputSourceProperty(self, kTISPropertyIconRef)) as IconRef?
|
return OpaquePointer(TISGetInputSourceProperty(self, kTISPropertyIconRef)) as IconRef?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,51 +20,51 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
|||||||
"com.spotify.client",
|
"com.spotify.client",
|
||||||
"com.coppertino.Vox",
|
"com.coppertino.Vox",
|
||||||
"com.google.Chrome",
|
"com.google.Chrome",
|
||||||
"com.apple.Safari"
|
"com.apple.Safari",
|
||||||
]
|
]
|
||||||
|
|
||||||
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
|
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
|
||||||
self.interval = interval
|
self.interval = interval
|
||||||
|
|
||||||
super.init(identifier: identifier, title: "⏳")
|
super.init(identifier: identifier, title: "⏳")
|
||||||
self.isBordered = false
|
isBordered = false
|
||||||
|
|
||||||
self.tapClosure = { [weak self] in self?.playPause() }
|
tapClosure = { [weak self] in self?.playPause() }
|
||||||
self.longTapClosure = { [weak self] in self?.nextTrack() }
|
longTapClosure = { [weak self] in self?.nextTrack() }
|
||||||
|
|
||||||
self.refreshAndSchedule()
|
refreshAndSchedule()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func marquee(){
|
@objc func marquee() {
|
||||||
let str = self.title
|
let str = title
|
||||||
if (str.count > 10) {
|
if str.count > 10 {
|
||||||
let indexFirst = str.index(str.startIndex, offsetBy: 0)
|
let indexFirst = str.index(str.startIndex, offsetBy: 0)
|
||||||
let indexSecond = str.index(str.startIndex, offsetBy: 1)
|
let indexSecond = str.index(str.startIndex, offsetBy: 1)
|
||||||
self.title = String(str.suffix(from: indexSecond)) + String(str[indexFirst])
|
title = String(str.suffix(from: indexSecond)) + String(str[indexFirst])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func playPause() {
|
@objc func playPause() {
|
||||||
for ident in playerBundleIdentifiers {
|
for ident in playerBundleIdentifiers {
|
||||||
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
|
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
|
||||||
if (musicPlayer.isRunning) {
|
if musicPlayer.isRunning {
|
||||||
if (musicPlayer.className == "SpotifyApplication") {
|
if musicPlayer.className == "SpotifyApplication" {
|
||||||
let mp = (musicPlayer as SpotifyApplication)
|
let mp = (musicPlayer as SpotifyApplication)
|
||||||
mp.playpause!()
|
mp.playpause!()
|
||||||
return
|
return
|
||||||
} else if (musicPlayer.className == "ITunesApplication") {
|
} else if musicPlayer.className == "ITunesApplication" {
|
||||||
let mp = (musicPlayer as iTunesApplication)
|
let mp = (musicPlayer as iTunesApplication)
|
||||||
mp.playpause!()
|
mp.playpause!()
|
||||||
return
|
return
|
||||||
} else if (musicPlayer.className == "VOXApplication") {
|
} else if musicPlayer.className == "VOXApplication" {
|
||||||
let mp = (musicPlayer as VoxApplication)
|
let mp = (musicPlayer as VoxApplication)
|
||||||
mp.playpause!()
|
mp.playpause!()
|
||||||
return
|
return
|
||||||
} else if (musicPlayer.className == "SafariApplication") {
|
} else if musicPlayer.className == "SafariApplication" {
|
||||||
// You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'.
|
// You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'.
|
||||||
let safariApplication = musicPlayer as SafariApplication
|
let safariApplication = musicPlayer as SafariApplication
|
||||||
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
|
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
|
||||||
@@ -74,7 +74,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
|||||||
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
|
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
|
||||||
_ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_play')[0].click()", in: tab)
|
_ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_play')[0].click()", in: tab)
|
||||||
return
|
return
|
||||||
} else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) {
|
} else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
|
||||||
_ = safariApplication.doJavaScript!("document.getElementsByClassName('audio_page_player_play')[0].click()", in: tab)
|
_ = safariApplication.doJavaScript!("document.getElementsByClassName('audio_page_player_play')[0].click()", in: tab)
|
||||||
return
|
return
|
||||||
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
|
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
|
||||||
@@ -112,23 +112,23 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
|||||||
@objc func nextTrack() {
|
@objc func nextTrack() {
|
||||||
for ident in playerBundleIdentifiers {
|
for ident in playerBundleIdentifiers {
|
||||||
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
|
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
|
||||||
if (musicPlayer.isRunning) {
|
if musicPlayer.isRunning {
|
||||||
if (musicPlayer.className == "SpotifyApplication") {
|
if musicPlayer.className == "SpotifyApplication" {
|
||||||
let mp = (musicPlayer as SpotifyApplication)
|
let mp = (musicPlayer as SpotifyApplication)
|
||||||
mp.nextTrack!()
|
mp.nextTrack!()
|
||||||
updatePlayer()
|
updatePlayer()
|
||||||
return
|
return
|
||||||
} else if (musicPlayer.className == "ITunesApplication") {
|
} else if musicPlayer.className == "ITunesApplication" {
|
||||||
let mp = (musicPlayer as iTunesApplication)
|
let mp = (musicPlayer as iTunesApplication)
|
||||||
mp.nextTrack!()
|
mp.nextTrack!()
|
||||||
updatePlayer()
|
updatePlayer()
|
||||||
return
|
return
|
||||||
} else if (musicPlayer.className == "VOXApplication") {
|
} else if musicPlayer.className == "VOXApplication" {
|
||||||
let mp = (musicPlayer as VoxApplication)
|
let mp = (musicPlayer as VoxApplication)
|
||||||
mp.next!()
|
mp.next!()
|
||||||
updatePlayer()
|
updatePlayer()
|
||||||
return
|
return
|
||||||
} else if (musicPlayer.className == "SafariApplication") {
|
} else if musicPlayer.className == "SafariApplication" {
|
||||||
// You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'.
|
// You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'.
|
||||||
let safariApplication = musicPlayer as SafariApplication
|
let safariApplication = musicPlayer as SafariApplication
|
||||||
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
|
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
|
||||||
@@ -139,7 +139,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
|||||||
_ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_next')[0].click()", in: tab)
|
_ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_next')[0].click()", in: tab)
|
||||||
updatePlayer()
|
updatePlayer()
|
||||||
return
|
return
|
||||||
} else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) {
|
} else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
|
||||||
_ = safariApplication.doJavaScript!("document.getElementsByClassName('audio_page_player_next')[0].click()", in: tab)
|
_ = safariApplication.doJavaScript!("document.getElementsByClassName('audio_page_player_next')[0].click()", in: tab)
|
||||||
updatePlayer()
|
updatePlayer()
|
||||||
return
|
return
|
||||||
@@ -171,15 +171,15 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
|||||||
|
|
||||||
for var ident in playerBundleIdentifiers {
|
for var ident in playerBundleIdentifiers {
|
||||||
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
|
if let musicPlayer = SBApplication(bundleIdentifier: ident) {
|
||||||
if (musicPlayer.isRunning) {
|
if musicPlayer.isRunning {
|
||||||
var tempTitle = ""
|
var tempTitle = ""
|
||||||
if (musicPlayer.className == "SpotifyApplication") {
|
if musicPlayer.className == "SpotifyApplication" {
|
||||||
tempTitle = (musicPlayer as SpotifyApplication).title
|
tempTitle = (musicPlayer as SpotifyApplication).title
|
||||||
} else if (musicPlayer.className == "ITunesApplication") {
|
} else if musicPlayer.className == "ITunesApplication" {
|
||||||
tempTitle = (musicPlayer as iTunesApplication).title
|
tempTitle = (musicPlayer as iTunesApplication).title
|
||||||
} else if (musicPlayer.className == "VOXApplication") {
|
} else if musicPlayer.className == "VOXApplication" {
|
||||||
tempTitle = (musicPlayer as VoxApplication).title
|
tempTitle = (musicPlayer as VoxApplication).title
|
||||||
} else if (musicPlayer.className == "SafariApplication") {
|
} else if musicPlayer.className == "SafariApplication" {
|
||||||
let safariApplication = musicPlayer as SafariApplication
|
let safariApplication = musicPlayer as SafariApplication
|
||||||
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
|
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
|
||||||
for window in safariWindows! {
|
for window in safariWindows! {
|
||||||
@@ -190,7 +190,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
|||||||
tempTitle = (tab.name)!
|
tempTitle = (tab.name)!
|
||||||
break
|
break
|
||||||
// }
|
// }
|
||||||
} else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) {
|
} else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
|
||||||
tempTitle = (tab.name)!
|
tempTitle = (tab.name)!
|
||||||
break
|
break
|
||||||
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
|
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
|
||||||
@@ -202,18 +202,18 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
|||||||
if tempTitle == "" {
|
if tempTitle == "" {
|
||||||
ident = ""
|
ident = ""
|
||||||
}
|
}
|
||||||
} else if (musicPlayer.className == "GoogleChromeApplication") {
|
} else if musicPlayer.className == "GoogleChromeApplication" {
|
||||||
let chromeApplication = musicPlayer as GoogleChromeApplication
|
let chromeApplication = musicPlayer as GoogleChromeApplication
|
||||||
let chromeWindows = chromeApplication.windows?().compactMap({ $0 as? GoogleChromeWindow })
|
let chromeWindows = chromeApplication.windows?().compactMap({ $0 as? GoogleChromeWindow })
|
||||||
for window in chromeWindows! {
|
for window in chromeWindows! {
|
||||||
for tab in window.tabs!() {
|
for tab in window.tabs!() {
|
||||||
let tab = tab as! GoogleChromeTab
|
let tab = tab as! GoogleChromeTab
|
||||||
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
|
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
|
||||||
if (!(tab.title?.hasSuffix("на Яндекс.Музыке"))!) {
|
if !(tab.title?.hasSuffix("на Яндекс.Музыке"))! {
|
||||||
tempTitle = tab.title!
|
tempTitle = tab.title!
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) {
|
} else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
|
||||||
tempTitle = tab.title!
|
tempTitle = tab.title!
|
||||||
break
|
break
|
||||||
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
|
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
|
||||||
@@ -227,7 +227,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tempTitle == self.songTitle) {
|
if tempTitle == self.songTitle {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
self.songTitle = tempTitle
|
self.songTitle = tempTitle
|
||||||
@@ -265,18 +265,20 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc protocol SpotifyApplication {
|
@objc protocol SpotifyApplication {
|
||||||
@objc optional var currentTrack: SpotifyTrack {get}
|
@objc optional var currentTrack: SpotifyTrack { get }
|
||||||
@objc optional func nextTrack()
|
@objc optional func nextTrack()
|
||||||
@objc optional func previousTrack()
|
@objc optional func previousTrack()
|
||||||
@objc optional func playpause()
|
@objc optional func playpause()
|
||||||
}
|
}
|
||||||
extension SBApplication: SpotifyApplication{}
|
|
||||||
|
extension SBApplication: SpotifyApplication {}
|
||||||
|
|
||||||
@objc protocol SpotifyTrack {
|
@objc protocol SpotifyTrack {
|
||||||
@objc optional var artist: String {get}
|
@objc optional var artist: String { get }
|
||||||
@objc optional var name: String {get}
|
@objc optional var name: String { get }
|
||||||
}
|
}
|
||||||
extension SBObject: SpotifyTrack{}
|
|
||||||
|
extension SBObject: SpotifyTrack {}
|
||||||
|
|
||||||
extension SpotifyApplication {
|
extension SpotifyApplication {
|
||||||
var title: String {
|
var title: String {
|
||||||
@@ -285,20 +287,21 @@ extension SpotifyApplication {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@objc protocol iTunesApplication {
|
@objc protocol iTunesApplication {
|
||||||
@objc optional var currentTrack: iTunesTrack {get}
|
@objc optional var currentTrack: iTunesTrack { get }
|
||||||
@objc optional func playpause()
|
@objc optional func playpause()
|
||||||
@objc optional func nextTrack()
|
@objc optional func nextTrack()
|
||||||
@objc optional func previousTrack()
|
@objc optional func previousTrack()
|
||||||
}
|
}
|
||||||
extension SBApplication: iTunesApplication{}
|
|
||||||
|
extension SBApplication: iTunesApplication {}
|
||||||
|
|
||||||
@objc protocol iTunesTrack {
|
@objc protocol iTunesTrack {
|
||||||
@objc optional var artist: String {get}
|
@objc optional var artist: String { get }
|
||||||
@objc optional var name: String {get}
|
@objc optional var name: String { get }
|
||||||
}
|
}
|
||||||
extension SBObject: iTunesTrack{}
|
|
||||||
|
extension SBObject: iTunesTrack {}
|
||||||
|
|
||||||
extension iTunesApplication {
|
extension iTunesApplication {
|
||||||
var title: String {
|
var title: String {
|
||||||
@@ -307,16 +310,15 @@ extension iTunesApplication {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@objc protocol VoxApplication {
|
@objc protocol VoxApplication {
|
||||||
@objc optional func playpause()
|
@objc optional func playpause()
|
||||||
@objc optional func next()
|
@objc optional func next()
|
||||||
@objc optional func previous()
|
@objc optional func previous()
|
||||||
@objc optional var track: String {get}
|
@objc optional var track: String { get }
|
||||||
@objc optional var artist: String {get}
|
@objc optional var artist: String { get }
|
||||||
}
|
}
|
||||||
extension SBApplication: VoxApplication{}
|
|
||||||
|
extension SBApplication: VoxApplication {}
|
||||||
|
|
||||||
extension VoxApplication {
|
extension VoxApplication {
|
||||||
var title: String {
|
var title: String {
|
||||||
@@ -324,7 +326,6 @@ extension VoxApplication {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@objc public protocol SBObjectProtocol: NSObjectProtocol {
|
@objc public protocol SBObjectProtocol: NSObjectProtocol {
|
||||||
func get() -> Any!
|
func get() -> Any!
|
||||||
}
|
}
|
||||||
@@ -338,6 +339,7 @@ extension VoxApplication {
|
|||||||
@objc optional func windows() -> SBElementArray
|
@objc optional func windows() -> SBElementArray
|
||||||
@objc optional func doJavaScript(_ x: String!, in in_: Any!) -> Any // Applies a string of JavaScript code to a document.
|
@objc optional func doJavaScript(_ x: String!, in in_: Any!) -> Any // Applies a string of JavaScript code to a document.
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SBApplication: SafariApplication {}
|
extension SBApplication: SafariApplication {}
|
||||||
|
|
||||||
@objc public protocol SafariWindow: SBObjectProtocol {
|
@objc public protocol SafariWindow: SBObjectProtocol {
|
||||||
@@ -346,36 +348,39 @@ extension SBApplication: SafariApplication {}
|
|||||||
// @objc optional var document: SafariDocument { get } // The document whose contents are displayed in the window.
|
// @objc optional var document: SafariDocument { get } // The document whose contents are displayed in the window.
|
||||||
// @objc optional func setCurrentTab(_ currentTab: SafariTab!) // The current tab.
|
// @objc optional func setCurrentTab(_ currentTab: SafariTab!) // The current tab.
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SBObject: SafariWindow {}
|
extension SBObject: SafariWindow {}
|
||||||
|
|
||||||
//@objc public protocol SafariDocument: SBObjectProtocol {
|
// @objc public protocol SafariDocument: SBObjectProtocol {
|
||||||
// @objc optional var name: String { get } // Its name.
|
// @objc optional var name: String { get } // Its name.
|
||||||
// @objc optional var URL: String { get } // The current URL of the document.
|
// @objc optional var URL: String { get } // The current URL of the document.
|
||||||
//}
|
// }
|
||||||
//extension SBObject: SafariDocument {}
|
// extension SBObject: SafariDocument {}
|
||||||
|
|
||||||
@objc public protocol SafariTab: SBObjectProtocol {
|
@objc public protocol SafariTab: SBObjectProtocol {
|
||||||
@objc optional var URL: String { get } // The current URL of the tab.
|
@objc optional var URL: String { get } // The current URL of the tab.
|
||||||
@objc optional var name: String { get } // The name of the tab.
|
@objc optional var name: String { get } // The name of the tab.
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SBObject: SafariTab {}
|
extension SBObject: SafariTab {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@objc public protocol GoogleChromeApplication: SBApplicationProtocol {
|
@objc public protocol GoogleChromeApplication: SBApplicationProtocol {
|
||||||
@objc optional func windows() -> SBElementArray
|
@objc optional func windows() -> SBElementArray
|
||||||
@objc optional func executeJavaScript(javascript: String!) -> Any // Applies a string of JavaScript code to a document. //, id: Any!
|
@objc optional func executeJavaScript(javascript: String!) -> Any // Applies a string of JavaScript code to a document. //, id: Any!
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SBApplication: GoogleChromeApplication {}
|
extension SBApplication: GoogleChromeApplication {}
|
||||||
|
|
||||||
@objc public protocol GoogleChromeWindow: SBObjectProtocol {
|
@objc public protocol GoogleChromeWindow: SBObjectProtocol {
|
||||||
@objc optional var name: String { get } // The title of the window.
|
@objc optional var name: String { get } // The title of the window.
|
||||||
@objc optional func tabs() -> SBElementArray
|
@objc optional func tabs() -> SBElementArray
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SBObject: GoogleChromeWindow {}
|
extension SBObject: GoogleChromeWindow {}
|
||||||
|
|
||||||
@objc public protocol GoogleChromeTab: SBObjectProtocol {
|
@objc public protocol GoogleChromeTab: SBObjectProtocol {
|
||||||
@objc optional var URL: String { get } // The current URL of the tab.
|
@objc optional var URL: String { get } // The current URL of the tab.
|
||||||
@objc optional var title: String { get } // The name of the tab.
|
@objc optional var title: String { get } // The name of the tab.
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SBObject: GoogleChromeTab {}
|
extension SBObject: GoogleChromeTab {}
|
||||||
|
|||||||
@@ -19,35 +19,35 @@ class NightShiftBarItem: CustomButtonTouchBarItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var isNightShiftEnabled: Bool {
|
private var isNightShiftEnabled: Bool {
|
||||||
return self.blueLightStatus.enabled.boolValue
|
return blueLightStatus.enabled.boolValue
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setNightShift(state: Bool) {
|
private func setNightShift(state: Bool) {
|
||||||
self.nsclient.setEnabled(state)
|
nsclient.setEnabled(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(identifier: NSTouchBarItem.Identifier) {
|
init(identifier: NSTouchBarItem.Identifier) {
|
||||||
super.init(identifier: identifier, title: "")
|
super.init(identifier: identifier, title: "")
|
||||||
self.isBordered = false
|
isBordered = false
|
||||||
self.setWidth(value: 28)
|
setWidth(value: 28)
|
||||||
|
|
||||||
self.tapClosure = { [weak self] in self?.nightShiftAction() }
|
tapClosure = { [weak self] in self?.nightShiftAction() }
|
||||||
|
|
||||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||||
|
|
||||||
self.refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func nightShiftAction() {
|
func nightShiftAction() {
|
||||||
self.setNightShift(state: !self.isNightShiftEnabled)
|
setNightShift(state: !isNightShiftEnabled)
|
||||||
self.refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func refresh() {
|
@objc func refresh() {
|
||||||
self.image = isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff")
|
image = isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
//
|
||||||
|
// PomodoroBarItem.swift
|
||||||
|
// MTMR
|
||||||
|
//
|
||||||
|
// Created by Daniel Apatin on 10.05.2018.
|
||||||
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class PomodoroBarItem: CustomButtonTouchBarItem, Widget {
|
||||||
|
static let identifier = "com.toxblh.mtmr.pomodoro."
|
||||||
|
static let name = "pomodoro"
|
||||||
|
static let decoder: ParametersDecoder = { decoder in
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case workTime
|
||||||
|
case restTime
|
||||||
|
}
|
||||||
|
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime)
|
||||||
|
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime)
|
||||||
|
|
||||||
|
return (
|
||||||
|
item: .pomodoro(workTime: workTime ?? 1500.00, restTime: restTime ?? 300),
|
||||||
|
action: .none,
|
||||||
|
longAction: .none,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TimeTypes {
|
||||||
|
case work
|
||||||
|
case rest
|
||||||
|
case none
|
||||||
|
}
|
||||||
|
private let defaultTitle = "🍅"
|
||||||
|
private let workTime: TimeInterval
|
||||||
|
private let restTime: TimeInterval
|
||||||
|
private var typeTime: TimeTypes = .none
|
||||||
|
private var timer: DispatchSourceTimer?
|
||||||
|
|
||||||
|
private var timeLeft: Int = 0
|
||||||
|
private var timeLeftString: String {
|
||||||
|
return String(format: "%.2i:%.2i", timeLeft / 60, timeLeft % 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(identifier: NSTouchBarItem.Identifier, workTime: TimeInterval, restTime: TimeInterval) {
|
||||||
|
self.workTime = workTime
|
||||||
|
self.restTime = restTime
|
||||||
|
super.init(identifier: identifier, title: defaultTitle)
|
||||||
|
tapClosure = { [weak self] in self?.startStopWork() }
|
||||||
|
longTapClosure = { [weak self] in self?.startStopRest() }
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder _: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
timer?.cancel()
|
||||||
|
timer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func startStopWork() {
|
||||||
|
typeTime = .work
|
||||||
|
startStopTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func startStopRest() {
|
||||||
|
typeTime = .rest
|
||||||
|
startStopTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func startStopTimer() {
|
||||||
|
timer == nil ? start() : reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func start() {
|
||||||
|
timeLeft = Int(typeTime == .work ? workTime : restTime)
|
||||||
|
let queue: DispatchQueue = DispatchQueue(label: "Timer")
|
||||||
|
timer = DispatchSource.makeTimerSource(flags: .strict, queue: queue)
|
||||||
|
timer?.schedule(deadline: .now(), repeating: .seconds(1), leeway: .never)
|
||||||
|
timer?.setEventHandler(handler: tick)
|
||||||
|
timer?.resume()
|
||||||
|
|
||||||
|
NSSound.beep()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func finish() {
|
||||||
|
if typeTime != .none {
|
||||||
|
sendNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func reset() {
|
||||||
|
typeTime = .none
|
||||||
|
timer?.cancel()
|
||||||
|
timer = nil
|
||||||
|
title = defaultTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
private func tick() {
|
||||||
|
timeLeft -= 1
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if self.timeLeft >= 0 {
|
||||||
|
self.title = self.defaultTitle + " " + self.timeLeftString
|
||||||
|
} else {
|
||||||
|
self.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func sendNotification() {
|
||||||
|
let notification: NSUserNotification = NSUserNotification()
|
||||||
|
notification.title = "Pomodoro"
|
||||||
|
notification.informativeText = typeTime == .work ? "it's time to rest your mind!" : "It's time to work!"
|
||||||
|
notification.soundName = "Submarine"
|
||||||
|
NSUserNotificationCenter.default.deliver(notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,16 +8,15 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
|
|||||||
dateFormatter.setLocalizedDateFormatFromTemplate(formatTemplate)
|
dateFormatter.setLocalizedDateFormatFromTemplate(formatTemplate)
|
||||||
super.init(identifier: identifier, title: " ")
|
super.init(identifier: identifier, title: " ")
|
||||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
|
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
|
||||||
self.isBordered = false
|
isBordered = false
|
||||||
updateTime()
|
updateTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func updateTime() {
|
@objc func updateTime() {
|
||||||
self.title = self.dateFormatter.string(from: Date())
|
title = dateFormatter.string(from: Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Cocoa
|
|
||||||
import AppKit
|
import AppKit
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
import Cocoa
|
||||||
import CoreAudio
|
import CoreAudio
|
||||||
|
|
||||||
class VolumeViewController: NSCustomTouchBarItem {
|
class VolumeViewController: NSCustomTouchBarItem {
|
||||||
@@ -12,11 +12,12 @@ class VolumeViewController: NSCustomTouchBarItem {
|
|||||||
var forPropertyAddress = AudioObjectPropertyAddress(
|
var forPropertyAddress = AudioObjectPropertyAddress(
|
||||||
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
|
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
|
||||||
mScope: kAudioDevicePropertyScopeOutput,
|
mScope: kAudioDevicePropertyScopeOutput,
|
||||||
mElement: kAudioObjectPropertyElementMaster)
|
mElement: kAudioObjectPropertyElementMaster
|
||||||
|
)
|
||||||
|
|
||||||
AudioObjectAddPropertyListenerBlock(defaultDeviceID, &forPropertyAddress, nil, audioObjectPropertyListenerBlock)
|
AudioObjectAddPropertyListenerBlock(defaultDeviceID, &forPropertyAddress, nil, audioObjectPropertyListenerBlock)
|
||||||
|
|
||||||
if (image == nil) {
|
if image == nil {
|
||||||
sliderItem = CustomSlider()
|
sliderItem = CustomSlider()
|
||||||
} else {
|
} else {
|
||||||
sliderItem = CustomSlider(knob: image!)
|
sliderItem = CustomSlider(knob: image!)
|
||||||
@@ -25,18 +26,18 @@ class VolumeViewController: NSCustomTouchBarItem {
|
|||||||
sliderItem.action = #selector(VolumeViewController.sliderValueChanged(_:))
|
sliderItem.action = #selector(VolumeViewController.sliderValueChanged(_:))
|
||||||
sliderItem.minValue = 0.0
|
sliderItem.minValue = 0.0
|
||||||
sliderItem.maxValue = 100.0
|
sliderItem.maxValue = 100.0
|
||||||
sliderItem.floatValue = getInputGain()*100
|
sliderItem.floatValue = getInputGain() * 100
|
||||||
|
|
||||||
self.view = sliderItem
|
view = sliderItem
|
||||||
}
|
}
|
||||||
|
|
||||||
func audioObjectPropertyListenerBlock (numberAddresses: UInt32, addresses: UnsafePointer<AudioObjectPropertyAddress>) {
|
func audioObjectPropertyListenerBlock(numberAddresses _: UInt32, addresses _: UnsafePointer<AudioObjectPropertyAddress>) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.sliderItem.floatValue = self.getInputGain() * 100
|
self.sliderItem.floatValue = self.getInputGain() * 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ class VolumeViewController: NSCustomTouchBarItem {
|
|||||||
|
|
||||||
@objc func sliderValueChanged(_ sender: Any) {
|
@objc func sliderValueChanged(_ sender: Any) {
|
||||||
if let sliderItem = sender as? NSSlider {
|
if let sliderItem = sender as? NSSlider {
|
||||||
_ = setInputGain(Float32(sliderItem.intValue)/100.0)
|
_ = setInputGain(Float32(sliderItem.intValue) / 100.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,9 +77,9 @@ class VolumeViewController: NSCustomTouchBarItem {
|
|||||||
var inputVolume: Float32 = volume
|
var inputVolume: Float32 = volume
|
||||||
|
|
||||||
if inputVolume == 0.0 {
|
if inputVolume == 0.0 {
|
||||||
_ = setMute( mute: 1)
|
_ = setMute(mute: 1)
|
||||||
} else {
|
} else {
|
||||||
_ = setMute( mute: 0)
|
_ = setMute(mute: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
let size: UInt32 = UInt32(MemoryLayout.size(ofValue: inputVolume))
|
let size: UInt32 = UInt32(MemoryLayout.size(ofValue: inputVolume))
|
||||||
@@ -89,7 +90,7 @@ class VolumeViewController: NSCustomTouchBarItem {
|
|||||||
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &inputVolume)
|
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &inputVolume)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setMute( mute: Int) -> OSStatus {
|
private func setMute(mute: Int) -> OSStatus {
|
||||||
var muteVal: Int = mute
|
var muteVal: Int = mute
|
||||||
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
|
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
|
||||||
address.mSelector = AudioObjectPropertySelector(kAudioDevicePropertyMute)
|
address.mSelector = AudioObjectPropertySelector(kAudioDevicePropertyMute)
|
||||||
@@ -99,4 +100,3 @@ class VolumeViewController: NSCustomTouchBarItem {
|
|||||||
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &muteVal)
|
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &muteVal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
|||||||
private let iconsText = ["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 iconsSource: Dictionary<String, String>
|
||||||
|
|
||||||
private var manager:CLLocationManager!
|
private var manager: CLLocationManager!
|
||||||
|
|
||||||
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text") {
|
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text") {
|
||||||
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||||
@@ -51,7 +51,7 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !CLLocationManager.locationServicesEnabled() {
|
if !CLLocationManager.locationServicesEnabled() {
|
||||||
print("Location services not enabled");
|
print("Location services not enabled")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,24 +69,24 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
|||||||
manager.startUpdatingLocation()
|
manager.startUpdatingLocation()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func updateWeather() {
|
@objc func updateWeather() {
|
||||||
if self.location != nil {
|
if 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 urlRequest = URLRequest(url: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)&units=\(units)&appid=\(api_key)")!)
|
||||||
|
|
||||||
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
|
let task = URLSession.shared.dataTask(with: urlRequest) { data, _, error in
|
||||||
|
|
||||||
if error == nil {
|
if error == nil {
|
||||||
do {
|
do {
|
||||||
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
|
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
|
||||||
// print(json)
|
// print(json)
|
||||||
var temperature: Int!
|
var temperature: Int!
|
||||||
var condition_icon = ""
|
var condition_icon = ""
|
||||||
|
|
||||||
if let main = json["main"] as? [String : AnyObject] {
|
if let main = json["main"] as? [String: AnyObject] {
|
||||||
if let temp = main["temp"] as? Double {
|
if let temp = main["temp"] as? Double {
|
||||||
temperature = Int(temp)
|
temperature = Int(temp)
|
||||||
}
|
}
|
||||||
@@ -115,25 +115,24 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setWeather(text: String) {
|
func setWeather(text: String) {
|
||||||
self.title = text
|
title = text
|
||||||
}
|
}
|
||||||
|
|
||||||
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
||||||
let lastLocation = locations.last!
|
let lastLocation = locations.last!
|
||||||
self.location = lastLocation
|
location = lastLocation
|
||||||
if prev_location == nil {
|
if prev_location == nil {
|
||||||
updateWeather()
|
updateWeather()
|
||||||
}
|
}
|
||||||
prev_location = lastLocation
|
prev_location = lastLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
|
func locationManager(_: CLLocationManager, didFailWithError error: Error) {
|
||||||
print(error);
|
print(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
|
func locationManager(_: CLLocationManager, didChangeAuthorization _: CLAuthorizationStatus) {
|
||||||
// print("inside didChangeAuthorization ");
|
// print("inside didChangeAuthorization ");
|
||||||
updateWeather()
|
updateWeather()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// WidgetProtocol.swift
|
||||||
|
// MTMR
|
||||||
|
//
|
||||||
|
// Created by Anton Palgunov on 20/10/2018.
|
||||||
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
protocol Widget {
|
||||||
|
static var name: String { get }
|
||||||
|
static var identifier: String { get }
|
||||||
|
static var decoder: ParametersDecoder { get }
|
||||||
|
}
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
|
<p align="center"><img src="Resources/logo.png" width="120"></p>
|
||||||
<p align="center">
|
|
||||||
<img src="Resources/logo.png" width="120">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# My TouchBar. My rules
|
# My TouchBar. My rules
|
||||||
|
|
||||||
@@ -16,58 +13,26 @@
|
|||||||
|
|
||||||
**MTMR** Community:
|
**MTMR** Community:
|
||||||
[<img height="24px" src="https://github.com/discourse/DiscourseMobile/raw/master/icon.png" /> Discourse](https://forum.mtmr.app)
|
[<img height="24px" src="https://github.com/discourse/DiscourseMobile/raw/master/icon.png" /> Discourse](https://forum.mtmr.app)
|
||||||
|
[<img height="24px" src="https://camo.githubusercontent.com/88f53948f291c54736bf08f5fd7b037a848dfc62/68747470733a2f2f646973636f72646170702e636f6d2f6173736574732f30376463613830613130326434313439653937333664346231363263666636662e69636f"> Discord](https://discord.gg/CmNcDuQ)
|
||||||
[<img height="24px" src="https://telegram.org/img/t_logo.png" /> Telegram](https://t.me/joinchat/AmVYGg8vW38c13_3MxdE_g)
|
[<img height="24px" src="https://telegram.org/img/t_logo.png" /> Telegram](https://t.me/joinchat/AmVYGg8vW38c13_3MxdE_g)
|
||||||
|
|
||||||
|
<a href="https://www.paypal.me/toxblh/10" title="Donate via Paypal"><img height="32px" src="Resources/support_paypal.svg" alt="PayPal donate button" /></a>
|
||||||
<a href="https://www.buymeacoffee.com/toxblh" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" height="32px" ></a>
|
<a href="https://www.buymeacoffee.com/toxblh" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" height="32px" ></a>
|
||||||
<a href="https://www.patreon.com/bePatron?u=9900748"><img height="32px" src="https://c5.patreon.com/external/logo/become_a_patron_button.png" srcset="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png 2x"></a>
|
<a href="https://www.patreon.com/bePatron?u=9900748"><img height="32px" src="https://c5.patreon.com/external/logo/become_a_patron_button.png" srcset="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png 2x"></a>
|
||||||
[<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/854859-e299e139ab7f79855c0bc589f10b0ec6-medium_jpg.jpg?buster=1453074480" height="32px" /> Become a backer](https://opencollective.com/MTMR#backer)
|
[<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/854859-e299e139ab7f79855c0bc589f10b0ec6-medium_jpg.jpg?buster=1453074480" height="32px" /> Become a backer](https://opencollective.com/MTMR#backer)
|
||||||
[<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/854859-e299e139ab7f79855c0bc589f10b0ec6-medium_jpg.jpg?buster=1453074480" height="32px" /> Become a sponsor](https://opencollective.com/MTMR#sponsor)
|
[<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/854859-e299e139ab7f79855c0bc589f10b0ec6-medium_jpg.jpg?buster=1453074480" height="32px" /> Become a sponsor](https://opencollective.com/MTMR#sponsor)
|
||||||
|
|
||||||
|
|
||||||
My the idea is to create the program like a platform for plugins for customization TouchBar. I very like BTT and a full custom TouchBar (my [BTT preset](https://github.com/Toxblh/btt-touchbar-preset)). And I want to create it. And it's my the first Swift project for MacOS :)
|
My idea is to create a platform for creating plugins to customize the TouchBar. I very much like BTT and having a full custom TouchBar (my [BTT preset](https://github.com/Toxblh/btt-touchbar-preset)), and I wanted to create it. It's my first Swift project for MacOS :)
|
||||||
|
|
||||||
### Roadmap
|
|
||||||
- [x] Create the first prototype with TouchBar in Storyboard
|
|
||||||
- [x] Put in stripe menu on startup the application
|
|
||||||
- [x] Find how to simulate real buttons like brightness, volume, night shift and etc.
|
|
||||||
- [x] Time in touchbar!
|
|
||||||
- [x] First the weather plugin
|
|
||||||
- [x] Find how to open full-screen TouchBar without the cross and stripe menu
|
|
||||||
- [x] Find how to add haptic feedback
|
|
||||||
- [x] Add icon and menu in StatusBar
|
|
||||||
- [x] Hide from Dock
|
|
||||||
- [x] Status menu: "preferences", "quit"
|
|
||||||
- [x] JSON or another approch for save preset, maybe in `~/Library/Application Support/MTMR/`
|
|
||||||
- [x] Custom buttons size, actions by click
|
|
||||||
- [x] Layout: [always left, NSSliderView for center, always right]
|
|
||||||
- [x] System for autoupdate (https://sparkle-project.org/)
|
|
||||||
- [ ] 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
|
|
||||||
|
|
||||||
Settings:
|
|
||||||
- [ ] Interface for plugins and export like presets
|
|
||||||
- [x] Startup at login
|
|
||||||
- [ ] Show on/off in Dock
|
|
||||||
- [ ] Show on/off in StatusBar
|
|
||||||
- [ ] On/off Haptic Feedback
|
|
||||||
|
|
||||||
Maybe:
|
|
||||||
- [ ] Refactoring the application on packages (AppleScript, JavaScript? and Swift?)
|
|
||||||
|
|
||||||
|
#### Share your presets [here](https://github.com/Toxblh/MTMR-presets)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
- Download last [release](https://github.com/Toxblh/MTMR/releases)
|
- Download lastest [release](https://github.com/Toxblh/MTMR/releases) from github
|
||||||
- Or via Homebrew `brew cask install mtmr`
|
- 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:
|
## Built-in button types:
|
||||||
|
> Buttons
|
||||||
- escape
|
- escape
|
||||||
- exitTouchbar
|
- exitTouchbar
|
||||||
- brightnessUp
|
- brightnessUp
|
||||||
@@ -77,9 +42,6 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
|||||||
- volumeDown
|
- volumeDown
|
||||||
- volumeUp
|
- volumeUp
|
||||||
- mute
|
- mute
|
||||||
- dock (half-long click to open app, full-long click to kill app)
|
|
||||||
- nightShift
|
|
||||||
- dnd (Dont disturb)
|
|
||||||
|
|
||||||
> Native Plugins
|
> Native Plugins
|
||||||
- battery
|
- battery
|
||||||
@@ -87,6 +49,10 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
|||||||
- weather
|
- weather
|
||||||
- inputsource
|
- inputsource
|
||||||
- music (tap for pause, longTap for next)
|
- music (tap for pause, longTap for next)
|
||||||
|
- dock (half-long click to open app, full-long click to kill app)
|
||||||
|
- nightShift
|
||||||
|
- dnd (Don't disturb)
|
||||||
|
- pomodoro
|
||||||
|
|
||||||
> Media Keys
|
> Media Keys
|
||||||
- previous
|
- previous
|
||||||
@@ -106,7 +72,7 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
|||||||
- brightness
|
- brightness
|
||||||
- volume
|
- volume
|
||||||
|
|
||||||
### You can also make a custom buttons using these types
|
### You can also make custom buttons using these types
|
||||||
- `staticButton`
|
- `staticButton`
|
||||||
```json
|
```json
|
||||||
"type": "staticButton",
|
"type": "staticButton",
|
||||||
@@ -139,39 +105,59 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
|||||||
"align": "center",
|
"align": "center",
|
||||||
"bordered": true,
|
"bordered": true,
|
||||||
"title": "stats",
|
"title": "stats",
|
||||||
"items": [{ button }, {button}, ...]
|
"items": [
|
||||||
|
{ "type": "play" }, { "type": "mute" }, ...]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To close a group, use the button:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"type": "close",
|
||||||
|
"width": 64
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
## Native plugins
|
## Native plugins
|
||||||
- `weather`
|
#### `weather`
|
||||||
> Provider: https://openweathermap.org Need allowance location service
|
> Provider: https://openweathermap.org Need allowance location service
|
||||||
```js
|
```js
|
||||||
"type": "weather",
|
"type": "weather",
|
||||||
"refreshInterval": 600,
|
"refreshInterval": 600, // in seconds
|
||||||
"units": "metric", // or imperial
|
"units": "metric", // or imperial
|
||||||
"icon_type": "text" // or images
|
"icon_type": "text" // or images
|
||||||
"api_key": "" // you can get the key on openweather
|
"api_key": "" // you can get the key on openweather
|
||||||
```
|
```
|
||||||
|
|
||||||
- `currency`
|
#### `currency`
|
||||||
> Provider: https://coinbase.com
|
> Provider: https://coinbase.com
|
||||||
```js
|
```js
|
||||||
"type": "currency",
|
"type": "currency",
|
||||||
"refreshInterval": 600,
|
"refreshInterval": 600, // in seconds
|
||||||
"align": "right",
|
"align": "right",
|
||||||
"from": "BTC",
|
"from": "BTC",
|
||||||
"to": "USD",
|
"to": "USD",
|
||||||
|
"full": true // £‣1.29$
|
||||||
```
|
```
|
||||||
|
|
||||||
- `music`
|
#### `music`
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
"type": "music",
|
"type": "music",
|
||||||
"align": "center",
|
"align": "center",
|
||||||
"width": 80,
|
"width": 80,
|
||||||
"bordered": false,
|
"bordered": false,
|
||||||
"refreshInterval": 2,
|
"refreshInterval": 2, // in seconds
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `pomodoro`
|
||||||
|
> Pomodoro plugin. One click to start the work timer, longclick to start the rest timer. Click in progress for reset.
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
"type": "pomodoro",
|
||||||
|
"workTime": 1200, // set time work in seconds. Default 1500 (25 min)
|
||||||
|
"restTime": 600, // set time rest in seconds. Default 300 (5 min)
|
||||||
},
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -213,7 +199,7 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
|||||||
```
|
```
|
||||||
|
|
||||||
## LongActions
|
## LongActions
|
||||||
This then you want to use longPress for some operations is will the same values like for Actions but different additional parameters, example:
|
If you want to longPress for some operations, it is similar to the configuration for Actions but with additional parameters, for example:
|
||||||
```js
|
```js
|
||||||
"longAction": "hidKey",
|
"longAction": "hidKey",
|
||||||
"longKeycode": 53,
|
"longKeycode": 53,
|
||||||
@@ -228,14 +214,19 @@ This then you want to use longPress for some operations is will the same values
|
|||||||
|
|
||||||
## Additional parameters:
|
## Additional parameters:
|
||||||
|
|
||||||
- `width` allow to restrict how much room a particular button will take
|
- `width` restrict how much room a particular button will take
|
||||||
```json
|
```json
|
||||||
"width": 34
|
"width": 34
|
||||||
```
|
```
|
||||||
|
|
||||||
- `align` can stick the item to the side. default is center
|
- `align` can stick the item to the side. default is center
|
||||||
```js
|
```js
|
||||||
"align": "left" //or "right" or "center"
|
"align": "left" // "left", "right" or "center"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `bordered` you can do button without border
|
||||||
|
```js
|
||||||
|
"bordered": "false" // "true" or "false"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example configuration:
|
## Example configuration:
|
||||||
@@ -296,17 +287,50 @@ This then you want to use longPress for some operations is will the same values
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Author's presets
|
### Roadmap
|
||||||
|
- [x] Create the first prototype with TouchBar in Storyboard
|
||||||
|
- [x] Put in stripe menu on startup the application
|
||||||
|
- [x] Find how to simulate real buttons like brightness, volume, night shift and etc.
|
||||||
|
- [x] Time in touchbar!
|
||||||
|
- [x] First the weather plugin
|
||||||
|
- [x] Find how to open full-screen TouchBar without the cross and stripe menu
|
||||||
|
- [x] Find how to add haptic feedback
|
||||||
|
- [x] Add icon and menu in StatusBar
|
||||||
|
- [x] Hide from Dock
|
||||||
|
- [x] Status menu: "preferences", "quit"
|
||||||
|
- [x] JSON or another approch for save preset, maybe in `~/Library/Application Support/MTMR/`
|
||||||
|
- [x] Custom buttons size, actions by click
|
||||||
|
- [x] Layout: [always left, NSSliderView for center, always right]
|
||||||
|
- [x] System for autoupdate (https://sparkle-project.org/)
|
||||||
|
- [ ] 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
|
||||||
|
|
||||||
|
Settings:
|
||||||
|
- [ ] Interface for plugins and export like presets
|
||||||
|
- [x] Startup at login
|
||||||
|
- [ ] Show on/off in Dock
|
||||||
|
- [ ] Show on/off in StatusBar
|
||||||
|
- [ ] On/off Haptic Feedback
|
||||||
|
|
||||||
|
Maybe:
|
||||||
|
- [ ] Refactoring the application on packages (AppleScript, JavaScript? and Swift?)
|
||||||
|
|
||||||
|
### Author's presets
|
||||||
[@Toxblh preset](Resources/toxblh.json)
|
[@Toxblh preset](Resources/toxblh.json)
|
||||||
|
|
||||||
[@ReDetection preset](Resources/ReDetection.json)
|
[@ReDetection preset](Resources/ReDetection.json)
|
||||||
|
|
||||||
### User's presets
|
### User's presets
|
||||||
|
|
||||||
[@luongvo209 preset](Resources/luongvo209.json)
|
[@luongvo209 preset](Resources/luongvo209.json)
|
||||||

|

|
||||||
|
|
||||||
|
[aadi_vs_anand preset](Resources/aadi_vs_anand.json)
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
Built by [@Toxblh](https://patreon.com/toxblh) and [@ReDetection](http://patreon.com/ReDetection).
|
Built by [@Toxblh](https://patreon.com/toxblh) and [@ReDetection](http://patreon.com/ReDetection).
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
[
|
||||||
|
{ "type": "escape", "align": "left" },
|
||||||
|
// --- iTunes ---
|
||||||
|
{
|
||||||
|
"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=="
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// --- VOX ---
|
||||||
|
// {
|
||||||
|
// "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": "dock", "width": 200, "align": "left"},
|
||||||
|
//{ "type": "button", "title": "alfred"},
|
||||||
|
//{ "type": "volume", "width": 160, "align": "right"},
|
||||||
|
{
|
||||||
|
"type": "group",
|
||||||
|
"align": "right",
|
||||||
|
"bordered": false,
|
||||||
|
"title": "Media",
|
||||||
|
"items": [
|
||||||
|
{"type": "close", "bordered": false, "align": "left"},
|
||||||
|
{
|
||||||
|
"type": "brightnessDown",
|
||||||
|
"bordered": false,
|
||||||
|
"align": "left",
|
||||||
|
"width": 36
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "brightness",
|
||||||
|
"width": 200,
|
||||||
|
"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",
|
||||||
|
"bordered": false,
|
||||||
|
"align": "left",
|
||||||
|
"width": 36
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "volumeDown",
|
||||||
|
"bordered": false,
|
||||||
|
"align": "left",
|
||||||
|
"width": 36,
|
||||||
|
},
|
||||||
|
{ "type": "volume", "width": 200, "align": "left"},
|
||||||
|
{
|
||||||
|
"type": "volumeUp",
|
||||||
|
"bordered": false,
|
||||||
|
"align": "left",
|
||||||
|
"width": 36
|
||||||
|
},
|
||||||
|
{"type": "previous", "bordered": false, "align": "center"},
|
||||||
|
//{"type": "play", "bordered": false, "align": "center"},
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "appleScriptTitledButton",
|
||||||
|
"align": "center",
|
||||||
|
"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\rpause\rend if\rend tell\rend if\r"
|
||||||
|
},
|
||||||
|
"refreshInterval": 0.0001,
|
||||||
|
"image": {
|
||||||
|
"base64":
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAYUExURUdwTB3WXx3UXh3VXx7XYBkXFRpVLRyURmIaeAQAAAAEdFJOUwDDO3fSqUUkAAABbklEQVRIx61VbW6DMAztoAeYNA7ApB6gkzhAWS24wIAL0HABaK6/pHFNEhy8TXu/kPzkPD/8cTj8K7KPAqB+K5NhQPCUrABCXe7HOUYYZxgVRLiG8RfY4DUgFFtC7cffAfZTFBwBdhWEKfgEq4ocEjgj4ZQifO6/QG9kkETp1dDeVWfRKx3XYSW0LoqY5kCElXDrQkyeCCuh6WL0M4nIWQIyzqixdfKU1koFDKvyCA8NJMzU4xiD+b4kfHRpsIyKc6hBwjVptFHVY51EMAINNDFGJITKDNQcdpX74Hz0CQ3rY5qwMp4EIxrlafzrsYZ2Veb0DkRgfNCUok4Y1fqEijfyi2b8RE9beWqa48Y/uvCNMcH9btfUi+/CGLR1vhL6Zz9N/vBlaCU+7lwY/cmJ67Ryen/2tj23PLqJBodZH8vgj544vOL4pxfI5acrSFxi8hrkU9TSKr78ZpnL50A8KPJJEo+afBblwyqf5j/iGys5j6ScrST2AAAAAElFTkSuQmCC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{"type": "next", "bordered": false, "align": "center"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ "type": "inputsource", "align": "right", },
|
||||||
|
{ "type": "mute", "bordered": false, "align": "right"},
|
||||||
|
{ "type": "dnd", "align": "right"},
|
||||||
|
{ "type": "nightShift", "align": "right"},
|
||||||
|
{ "type": "sleep", "align": "right", "bordered": false},
|
||||||
|
{ "type": "currency",
|
||||||
|
"refreshInterval": 600, // in seconds
|
||||||
|
"bordered": false,
|
||||||
|
"align": "right",
|
||||||
|
"from": "USD",
|
||||||
|
"to": "HKD",},
|
||||||
|
{
|
||||||
|
"type": "pomodoro",
|
||||||
|
"bordered": false,
|
||||||
|
"align": "right",
|
||||||
|
"workTime": 1800, // set time work in seconds. Default 1500 (25 min)
|
||||||
|
"restTime": 600, // set time rest in seconds. Default 300 (5 min)
|
||||||
|
},
|
||||||
|
{ "type": "weather", "refreshInterval": 60, "units": "metric", "align": "right", "bordered": false, "icon_type": "images", "api_key": "84645702688e83a35e2549ca77f73369"},
|
||||||
|
{ "type": "battery", "align": "right", "bordered": false },
|
||||||
|
{ "type": "timeButton", "align": "right", "formatTemplate": "E MMM d h:mm a", "bordered": false }
|
||||||
|
]
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,90 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 250 49" style="enable-background:new 0 0 250 49;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#189BD7;}
|
||||||
|
.st1{enable-background:new ;}
|
||||||
|
.st2{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<title>support</title>
|
||||||
|
<path class="st0" d="M6.6,0h236.8c3.7,0,6.6,3,6.6,6.6v35.8c0,3.7-3,6.6-6.6,6.6c0,0,0,0,0,0H6.6C3,49,0,46,0,42.4c0,0,0,0,0,0V6.6
|
||||||
|
C0,3,3,0,6.6,0z"/>
|
||||||
|
<g class="st1">
|
||||||
|
<path class="st2" d="M25.4,18.8c-0.6-0.8-1.6-1.4-2.9-1.4c-1.4,0-3,0.8-3,2.6c0,1.8,1.5,2.2,3,2.7c2,0.7,4.1,1.3,4.1,4.1
|
||||||
|
c0,2.8-2.3,4-4.6,4c-1.7,0-3.4-0.7-4.4-2.1l1.2-0.9c0.6,1,1.7,1.8,3.2,1.8c1.4,0,3.1-0.9,3.1-2.7c0-2-1.6-2.4-3.4-3
|
||||||
|
c-1.9-0.6-3.8-1.4-3.8-3.9c0-2.7,2.4-3.9,4.5-3.9c1.9,0,3.3,0.8,3.9,1.7L25.4,18.8z"/>
|
||||||
|
<path class="st2" d="M36.4,30.6c0-0.5-0.1-1.2-0.1-1.6h0c-0.5,1.1-1.9,1.8-3.2,1.8c-2.3,0-3.5-1.5-3.5-3.8v-5.7h1.3v5.1
|
||||||
|
c0,1.9,0.6,3.2,2.5,3.2c1.4,0,2.8-1.1,2.8-3.6v-4.7h1.3v7.2c0,0.5,0,1.4,0.1,2H36.4z"/>
|
||||||
|
<path class="st2" d="M42.5,35.3h-1.3v-14h1.3v1.6h0.1c0.8-1.2,2.2-1.8,3.5-1.8c2.9,0,4.8,2.1,4.8,4.9c0,2.7-1.9,4.9-4.8,4.9
|
||||||
|
c-1.3,0-2.7-0.7-3.5-1.8h-0.1V35.3z M45.9,22.2c-2.1,0-3.6,1.6-3.6,3.7c0,2.1,1.5,3.7,3.6,3.7c2.2,0,3.5-1.7,3.5-3.7
|
||||||
|
C49.4,23.9,48.1,22.2,45.9,22.2z"/>
|
||||||
|
<path class="st2" d="M55,35.3h-1.3v-14H55v1.6h0.1c0.8-1.2,2.2-1.8,3.5-1.8c2.9,0,4.8,2.1,4.8,4.9c0,2.7-1.9,4.9-4.8,4.9
|
||||||
|
c-1.3,0-2.7-0.7-3.5-1.8H55V35.3z M58.4,22.2c-2.1,0-3.6,1.6-3.6,3.7c0,2.1,1.5,3.7,3.6,3.7c2.2,0,3.5-1.7,3.5-3.7
|
||||||
|
C61.9,23.9,60.6,22.2,58.4,22.2z"/>
|
||||||
|
<path class="st2" d="M70.4,30.8c-2.9,0-4.9-2.1-4.9-4.9c0-2.8,2.1-4.9,4.9-4.9c2.9,0,4.9,2.1,4.9,4.9
|
||||||
|
C75.4,28.7,73.3,30.8,70.4,30.8z M70.4,22.2c-2.1,0-3.5,1.7-3.5,3.7c0,2.1,1.4,3.7,3.5,3.7c2.2,0,3.5-1.6,3.5-3.7
|
||||||
|
C74,23.9,72.6,22.2,70.4,22.2z"/>
|
||||||
|
</g>
|
||||||
|
<g class="st1">
|
||||||
|
<path class="st2" d="M78.3,21.3h1.3c0,0.5,0.1,1.2,0.1,1.6h0c0.5-1.1,1.7-1.8,3-1.8c0.3,0,0.6,0,0.9,0.1l-0.2,1.3
|
||||||
|
c-0.2-0.1-0.6-0.1-0.9-0.1c-1.4,0-2.7,1-2.7,3.5v4.7h-1.3v-7.2C78.4,22.9,78.4,22,78.3,21.3z"/>
|
||||||
|
</g>
|
||||||
|
<g class="st1">
|
||||||
|
<path class="st2" d="M89.5,22.4h-2.6v5.4c0,1.4,0.6,1.7,1.4,1.7c0.4,0,0.8-0.1,1.2-0.3l0.1,1.2c-0.5,0.2-1,0.3-1.6,0.3
|
||||||
|
c-1,0-2.4-0.4-2.4-2.5v-5.8h-1.9v-1.1h1.9v-2.6h1.3v2.6h2.6V22.4z"/>
|
||||||
|
<path class="st2" d="M100.8,30.8c-2.9,0-4.9-2.1-4.9-4.9c0-2.8,2.1-4.9,4.9-4.9c2.9,0,4.9,2.1,4.9,4.9
|
||||||
|
C105.7,28.7,103.7,30.8,100.8,30.8z M100.8,22.2c-2.1,0-3.5,1.7-3.5,3.7c0,2.1,1.4,3.7,3.5,3.7c2.2,0,3.5-1.6,3.5-3.7
|
||||||
|
C104.3,23.9,102.9,22.2,100.8,22.2z"/>
|
||||||
|
<path class="st2" d="M109.8,21.3c0,0.5,0.1,1.2,0.1,1.6h0c0.5-1.1,1.9-1.8,3.2-1.8c2.3,0,3.5,1.5,3.5,3.8v5.7h-1.3v-5.1
|
||||||
|
c0-1.9-0.6-3.2-2.5-3.2c-1.4,0-2.8,1.1-2.8,3.6v4.7h-1.3v-7.2c0-0.5,0-1.4-0.1-2H109.8z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st2" d="M166,18.3c0.1,0,0.2,0,0.3,0h4.9c1.1-0.1,2.2,0.1,3.2,0.4c0.8,0.2,1.5,0.7,2,1.4c0.5,0.8,0.7,1.8,0.6,2.8
|
||||||
|
c-0.1,0.9-0.3,1.7-0.6,2.5c-0.3,0.8-0.8,1.5-1.4,2.1c-0.9,0.7-2,1.2-3.1,1.4c-1.1,0.1-2.2,0.1-3.3,0.1c-0.3,0-0.5,0.1-0.6,0.4
|
||||||
|
c-0.1,0.2-0.2,0.4-0.2,0.6c-0.2,1.2-0.4,2.4-0.6,3.6c-0.1,0.4-0.4,0.7-0.8,0.7h-2.8c-0.3,0-0.5-0.2-0.5-0.5c0,0,0,0,0-0.1
|
||||||
|
c0.8-4.9,1.5-9.8,2.3-14.6C165.4,18.7,165.7,18.4,166,18.3z M169.1,21.5c-0.1,0.1-0.2,0.3-0.2,0.4c-0.2,1.2-0.4,2.5-0.6,3.7h0.9
|
||||||
|
c0.8,0.1,1.6-0.1,2.3-0.4c0.5-0.3,0.9-0.8,1-1.4c0.2-0.5,0.2-1.1,0-1.6c-0.2-0.4-0.5-0.6-0.9-0.8c-0.7-0.2-1.3-0.2-2-0.2
|
||||||
|
C169.5,21.4,169.3,21.4,169.1,21.5L169.1,21.5z"/>
|
||||||
|
<path class="st2" d="M179.3,24.1c1-0.6,2.1-0.9,3.2-0.8c0.9,0,1.7,0.2,2.5,0.6c0.3,0.2,0.5,0.5,0.7,0.8c0.1-0.3,0.1-0.5,0.1-0.8
|
||||||
|
c0.1-0.2,0.3-0.4,0.5-0.4h2.7c0.3,0,0.5,0.2,0.5,0.5c0,0,0,0.1,0,0.1c-0.5,3.1-1,6.2-1.5,9.4c-0.1,0.4-0.4,0.7-0.8,0.7h-2.5
|
||||||
|
c-0.2,0-0.4-0.1-0.5-0.3c0-0.1,0-0.3,0-0.4c0-0.2,0.1-0.4,0.1-0.6c-0.6,0.6-1.3,1-2.1,1.3c-0.9,0.3-1.9,0.3-2.9,0.1
|
||||||
|
c-0.7-0.1-1.4-0.5-2-1c-0.6-0.6-1.1-1.5-1.2-2.4c-0.2-1.1-0.1-2.1,0.2-3.2C176.9,26.2,177.9,24.9,179.3,24.1z M182.6,26.2
|
||||||
|
c-0.6,0.1-1.2,0.3-1.6,0.7c-0.6,0.5-1,1.1-1.1,1.9c-0.1,0.6,0,1.3,0.3,1.8c0.3,0.4,0.7,0.7,1.2,0.8c0.8,0.2,1.7,0.1,2.4-0.3
|
||||||
|
c1.1-0.7,1.7-1.9,1.5-3.2c-0.1-0.6-0.5-1.2-1.1-1.4C183.6,26.2,183.1,26.2,182.6,26.2z"/>
|
||||||
|
<path class="st2" d="M191,23.7c0.1-0.1,0.3-0.2,0.5-0.2h2.6c0.2,0,0.4,0.1,0.6,0.2c0.1,0.2,0.2,0.4,0.3,0.6l1.5,5.1
|
||||||
|
c1.2-1.7,2.3-3.5,3.5-5.2c0.1-0.2,0.3-0.4,0.4-0.6c0.2-0.1,0.4-0.2,0.6-0.2c0.9,0,1.8,0,2.7,0c0.3,0,0.5,0.3,0.4,0.5
|
||||||
|
c0,0.1,0,0.2-0.1,0.3l-9.2,13.2c-0.1,0.2-0.4,0.4-0.7,0.4h-2.7c-0.2,0-0.3,0-0.4-0.1c-0.2-0.2-0.2-0.5-0.1-0.7l2.8-3.9
|
||||||
|
c0-0.1,0-0.1,0-0.2l-3-8.8C190.8,24,190.9,23.8,191,23.7z"/>
|
||||||
|
<path class="st2" d="M206.9,18.3c0.1,0,0.2,0,0.4,0h5.1c1-0.1,2.1,0.1,3.1,0.4c0.8,0.3,1.4,0.8,1.9,1.5c0.5,0.9,0.7,1.8,0.5,2.8
|
||||||
|
c-0.1,1-0.4,2-0.9,2.9c-0.4,0.8-1,1.5-1.7,2c-0.8,0.5-1.7,0.9-2.7,1c-1.1,0.1-2.2,0.1-3.3,0.1c-0.3,0-0.5,0.2-0.6,0.4
|
||||||
|
c-0.1,0.2-0.1,0.4-0.2,0.6l-0.6,3.8c0,0.3-0.3,0.5-0.5,0.5h-3c-0.3,0-0.5-0.2-0.5-0.5c0,0,0,0,0-0.1c0.8-4.9,1.5-9.8,2.3-14.6
|
||||||
|
C206.3,18.6,206.6,18.4,206.9,18.3z M209.9,21.9c-0.2,1.3-0.4,2.5-0.6,3.8c0.9,0,1.8,0,2.7-0.2c0.5-0.1,0.9-0.4,1.1-0.7
|
||||||
|
c0.3-0.5,0.5-1,0.5-1.5c0.1-0.4-0.1-0.9-0.3-1.3c-0.3-0.3-0.7-0.5-1.1-0.5c-0.6-0.1-1.1-0.1-1.7-0.1
|
||||||
|
C210.1,21.4,209.9,21.6,209.9,21.9C209.9,21.8,209.9,21.8,209.9,21.9L209.9,21.9z"/>
|
||||||
|
<path class="st2" d="M233.6,18.3c0.1,0,0.3,0,0.4,0h2.4c0.3,0,0.5,0.2,0.5,0.5c0,0,0,0.1,0,0.1c-0.8,4.9-1.5,9.8-2.3,14.7
|
||||||
|
c-0.1,0.4-0.4,0.7-0.8,0.7h-2.4c-0.3,0-0.5-0.2-0.5-0.5c0,0,0-0.1,0-0.1c0.8-5,1.6-9.9,2.3-14.9C233.2,18.6,233.4,18.4,233.6,18.3z"
|
||||||
|
/>
|
||||||
|
<path class="st2" d="M152.5,19c0.8,0.4,1.5,1.2,1.8,2c0.3,1.1,0.4,2.2,0.1,3.3c-0.2,1.5-0.9,3-1.9,4.1c-0.6,0.7-1.4,1.3-2.3,1.6
|
||||||
|
c-1.3,0.5-2.7,0.7-4.1,0.7c-0.3,0-0.6,0-0.8,0.1c-0.3,0.2-0.5,0.5-0.6,0.8c-0.3,1.6-0.5,3.1-0.8,4.7c0,0.2-0.1,0.5-0.1,0.7
|
||||||
|
c-0.1,0.4-0.5,0.7-0.9,0.7H139c-0.2,0-0.5-0.1-0.6-0.3c-0.1-0.2-0.1-0.4,0-0.6c0.1-0.6,0.2-1.3,0.3-1.9c0-0.1,0-0.2,0.1-0.4
|
||||||
|
c0.3-2.3,0.7-4.6,1.1-6.8c0-0.2,0.1-0.4,0.2-0.7c0.2-0.2,0.4-0.4,0.7-0.5c0.3,0,0.6,0,0.9,0c1.2,0,2.4,0,3.6-0.1
|
||||||
|
c1.6-0.2,3.2-0.8,4.5-1.9c1.2-1.1,2-2.4,2.4-3.9C152.3,20.1,152.4,19.6,152.5,19z"/>
|
||||||
|
<path class="st2" d="M221.4,23.6c0.9-0.3,1.9-0.4,2.9-0.2c0.6,0.1,1.2,0.3,1.8,0.6c0.2,0.2,0.5,0.4,0.6,0.7c0-0.3,0.1-0.5,0.1-0.8
|
||||||
|
c0.1-0.2,0.3-0.4,0.5-0.3h2.7c0.3,0,0.5,0.2,0.5,0.5c0,0,0,0.1,0,0.1l-1.5,9.3c0,0.4-0.4,0.7-0.8,0.7h-2.5c-0.3,0-0.5-0.2-0.5-0.4
|
||||||
|
c0,0,0,0,0-0.1c0-0.3,0.1-0.5,0.1-0.8c-0.7,0.7-1.5,1.1-2.4,1.4c-0.9,0.2-1.8,0.2-2.7,0c-0.9-0.2-1.6-0.7-2.2-1.3
|
||||||
|
c-0.7-0.9-1-2-1-3.2c0-1.7,0.7-3.4,1.9-4.6C219.7,24.4,220.5,23.9,221.4,23.6z M223.5,26.2c-0.9,0.1-1.8,0.6-2.3,1.4
|
||||||
|
c-0.4,0.7-0.6,1.5-0.5,2.3c0.1,0.6,0.4,1.1,0.9,1.3c0.6,0.3,1.3,0.4,2,0.3c0.8-0.2,1.6-0.7,2-1.4c0.4-0.7,0.6-1.5,0.4-2.3
|
||||||
|
c-0.2-0.6-0.6-1.1-1.1-1.3C224.5,26.2,224.1,26.2,223.5,26.2L223.5,26.2z"/>
|
||||||
|
<path class="st2" d="M133.8,34.7c-0.2,0-0.3-0.1-0.3-0.3v-0.2c0.1-0.2,0.1-0.5,0.1-0.7l3.1-19.7c0-0.2,0.1-0.4,0.2-0.6
|
||||||
|
c0.1-0.2,0.3-0.3,0.6-0.3h8.5c1.4,0,2.7,0.3,4,0.9c0.9,0.5,1.7,1.3,2,2.3c0.3,0.9,0.3,1.8,0.1,2.8c-0.1,0.6-0.2,1.1-0.4,1.6
|
||||||
|
c-0.4,1.4-1.2,2.8-2.3,3.8c-1.2,1-2.7,1.6-4.3,1.8c-0.8,0.1-1.6,0.1-2.3,0.1h-1.7c-0.2,0-0.4,0-0.6,0c-0.4,0.1-0.7,0.3-0.9,0.7V27
|
||||||
|
v0.1c-0.1,0.2-0.1,0.5-0.2,0.8c-0.2,1-0.3,2-0.5,3.1c-0.2,1.3-0.4,2.5-0.6,3.8l0,0h-4.6L133.8,34.7z"/>
|
||||||
|
<path class="st0" d="M146,13.3c1.3,0,2.6,0.3,3.8,0.9c0.9,0.5,1.5,1.2,1.8,2.1c0.2,0.8,0.3,1.7,0.1,2.6l0,0l0,0
|
||||||
|
c-0.1,0.5-0.2,1-0.4,1.5c-0.4,1.4-1.1,2.6-2.2,3.6c-1.2,1-2.6,1.5-4.1,1.7c-0.8,0.1-1.5,0.1-2.3,0.1h-1.7c-0.2,0-0.4,0-0.6,0.1
|
||||||
|
c-0.5,0.1-0.9,0.4-1.1,0.8l-0.1,0.1V27c-0.1,0.3-0.1,0.5-0.2,0.8c-0.2,1-0.3,2-0.5,3c-0.2,1.2-0.4,2.3-0.6,3.5h-4.2
|
||||||
|
c0.1-0.2,0.1-0.5,0.1-0.7v-0.2l1.1-7.1l1.9-12.1c0-0.1,0-0.1,0-0.2c0-0.1,0-0.3,0.1-0.4c0.1-0.1,0.2-0.1,0.3-0.2L146,13.3 M146,12.5
|
||||||
|
h-8.6c-0.3,0-0.6,0.2-0.8,0.5c-0.2,0.3-0.2,0.6-0.3,0.9l-3,19.3c-0.1,0.3-0.1,0.6-0.2,1v0.3c0.1,0.3,0.3,0.5,0.6,0.6h4.4h0.5
|
||||||
|
c0-0.1,0-0.2,0.1-0.4c0.3-2.3,0.7-4.6,1.1-6.8c0-0.2,0.1-0.4,0.1-0.7c0.2-0.2,0.4-0.4,0.7-0.5c0.2,0,0.3,0,0.5,0h1.7
|
||||||
|
c0.8,0,1.6,0,2.4-0.1c1.6-0.2,3.2-0.8,4.5-1.9c1.2-1.1,2-2.4,2.4-3.9c0.2-0.5,0.3-1.1,0.4-1.7l0,0c0.2-1,0.1-2-0.1-2.9
|
||||||
|
c-0.4-1.1-1.2-2-2.2-2.6c-1.3-0.6-2.7-0.9-4.1-0.9l0,0L146,12.5z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.0 KiB |
@@ -20,7 +20,42 @@ NAME_DMG="${NAME}.app"
|
|||||||
echo $NAME_DMG
|
echo $NAME_DMG
|
||||||
create-dmg $NAME_DMG
|
create-dmg $NAME_DMG
|
||||||
|
|
||||||
VERSION=`mdls -raw -name kMDItemVersion ${NAME}.app`
|
DATE=`date +"%a, %d %b %Y %H:%M:%S %z"`
|
||||||
|
BUILD=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" ${NAME}.app/Contents/Info.plist`
|
||||||
|
VERSION=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${NAME}.app/Contents/Info.plist`
|
||||||
|
MINIMUM=`/usr/libexec/PlistBuddy -c "Print LSMinimumSystemVersion" ${NAME}.app/Contents/Info.plist`
|
||||||
|
SIZE=`stat -f%z MTMR\ ${VERSION}.dmg`
|
||||||
|
SIGN=`~/Sparkle/bin/sign_update MTMR\ ${VERSION}.dmg ~/Sparkle/bin/dsa_priv.pem | awk '{printf "%s",$0} END {print ""}'`
|
||||||
|
|
||||||
echo $VERSION
|
# ditto -c -k --sequesterRsrc --keepParent "${NAME}.app" "${NAME}v${VERSION}.zip"
|
||||||
ditto -c -k --sequesterRsrc --keepParent "${NAME}.app" "${NAME}v${VERSION}.zip"
|
|
||||||
|
echo DATE $DATE
|
||||||
|
echo VERSION $VERSION
|
||||||
|
echo BUILD $BUILD
|
||||||
|
echo MINIMUM $MINIMUM
|
||||||
|
echo SIZE $SIZE
|
||||||
|
echo SIGN ${SIGN}
|
||||||
|
|
||||||
|
echo "<?xml version=\"1.0\" standalone=\"yes\"?>
|
||||||
|
<rss xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/sparkle\" version=\"2.0\">
|
||||||
|
<channel>
|
||||||
|
<item>
|
||||||
|
<title>${VERSION}</title>
|
||||||
|
<pubDate>${DATE}</pubDate>
|
||||||
|
<description>
|
||||||
|
${1}
|
||||||
|
</description>
|
||||||
|
<sparkle:minimumSystemVersion>${MINIMUM}</sparkle:minimumSystemVersion>
|
||||||
|
<enclosure url=\"https://mtmr.app/MTMR%20${VERSION}.dmg\"
|
||||||
|
sparkle:version=\"${BUILD}\"
|
||||||
|
sparkle:shortVersionString=\"${VERSION}\"
|
||||||
|
length=\"${SIZE}\"
|
||||||
|
type=\"application/octet-stream\"
|
||||||
|
sparkle:dsaSignature=\"${SIGN}\"
|
||||||
|
/>
|
||||||
|
</item>
|
||||||
|
</channel>
|
||||||
|
</rss>" > appcast.xml
|
||||||
|
|
||||||
|
scp MTMR\ ${VERSION}.dmg do:/var/www/mtmr
|
||||||
|
scp appcast.xml do:/var/www/mtmr
|
||||||
|
|||||||
Reference in New Issue
Block a user