1
0
mirror of https://github.com/Toxblh/MTMR.git synced 2026-01-10 17:08:39 +00:00

Merge pull request #31 from ad/master

Action for battery plugin
This commit is contained in:
Anton Palgunov 2018-04-21 12:08:01 +01:00 committed by GitHub
commit 9bf42dfb79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 221 additions and 53 deletions

View File

@ -56,7 +56,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
if (result != nil) {
let path = result!.path
let jsonData = path.fileData
let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, additionalParameters: [:])]
let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
TouchBarController.shared.createAndUpdatePreset(jsonItems: jsonItems)
}

View File

@ -5,9 +5,9 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
private let interval: TimeInterval
private var forceHideConstraint: NSLayoutConstraint!
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, onTap: @escaping ()->()) {
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, onTap: @escaping ()->(), onLongTap: @escaping ()->()) {
self.interval = interval
super.init(identifier: identifier, title: "", onTap: onTap)
super.init(identifier: identifier, title: "", onTap: onTap, onLongTap: onLongTap)
self.forceHideConstraint = self.view.widthAnchor.constraint(equalToConstant: 0)
guard let script = source.appleScript else {
button.title = "no script"

View File

@ -9,12 +9,11 @@
import IOKit.ps
import Foundation
class BatteryBarItem: NSCustomTouchBarItem {
class BatteryBarItem: CustomButtonTouchBarItem {
private var timer: Timer!
private let button = NSButton(title: "", target: nil, action: nil)
override init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier)
init(identifier: NSTouchBarItem.Identifier, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
super.init(identifier: identifier, title: " ", onTap: onTap, onLongTap: onLongTap)
self.view = button
button.bezelColor = .clear

View File

@ -37,7 +37,7 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
"ETH": "Ξ",
]
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, onTap: @escaping () -> ()) {
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
self.interval = interval
self.from = from
self.to = to
@ -48,7 +48,7 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
self.prefix = from
}
super.init(identifier: identifier, title: "", onTap: onTap)
super.init(identifier: identifier, title: "", onTap: onTap, onLongTap: onLongTap)
button.bezelColor = .clear
self.view = button

View File

@ -8,26 +8,76 @@
import Cocoa
class CustomButtonTouchBarItem: NSCustomTouchBarItem {
let tapClosure: () -> ()
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
private let tapClosure: () -> ()?
private let longTapClosure: () -> ()?
private(set) var button: NSButton!
private var singleClick: NSClickGestureRecognizer!
private var longClick: NSPressGestureRecognizer!
init(identifier: NSTouchBarItem.Identifier, title: String, onTap callback: @escaping () -> ()) {
init(identifier: NSTouchBarItem.Identifier, title: String, onTap callback: @escaping () -> (), onLongTap callbackLong: @escaping () -> ()) {
self.tapClosure = callback
self.longTapClosure = callbackLong
super.init(identifier: identifier)
button = NSButton(title: title, target: self, action: #selector(didTapped))
button = NSButton(title: title, target: self, action: nil)
button.title = title
self.view = button
longClick = NSPressGestureRecognizer(target: self, action: #selector(handleGestureLong))
longClick.allowedTouchTypes = .direct
longClick.delegate = self
singleClick = NSClickGestureRecognizer(target: self, action: #selector(handleGestureSingle))
singleClick.allowedTouchTypes = .direct
singleClick.delegate = self
self.view.addGestureRecognizer(longClick)
self.view.addGestureRecognizer(singleClick)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func didTapped() {
self.tapClosure()
func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool {
if gestureRecognizer == singleClick && otherGestureRecognizer == longClick {
return false
}
return true
}
@objc func handleGestureSingle(gr: NSClickGestureRecognizer) {
let hf: HapticFeedback = HapticFeedback()
hf.tap(strong: 6)
switch gr.state {
case .ended:
hf.tap(strong: 2)
self.tapClosure()
break
default:
break
}
}
@objc func handleGestureLong(gr: NSPressGestureRecognizer) {
let hf: HapticFeedback = HapticFeedback()
switch gr.state {
case .began:
if self.longTapClosure != nil {
hf.tap(strong: 2)
self.tapClosure()
} else {
hf.tap(strong: 6)
self.longTapClosure()
print("long click")
}
break
default:
break
}
}
}

View File

@ -12,15 +12,17 @@ extension Data {
struct BarItemDefinition: Decodable {
let type: ItemType
let action: ActionType
let longAction: LongActionType
let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]
private enum CodingKeys: String, CodingKey {
case type
}
init(type: ItemType, action: ActionType, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) {
init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) {
self.type = type
self.action = action
self.longAction = longAction
self.additionalParameters = additionalParameters
}
@ -30,51 +32,51 @@ struct BarItemDefinition: Decodable {
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type)
var additionalParameters = try GeneralParameters(from: decoder).parameters
if let result = try? parametersDecoder(decoder),
case let (itemType, action, parameters) = result {
case let (itemType, action, longAction, parameters) = result {
parameters.forEach { additionalParameters[$0] = $1 }
self.init(type: itemType, action: action, additionalParameters: additionalParameters)
self.init(type: itemType, action: action, longAction: longAction, additionalParameters: additionalParameters)
} else {
self.init(type: .staticButton(title: "unknown"), action: .none, additionalParameters: additionalParameters)
self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, additionalParameters: additionalParameters)
}
}
}
class SupportedTypesHolder {
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter])
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, longAction: LongActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter])
private var supportedTypes: [String: ParametersDecoder] = [
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), parameters: [.align: .align(.left)]) },
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), longAction: .none, parameters: [.align: .align(.left)]) },
"brightnessUp": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
return (item: .staticButton(title: ""), action: .keyPress(keycode: 113), parameters: [.image: imageParameter])
return (item: .staticButton(title: ""), action: .keyPress(keycode: 113), longAction: .none, parameters: [.image: imageParameter])
},
"brightnessDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
return (item: .staticButton(title: ""), action: .keyPress(keycode: 107), parameters: [.image: imageParameter])
return (item: .staticButton(title: ""), action: .keyPress(keycode: 107), longAction: .none, parameters: [.image: imageParameter])
},
"volumeDown": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), parameters: [.image: imageParameter])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), longAction: .none, parameters: [.image: imageParameter])
},
"volumeUp": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeUpTemplate)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), parameters: [.image: imageParameter])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), longAction: .none, parameters: [.image: imageParameter])
},
"mute": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarAudioOutputMuteTemplate)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), parameters: [.image: imageParameter])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), longAction: .none, parameters: [.image: imageParameter])
},
"previous": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarRewindTemplate)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), parameters: [.image: imageParameter])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), longAction: .none, parameters: [.image: imageParameter])
},
"play": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarPlayPauseTemplate)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), parameters: [.image: imageParameter])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), longAction: .none, parameters: [.image: imageParameter])
},
"next": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarFastForwardTemplate)!)
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), parameters: [.image: imageParameter])
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), longAction: .none, parameters: [.image: imageParameter])
},
"weather": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key ; case icon_type }
@ -84,7 +86,8 @@ class SupportedTypesHolder {
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key)
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type)
let action = try ActionType(from: decoder)
return (item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"), action: action, parameters: [:])
let longAction = try LongActionType(from: decoder)
return (item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"), action: action, longAction: longAction, parameters: [:])
},
"currency": { decoder in
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to }
@ -93,18 +96,19 @@ class SupportedTypesHolder {
let from = try container.decodeIfPresent(String.self, forKey: .from)
let to = try container.decodeIfPresent(String.self, forKey: .to)
let action = try ActionType(from: decoder)
return (item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), action: action, parameters: [:])
let longAction = try LongActionType(from: decoder)
return (item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), action: action, longAction: longAction, parameters: [:])
},
"dock": { decoder in
return (item: .dock(), action: .none, parameters: [:])
return (item: .dock(), action: .none, longAction: .none, parameters: [:])
},
"volume": { decoder in
enum CodingKeys: String, CodingKey { case image }
let container = try decoder.container(keyedBy: CodingKeys.self)
if var img = try container.decodeIfPresent(Source.self, forKey: .image) {
return (item: .volume(), action: .none, parameters: [.image: .image(source: img)])
return (item: .volume(), action: .none, longAction: .none, parameters: [.image: .image(source: img)])
} else {
return (item: .volume(), action: .none, parameters: [:])
return (item: .volume(), action: .none, longAction: .none, parameters: [:])
}
},
"brightness": { decoder in
@ -112,20 +116,20 @@ class SupportedTypesHolder {
let container = try decoder.container(keyedBy: CodingKeys.self)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
if var img = try container.decodeIfPresent(Source.self, forKey: .image) {
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, parameters: [.image: .image(source: img)])
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [.image: .image(source: img)])
} else {
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, parameters: [:])
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [:])
}
},
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), parameters: [:]) },
"displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), parameters: [:]) },
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), longAction: .none, parameters: [:]) },
"displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), longAction: .none, parameters: [:])},
]
static let sharedInstance = SupportedTypesHolder()
func lookup(by type: String) -> ParametersDecoder {
return supportedTypes[type] ?? { decoder in
return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), parameters: [:])
return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), longAction: try LongActionType(from: decoder), parameters: [:])
}
}
@ -133,9 +137,9 @@ class SupportedTypesHolder {
supportedTypes[typename] = decoder
}
func register(typename: String, item: ItemType, action: ActionType) {
func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) {
register(typename: typename) { _ in
return (item: item, action: action, parameters: [:])
return (item: item, action: action, longAction: longAction, parameters: [:])
}
}
}
@ -164,6 +168,7 @@ enum ItemType: Decodable {
case formatTemplate
case image
case url
case longUrl
}
enum ItemTypeRaw: String, Decodable {
@ -268,6 +273,59 @@ enum ActionType: Decodable {
}
}
enum LongActionType: Decodable {
case none
case hidKey(keycode: Int)
case keyPress(keycode: Int)
case appleSctipt(source: SourceProtocol)
case shellScript(executable: String, parameters: [String])
case custom(closure: ()->())
case openUrl(url: String)
private enum CodingKeys: String, CodingKey {
case longAction
case keycode
case actionAppleScript
case executablePath
case shellArguments
case longUrl
}
private enum LongActionTypeRaw: String, Decodable {
case hidKey
case keyPress
case appleScript
case shellScript
case openUrl
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let longType = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction)
switch longType {
case .some(.hidKey):
let keycode = try container.decode(Int.self, forKey: .keycode)
self = .hidKey(keycode: keycode)
case .some(.keyPress):
let keycode = try container.decode(Int.self, forKey: .keycode)
self = .keyPress(keycode: keycode)
case .some(.appleScript):
let source = try container.decode(Source.self, forKey: .actionAppleScript)
self = .appleSctipt(source: source)
case .some(.shellScript):
let executable = try container.decode(String.self, forKey: .executablePath)
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
self = .shellScript(executable: executable, parameters: parameters)
case .some(.openUrl):
let longUrl = try container.decode(String.self, forKey: .longUrl)
self = .openUrl(url: longUrl)
case .none:
self = .none
}
}
}
extension ItemType: Equatable {}
func ==(lhs: ItemType, rhs: ItemType) -> Bool {
switch (lhs, rhs) {
@ -300,6 +358,26 @@ func ==(lhs: ActionType, rhs: ActionType) -> Bool {
}
}
extension LongActionType: Equatable {}
func ==(lhs: LongActionType, rhs: LongActionType) -> Bool {
switch (lhs, rhs) {
case (.none, .none):
return true
case let (.hidKey(a), .hidKey(b)),
let (.keyPress(a), .keyPress(b)):
return a == b
case let (.appleSctipt(a), .appleSctipt(b)):
return a == b
case let (.shellScript(a, b), .shellScript(c, d)):
return a == c && b == d
case let (.openUrl(a), .openUrl(b)):
return a == b
default:
return false
}
}
enum GeneralParameter {
case width(_: CGFloat)
case image(source: SourceProtocol)

View File

@ -60,9 +60,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
private override init() {
super.init()
SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in
self?.dismissTouchBar()
}))
SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in self?.dismissTouchBar()}), longAction: .none)
createAndUpdatePreset()
}
@ -104,7 +102,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
let jsonData = presetPath.fileData
return jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, additionalParameters: [:])]
return jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
}
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
@ -162,17 +160,18 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? {
let action = self.action(forItem: item)
let longAction = self.longAction(forItem: item)
var barItem: NSTouchBarItem!
switch item.type {
case .staticButton(title: let title):
barItem = CustomButtonTouchBarItem(identifier: identifier, title: title, onTap: action)
barItem = CustomButtonTouchBarItem(identifier: identifier, title: title, onTap: action, onLongTap: longAction)
case .appleScriptTitledButton(source: let source, refreshInterval: let interval):
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action)
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action, onLongTap: longAction)
case .timeButton(formatTemplate: let template):
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template)
case .battery():
barItem = BatteryBarItem(identifier: identifier)
barItem = BatteryBarItem(identifier: identifier, onTap: action, onLongTap: longAction)
case .dock:
barItem = AppScrubberTouchBarItem(identifier: identifier)
case .volume:
@ -188,9 +187,9 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
}
case .weather(interval: let interval, units: let units, api_key: let api_key, icon_type: let icon_type):
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type, onTap: action)
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type, onTap: action, onLongTap: longAction)
case .currency(interval: let interval, from: let from, to: let to):
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, onTap: action)
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, onTap: action, onLongTap: longAction)
}
if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
@ -248,6 +247,48 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
}
}
func longAction(forItem item: BarItemDefinition) -> ()->() {
switch item.longAction {
case .hidKey(keycode: let keycode):
return { HIDPostAuxKey(keycode) }
case .keyPress(keycode: let keycode):
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
case .appleSctipt(source: let source):
guard let appleScript = source.appleScript else {
print("cannot create apple script for item \(item)")
return {}
}
return {
var error: NSDictionary?
appleScript.executeAndReturnError(&error)
if let error = error {
print("error \(error) when handling \(item) ")
}
}
case .shellScript(executable: let executable, parameters: let parameters):
return {
let task = Process()
task.launchPath = executable
task.arguments = parameters
task.launch()
}
case .openUrl(url: let url):
return {
if let url = URL(string: url), NSWorkspace.shared.open(url) {
#if DEBUG
print("URL was successfully opened")
#endif
} else {
print("error", url)
}
}
case .custom(closure: let closure):
return closure
case .none:
return {}
}
}
}
protocol CanSetWidth {

View File

@ -24,7 +24,7 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
private var manager:CLLocationManager!
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text", onTap: @escaping () -> ()) {
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text", onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
self.interval = interval
self.units = units
self.api_key = api_key
@ -43,7 +43,7 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
iconsSource = iconsText
}
super.init(identifier: identifier, title: "", onTap: onTap)
super.init(identifier: identifier, title: "", onTap: onTap, onLongTap: onLongTap)
button.bezelColor = .clear
self.view = button