From 93d2f496d0b46df0d0404d4d0855c9eb5adf8436 Mon Sep 17 00:00:00 2001 From: ad Date: Sun, 29 Apr 2018 01:18:05 +0300 Subject: [PATCH 1/2] * first working --- MTMR/AppDelegate.swift | 2 +- MTMR/ItemsParsing.swift | 195 +++++++++++++++++++++----- MTMR/TouchBarController.swift | 61 ++++++-- MTMR/Widgets/InputSourceBarItem.swift | 4 +- 4 files changed, 211 insertions(+), 51 deletions(-) diff --git a/MTMR/AppDelegate.swift b/MTMR/AppDelegate.swift index 6c55260..e94450b 100644 --- a/MTMR/AppDelegate.swift +++ b/MTMR/AppDelegate.swift @@ -59,7 +59,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, longAction: .none, additionalParameters: [:])] + let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none), additionalParameters: [:])] TouchBarController.shared.createAndUpdatePreset(jsonItems: jsonItems) } diff --git a/MTMR/ItemsParsing.swift b/MTMR/ItemsParsing.swift index b564cd3..ae96816 100644 --- a/MTMR/ItemsParsing.swift +++ b/MTMR/ItemsParsing.swift @@ -3,7 +3,12 @@ import AppKit extension Data { func barItemDefinitions() -> [BarItemDefinition]? { - return try? JSONDecoder().decode([BarItemDefinition].self, from: self.utf8string!.stripComments().data(using: .utf8)!) + do { + return try JSONDecoder().decode([BarItemDefinition].self, from: self.utf8string!.stripComments().data(using: .utf8)!) + } catch { + print("\(error)") + } + return [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none), additionalParameters: [:])] } } @@ -11,16 +16,19 @@ struct BarItemDefinition: Decodable { let type: ItemType let action: ActionType let longAction: LongActionType + let shortPressAction: ShortPressAction let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter] - + private enum CodingKeys: String, CodingKey { case type + case shortPressAction } - init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) { + init(type: ItemType, action: ActionType, longAction: LongActionType, shortPressAction: ShortPressAction, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) { self.type = type self.action = action self.longAction = longAction + self.shortPressAction = shortPressAction self.additionalParameters = additionalParameters } @@ -28,89 +36,107 @@ struct BarItemDefinition: Decodable { let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(String.self, forKey: .type) let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type) + let shortPressAction = try container.decode(ShortPressAction.self, forKey: .shortPressAction) var additionalParameters = try GeneralParameters(from: decoder).parameters - if let result = try? parametersDecoder(decoder), - case let (itemType, action, longAction, parameters) = result { - parameters.forEach { additionalParameters[$0] = $1 } - self.init(type: itemType, action: action, longAction: longAction, additionalParameters: additionalParameters) - } else { - self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, additionalParameters: additionalParameters) + do { + if let result = try? parametersDecoder(decoder), + case let (itemType, action, longAction, shortPressAction, parameters) = result { + parameters.forEach { additionalParameters[$0] = $1 } + self.init(type: itemType, action: action, longAction: longAction, shortPressAction: shortPressAction, additionalParameters: additionalParameters) + } else { + self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none), additionalParameters: additionalParameters) + } + } catch { + print("\(error)") } } } class SupportedTypesHolder { - typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, longAction: LongActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter]) + typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, longAction: LongActionType, shortPressAction: ShortPressAction, parameters: [GeneralParameters.CodingKeys: GeneralParameter]) private var supportedTypes: [String: ParametersDecoder] = [ - "escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), longAction: .none, parameters: [.align: .align(.left)]) }, - "delete": { _ in return (item: .staticButton(title: "del"), action: .keyPress(keycode: 117), longAction: .none, parameters: [:])}, + "escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), longAction: .none, parameters: [.align: .align(.left)], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.keyPress, keycode: 53)) }, + "delete": { _ in return (item: .staticButton(title: "del"), action: .keyPress(keycode: 117), longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.keyPress, keycode: 117))}, "brightnessUp": { _ in let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp")) - return (item: .staticButton(title: ""), action: .keyPress(keycode: 113), longAction: .none, parameters: [.image: imageParameter]) + return (item: .staticButton(title: ""), action: .keyPress(keycode: 113), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.keyPress, keycode: 113)) }, "brightnessDown": { _ in let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown")) - return (item: .staticButton(title: ""), action: .keyPress(keycode: 107), longAction: .none, parameters: [.image: imageParameter]) + return (item: .staticButton(title: ""), action: .keyPress(keycode: 107), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.keyPress, keycode: 107)) }, "volumeDown": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), longAction: .none, parameters: [.image: imageParameter]) + return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_SOUND_DOWN)) }, "volumeUp": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeUpTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), longAction: .none, parameters: [.image: imageParameter]) + return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_SOUND_UP)) }, "mute": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarAudioOutputMuteTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), longAction: .none, parameters: [.image: imageParameter]) + return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_MUTE)) }, "previous": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarRewindTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), longAction: .none, parameters: [.image: imageParameter]) + return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_PREVIOUS)) }, "play": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarPlayPauseTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), longAction: .none, parameters: [.image: imageParameter]) + return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_PLAY)) }, "next": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarFastForwardTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), longAction: .none, parameters: [.image: imageParameter]) + return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_NEXT)) }, "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; case shortPressAction } let container = try decoder.container(keyedBy: CodingKeys.self) let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) let units = try container.decodeIfPresent(String.self, forKey: .units) let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) - let action = try ActionType(from: decoder) + let shortPressAction = try container.decodeIfPresent(ShortPressAction.self, forKey: .shortPressAction) 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: .none, longAction: longAction, parameters: [:], shortPressAction: shortPressAction!) }, "currency": { decoder in - enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to } + enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to; case shortPressAction } let container = try decoder.container(keyedBy: CodingKeys.self) let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) let from = try container.decodeIfPresent(String.self, forKey: .from) let to = try container.decodeIfPresent(String.self, forKey: .to) - let action = try ActionType(from: decoder) + let shortPressAction = try container.decodeIfPresent(ShortPressAction.self, forKey: .shortPressAction) 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"), action: .none, longAction: longAction, parameters: [:], shortPressAction: shortPressAction!) }, "dock": { decoder in - return (item: .dock(), action: .none, longAction: .none, parameters: [:]) + return (item: .dock(), action: .none, longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) }, "inputsource": { decoder in - return (item: .inputsource(), action: .none, longAction: .none, parameters: [:]) + return (item: .inputsource(), action: .none, longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) + }, + "battery": { decoder in + enum CodingKeys: String, CodingKey { case shortPressAction } + let container = try decoder.container(keyedBy: CodingKeys.self) + let shortPressAction = try container.decodeIfPresent(ShortPressAction.self, forKey: .shortPressAction) + return (item: .battery(), action: .none, longAction: .none, parameters: [:], shortPressAction: shortPressAction!) + }, + "timeButton": { decoder in + enum CodingKeys: String, CodingKey { case formatTemplate; case shortPressAction } + let container = try decoder.container(keyedBy: CodingKeys.self) + let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm" + let shortPressAction = try container.decodeIfPresent(ShortPressAction.self, forKey: .shortPressAction) + return (item: .timeButton(formatTemplate: template), action: .none, longAction: .none, parameters: [:], shortPressAction: shortPressAction!) }, "volume": { decoder in enum CodingKeys: String, CodingKey { case image } let container = try decoder.container(keyedBy: CodingKeys.self) if var img = try container.decodeIfPresent(Source.self, forKey: .image) { - return (item: .volume(), action: .none, longAction: .none, parameters: [.image: .image(source: img)]) + return (item: .volume(), action: .none, longAction: .none, parameters: [.image: .image(source: img)], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) } else { - return (item: .volume(), action: .none, longAction: .none, parameters: [:]) + return (item: .volume(), action: .none, longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) } }, "brightness": { decoder in @@ -118,20 +144,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, longAction: .none, parameters: [.image: .image(source: img)]) + return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [.image: .image(source: img)], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) } 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: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) } }, - "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: [:])}, + "sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.shellScript, executablePath: "/usr/bin/pmset", shellArguments: ["sleepnow"])) }, + "displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.shellScript, executablePath: "/usr/bin/pmset", shellArguments: ["displaysleepnow"]))}, ] static let sharedInstance = SupportedTypesHolder() func lookup(by type: String) -> ParametersDecoder { return supportedTypes[type] ?? { decoder in - return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), longAction: try LongActionType(from: decoder), parameters: [:]) + return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), longAction: try LongActionType(from: decoder), shortPressAction: try ShortPressAction(from: decoder), parameters: [:]) } } @@ -139,13 +165,14 @@ class SupportedTypesHolder { supportedTypes[typename] = decoder } - func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) { + func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType, shortPressAction: ShortPressAction) { register(typename: typename) { _ in - return (item: item, action: action, longAction: longAction, parameters: [:]) + return (item: item, action: action, longAction: longAction, shortPressAction: shortPressAction, parameters: [:]) } } } + enum ItemType: Decodable { case staticButton(title: String) case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double) @@ -485,3 +512,97 @@ enum Align: String, Decodable { case center case right } + +struct ShortPressAction: Codable { + let actionType: PressActionType + let url: String? + let keycode: Int + let appleScript: String? + let executablePath: String? + let shellArguments: [String]? + let custom: () -> ()? + + enum CodingKeys: String, CodingKey { + case actionType = "type" + case url + case keycode + case appleScript + case executablePath + case shellArguments + } + + init(actionType: PressActionType, url: String? = "", keycode: Int? = -1, appleScript: String? = "", executablePath: String? = "", shellArguments: [String]? = [], custom: @escaping () -> Void? = {return}) { + self.actionType = actionType + self.url = url + self.keycode = keycode! + self.appleScript = appleScript + self.executablePath = executablePath + self.shellArguments = shellArguments + self.custom = custom + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let actionType = try container.decode(PressActionType.self, forKey: .actionType) + let url = try container.decodeIfPresent(String.self, forKey: .url) ?? "" + let keycode = try container.decodeIfPresent(Int.self, forKey: .keycode) ?? -1 + var appleScript = try container.decodeIfPresent(String.self, forKey: .appleScript) + do { + if let test = appleScript?.fileData?.utf8string { + appleScript = test + } else if let test = appleScript?.base64Data?.utf8string { + appleScript = test + } + } catch { + print("error") + } + let executablePath = try container.decodeIfPresent(String.self, forKey: .executablePath) ?? "" + let shellArguments = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? [] + self.init(actionType: actionType, url: url, keycode: keycode, appleScript: appleScript, executablePath: executablePath, shellArguments: shellArguments) + } + + enum PressActionType: Codable { + func encode(to encoder: Encoder) throws { + + } + + case none + case hidKey + case keyPress + case appleScript + case shellScript + case custom + case openUrl + + private enum CodingKeys: String, CodingKey { + case type + } + + private enum ActionTypeRaw: String, Decodable { + case hidKey + case keyPress + case appleScript + case shellScript + case openUrl + } + + init(from decoder: Decoder) throws { + self = .none + + let container = try decoder.singleValueContainer() + let actionType = try container.decode(ActionTypeRaw.self) + switch actionType { + case .hidKey: + self = .hidKey + case .keyPress: + self = .keyPress + case .appleScript: + self = .appleScript + case .shellScript: + self = .shellScript + case .openUrl: + self = .openUrl + } + } + } +} diff --git a/MTMR/TouchBarController.swift b/MTMR/TouchBarController.swift index 945d464..2586d62 100644 --- a/MTMR/TouchBarController.swift +++ b/MTMR/TouchBarController.swift @@ -63,7 +63,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()}), longAction: .none) + SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in self?.dismissTouchBar()}), longAction: .none, shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none )) createAndUpdatePreset() } @@ -106,7 +106,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate { let jsonData = presetPath.fileData - return jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])] + return jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none), additionalParameters: [:])] } func loadItemDefinitions(jsonItems: [BarItemDefinition]) { @@ -166,19 +166,20 @@ class TouchBarController: NSObject, NSTouchBarDelegate { } func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? { - let action = self.action(forItem: item) +// let action = self.action(forItem: item) let longAction = self.longAction(forItem: item) + let shortPressAction = self.shortPressAction(forItem: item) var barItem: NSTouchBarItem! switch item.type { case .staticButton(title: let title): - barItem = CustomButtonTouchBarItem(identifier: identifier, title: title, onTap: action, onLongTap: longAction, bezelColor: NSColor.controlColor) + barItem = CustomButtonTouchBarItem(identifier: identifier, title: title, onTap: shortPressAction, onLongTap: longAction, bezelColor: NSColor.controlColor) case .appleScriptTitledButton(source: let source, refreshInterval: let interval): - barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action, onLongTap: longAction) + barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: shortPressAction, onLongTap: longAction) case .timeButton(formatTemplate: let template): - barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, onTap: action, onLongTap: longAction) + barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, onTap: shortPressAction, onLongTap: longAction) case .battery(): - barItem = BatteryBarItem(identifier: identifier, onTap: action, onLongTap: longAction) + barItem = BatteryBarItem(identifier: identifier, onTap: shortPressAction, onLongTap: longAction) case .dock: barItem = AppScrubberTouchBarItem(identifier: identifier) case .volume: @@ -194,11 +195,11 @@ 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, onLongTap: longAction) + barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type, onTap: shortPressAction, onLongTap: longAction) case .currency(interval: let interval, from: let from, to: let to): - barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, onTap: action, onLongTap: longAction) + barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, onTap: shortPressAction, onLongTap: longAction) case .inputsource(): - barItem = InputSourceBarItem(identifier: identifier, onTap: action, onLongTap: longAction) + barItem = InputSourceBarItem(identifier: identifier, onLongTap: longAction) } if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth { @@ -256,7 +257,6 @@ class TouchBarController: NSObject, NSTouchBarDelegate { return {} } } - func longAction(forItem item: BarItemDefinition) -> ()->() { switch item.longAction { @@ -299,6 +299,45 @@ class TouchBarController: NSObject, NSTouchBarDelegate { return {} } } + + func shortPressAction(forItem item: BarItemDefinition) -> ()->() { + switch item.shortPressAction.actionType { + case ShortPressAction.PressActionType.hidKey: + return { HIDPostAuxKey(item.shortPressAction.keycode) } + case ShortPressAction.PressActionType.keyPress: + return { GenericKeyPress(keyCode: CGKeyCode(item.shortPressAction.keycode)).send() } + case ShortPressAction.PressActionType.appleScript: + return { + let appleScriptSource = NSAppleScript(source: item.shortPressAction.appleScript!) + var error: NSDictionary? + appleScriptSource?.executeAndReturnError(&error) + if let error = error { + print("error \(error) when handling \(item) ") + } + } + case ShortPressAction.PressActionType.shellScript: + return { + let task = Process() + task.launchPath = item.shortPressAction.executablePath + task.arguments = item.shortPressAction.shellArguments + task.launch() + } + case ShortPressAction.PressActionType.openUrl: + return { + if let url = URL(string: item.shortPressAction.url!), NSWorkspace.shared.open(url) { + #if DEBUG + print("URL was successfully opened") + #endif + } + } + case ShortPressAction.PressActionType.none: + return {} + case ShortPressAction.PressActionType.custom: + return item.shortPressAction.custom as! () -> () + case .none: + return {} + } + } } protocol CanSetWidth { diff --git a/MTMR/Widgets/InputSourceBarItem.swift b/MTMR/Widgets/InputSourceBarItem.swift index c56e26e..3e17108 100644 --- a/MTMR/Widgets/InputSourceBarItem.swift +++ b/MTMR/Widgets/InputSourceBarItem.swift @@ -13,9 +13,9 @@ class InputSourceBarItem: CustomButtonTouchBarItem { fileprivate var notificationCenter: CFNotificationCenter let buttonSize = NSSize(width: 21, height: 21) - init(identifier: NSTouchBarItem.Identifier, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) { + init(identifier: NSTouchBarItem.Identifier, onLongTap: @escaping () -> ()) { notificationCenter = CFNotificationCenterGetDistributedCenter(); - super.init(identifier: identifier, title: "⏳", onTap: onTap, onLongTap: onLongTap) + super.init(identifier: identifier, title: "⏳", onTap: onLongTap, onLongTap: onLongTap) observeIputSourceChangedNotification(); textInputSourceDidChange() From 8e58bb179a3e7505af5f254673b6e7bf36f85fd1 Mon Sep 17 00:00:00 2001 From: ad Date: Mon, 30 Apr 2018 09:50:59 +0300 Subject: [PATCH 2/2] tap and longTap --- MTMR/AppDelegate.swift | 2 +- MTMR/ItemsParsing.swift | 443 ++++++++++++++++++++++------------ MTMR/TouchBarController.swift | 138 ++++++----- 3 files changed, 352 insertions(+), 231 deletions(-) diff --git a/MTMR/AppDelegate.swift b/MTMR/AppDelegate.swift index e94450b..1923954 100644 --- a/MTMR/AppDelegate.swift +++ b/MTMR/AppDelegate.swift @@ -59,7 +59,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, longAction: .none, shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none), additionalParameters: [:])] + let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"))] TouchBarController.shared.createAndUpdatePreset(jsonItems: jsonItems) } diff --git a/MTMR/ItemsParsing.swift b/MTMR/ItemsParsing.swift index ae96816..bd56385 100644 --- a/MTMR/ItemsParsing.swift +++ b/MTMR/ItemsParsing.swift @@ -8,43 +8,45 @@ extension Data { } catch { print("\(error)") } - return [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none), additionalParameters: [:])] + return [BarItemDefinition(type: .staticButton(title: "bad preset"))] } } struct BarItemDefinition: Decodable { let type: ItemType let action: ActionType - let longAction: LongActionType - let shortPressAction: ShortPressAction + let longTapAction: LongTapAction + let tapAction: TapAction let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter] private enum CodingKeys: String, CodingKey { case type - case shortPressAction + case tapAction + case longTapAction } - init(type: ItemType, action: ActionType, longAction: LongActionType, shortPressAction: ShortPressAction, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) { + init(type: ItemType, action: ActionType? = .none, tapAction: TapAction? = TapAction(actionType: TapActionType.none), longTapAction: LongTapAction? = LongTapAction(actionType: TapActionType.none), additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]? = [:]) { self.type = type - self.action = action - self.longAction = longAction - self.shortPressAction = shortPressAction - self.additionalParameters = additionalParameters + self.action = action! + self.longTapAction = longTapAction! + self.tapAction = tapAction! + self.additionalParameters = additionalParameters! } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(String.self, forKey: .type) let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type) - let shortPressAction = try container.decode(ShortPressAction.self, forKey: .shortPressAction) +// let tapAction = try container.decodeIfPresent(TapAction.self, forKey: .tapAction) +// let longTapAction = try container.decodeIfPresent(LongTapAction.self, forKey: .longTapAction) var additionalParameters = try GeneralParameters(from: decoder).parameters do { if let result = try? parametersDecoder(decoder), - case let (itemType, action, longAction, shortPressAction, parameters) = result { + case let (itemType, action, tapAction, longTapAction, parameters) = result { parameters.forEach { additionalParameters[$0] = $1 } - self.init(type: itemType, action: action, longAction: longAction, shortPressAction: shortPressAction, additionalParameters: additionalParameters) + self.init(type: itemType, action: action, tapAction: tapAction, longTapAction: longTapAction, additionalParameters: additionalParameters) } else { - self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none), additionalParameters: additionalParameters) + self.init(type: .staticButton(title: "unknown"), additionalParameters: additionalParameters) } } catch { print("\(error)") @@ -54,89 +56,208 @@ struct BarItemDefinition: Decodable { } class SupportedTypesHolder { - typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, longAction: LongActionType, shortPressAction: ShortPressAction, parameters: [GeneralParameters.CodingKeys: GeneralParameter]) + typealias ParametersDecoder = (Decoder) throws -> (item: ItemType, action: ActionType, tapAction: TapAction, longTapAction: LongTapAction, parameters: [GeneralParameters.CodingKeys: GeneralParameter]) private var supportedTypes: [String: ParametersDecoder] = [ - "escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), longAction: .none, parameters: [.align: .align(.left)], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.keyPress, keycode: 53)) }, - "delete": { _ in return (item: .staticButton(title: "del"), action: .keyPress(keycode: 117), longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.keyPress, keycode: 117))}, + "escape": { _ in return ( + item: .staticButton(title: "esc"), + action: .keyPress(keycode: 53), + tapAction: TapAction(actionType: TapActionType.keyPress, keycode: 53), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.align: .align(.left)] + ) + }, + "delete": { _ in return ( + item: .staticButton(title: "del"), + action: .keyPress(keycode: 117), + tapAction: TapAction(actionType: TapActionType.keyPress, keycode: 117), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) + }, "brightnessUp": { _ in let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp")) - return (item: .staticButton(title: ""), action: .keyPress(keycode: 113), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.keyPress, keycode: 113)) + return ( + item: .staticButton(title: ""), + action: .keyPress(keycode: 113), + tapAction: TapAction(actionType: TapActionType.keyPress, keycode: 113), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.image: imageParameter] + ) }, "brightnessDown": { _ in let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown")) - return (item: .staticButton(title: ""), action: .keyPress(keycode: 107), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.keyPress, keycode: 107)) + return ( + item: .staticButton(title: ""), + action: .keyPress(keycode: 107), + tapAction: TapAction(actionType: TapActionType.keyPress, keycode: 107), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.image: imageParameter] + ) }, "volumeDown": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_SOUND_DOWN)) + return ( + item: .staticButton(title: ""), + action: .hidKey(keycode: Int(NX_KEYTYPE_SOUND_DOWN)), + tapAction: TapAction(actionType: TapActionType.hidKey, keycode: Int(NX_KEYTYPE_SOUND_DOWN)), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.image: imageParameter] + ) }, "volumeUp": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeUpTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_SOUND_UP)) + return ( + item: .staticButton(title: ""), + action: .hidKey(keycode: Int(NX_KEYTYPE_SOUND_UP)), + tapAction: TapAction(actionType: TapActionType.hidKey, keycode: Int(NX_KEYTYPE_SOUND_UP)), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.image: imageParameter] + ) }, "mute": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarAudioOutputMuteTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_MUTE), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_MUTE)) + return ( + item: .staticButton(title: ""), + action: .hidKey(keycode: Int(NX_KEYTYPE_MUTE)), + tapAction: TapAction(actionType: TapActionType.hidKey, keycode: Int(NX_KEYTYPE_MUTE)), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.image: imageParameter] + ) }, "previous": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarRewindTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_PREVIOUS)) + return ( + item: .staticButton(title: ""), + action: .hidKey(keycode: Int(NX_KEYTYPE_PREVIOUS)), + tapAction: TapAction(actionType: TapActionType.hidKey, keycode: Int(NX_KEYTYPE_PREVIOUS)), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.image: imageParameter] + ) }, "play": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarPlayPauseTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_PLAY)) + return ( + item: .staticButton(title: ""), + action: .hidKey(keycode: Int(NX_KEYTYPE_PLAY)), + tapAction: TapAction(actionType: TapActionType.hidKey, keycode: Int(NX_KEYTYPE_PLAY)), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.image: imageParameter] + ) }, "next": { _ in let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarFastForwardTemplate)!) - return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), longAction: .none, parameters: [.image: imageParameter], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.hidKey, keycode: NX_KEYTYPE_NEXT)) + return ( + item: .staticButton(title: ""), + action: .hidKey(keycode: Int(NX_KEYTYPE_NEXT)), + tapAction: TapAction(actionType: TapActionType.hidKey, keycode: Int(NX_KEYTYPE_NEXT)), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.image: imageParameter] + ) }, "weather": { decoder in - enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key ; case icon_type; case shortPressAction } + enum CodingKeys: String, CodingKey { case refreshInterval; case units; case api_key ; case icon_type; case tapAction; case longTapAction } let container = try decoder.container(keyedBy: CodingKeys.self) let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) let units = try container.decodeIfPresent(String.self, forKey: .units) let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) - let shortPressAction = try container.decodeIfPresent(ShortPressAction.self, forKey: .shortPressAction) - 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: .none, longAction: longAction, parameters: [:], shortPressAction: shortPressAction!) + let action = try ActionType(from: decoder) + let tapAction = try container.decodeIfPresent(TapAction.self, forKey: .tapAction) + let longTapAction = try container.decodeIfPresent(LongTapAction.self, forKey: .longTapAction) + return ( + item: .weather(interval: interval ?? 1800.00,units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"), + action: action, + tapAction: tapAction ?? TapAction(actionType: TapActionType.none), + longTapAction: longTapAction ?? LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) }, "currency": { decoder in - enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to; case shortPressAction } + enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to; case tapAction; case longTapAction } let container = try decoder.container(keyedBy: CodingKeys.self) let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) let from = try container.decodeIfPresent(String.self, forKey: .from) let to = try container.decodeIfPresent(String.self, forKey: .to) - let shortPressAction = try container.decodeIfPresent(ShortPressAction.self, forKey: .shortPressAction) - let longAction = try LongActionType(from: decoder) - return (item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), action: .none, longAction: longAction, parameters: [:], shortPressAction: shortPressAction!) + let action = try ActionType(from: decoder) + let tapAction = try container.decodeIfPresent(TapAction.self, forKey: .tapAction) + let longTapAction = try container.decodeIfPresent(LongTapAction.self, forKey: .longTapAction) + return ( + item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), + action: action, + tapAction: tapAction ?? TapAction(actionType: TapActionType.none), + longTapAction: longTapAction ?? LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) }, "dock": { decoder in - return (item: .dock(), action: .none, longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) + return ( + item: .dock(), + action: .none, + tapAction: TapAction(actionType: TapActionType.none), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) }, "inputsource": { decoder in - return (item: .inputsource(), action: .none, longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) + enum CodingKeys: String, CodingKey { case longTapAction } + let container = try decoder.container(keyedBy: CodingKeys.self) + let longTapAction = try container.decodeIfPresent(LongTapAction.self, forKey: .longTapAction) + return ( + item: .inputsource(), + action: .none, + tapAction: TapAction(actionType: TapActionType.none), + longTapAction: longTapAction ?? LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) }, "battery": { decoder in - enum CodingKeys: String, CodingKey { case shortPressAction } + enum CodingKeys: String, CodingKey { case tapAction; case longTapAction } let container = try decoder.container(keyedBy: CodingKeys.self) - let shortPressAction = try container.decodeIfPresent(ShortPressAction.self, forKey: .shortPressAction) - return (item: .battery(), action: .none, longAction: .none, parameters: [:], shortPressAction: shortPressAction!) + let action = try ActionType(from: decoder) + let tapAction = try container.decodeIfPresent(TapAction.self, forKey: .tapAction) + let longTapAction = try container.decodeIfPresent(LongTapAction.self, forKey: .longTapAction) + return ( + item: .battery(), + action: action, + tapAction: tapAction ?? TapAction(actionType: TapActionType.none), + longTapAction: longTapAction ?? LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) }, "timeButton": { decoder in - enum CodingKeys: String, CodingKey { case formatTemplate; case shortPressAction } + enum CodingKeys: String, CodingKey { case formatTemplate; case tapAction; case longTapAction } let container = try decoder.container(keyedBy: CodingKeys.self) let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm" - let shortPressAction = try container.decodeIfPresent(ShortPressAction.self, forKey: .shortPressAction) - return (item: .timeButton(formatTemplate: template), action: .none, longAction: .none, parameters: [:], shortPressAction: shortPressAction!) + let action = try ActionType(from: decoder) + let tapAction = try container.decodeIfPresent(TapAction.self, forKey: .tapAction) + let longTapAction = try container.decodeIfPresent(LongTapAction.self, forKey: .longTapAction) + return ( + item: .timeButton(formatTemplate: template), + action: action, + tapAction: tapAction ?? TapAction(actionType: TapActionType.none), + longTapAction: longTapAction ?? LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) }, "volume": { decoder in enum CodingKeys: String, CodingKey { case image } let container = try decoder.container(keyedBy: CodingKeys.self) if var img = try container.decodeIfPresent(Source.self, forKey: .image) { - return (item: .volume(), action: .none, longAction: .none, parameters: [.image: .image(source: img)], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) + return ( + item: .volume(), + action: .none, + tapAction: TapAction(actionType: TapActionType.none), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.image: .image(source: img)] + ) } else { - return (item: .volume(), action: .none, longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) + return ( + item: .volume(), + action: .none, + tapAction: TapAction(actionType: TapActionType.none), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) } }, "brightness": { decoder in @@ -144,20 +265,46 @@ 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, longAction: .none, parameters: [.image: .image(source: img)], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) + return ( + item: .brightness(refreshInterval: interval ?? 0.5), + action: .none, + tapAction: TapAction(actionType: TapActionType.none), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [.image: .image(source: img)] + ) } else { - return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none)) + return ( + item: .brightness(refreshInterval: interval ?? 0.5), + action: .none, + tapAction: TapAction(actionType: TapActionType.none), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) } }, - "sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.shellScript, executablePath: "/usr/bin/pmset", shellArguments: ["sleepnow"])) }, - "displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), longAction: .none, parameters: [:], shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.shellScript, executablePath: "/usr/bin/pmset", shellArguments: ["displaysleepnow"]))}, + "sleep": { _ in return ( + item: .staticButton(title: "☕️"), + action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), + tapAction: TapAction(actionType: TapActionType.shellScript, executablePath: "/usr/bin/pmset", shellArguments: ["sleepnow"]), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) + }, + "displaySleep": { _ in return ( + item: .staticButton(title: "☕️"), + action: .shellScript(executable: "/usr/bin/pmset",parameters: ["displaysleepnow"]), + tapAction: TapAction(actionType: TapActionType.shellScript, executablePath: "/usr/bin/pmset", shellArguments: ["displaysleepnow"]), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) + }, ] static let sharedInstance = SupportedTypesHolder() func lookup(by type: String) -> ParametersDecoder { return supportedTypes[type] ?? { decoder in - return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), longAction: try LongActionType(from: decoder), shortPressAction: try ShortPressAction(from: decoder), parameters: [:]) + return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), tapAction: try TapAction(from: decoder), longTapAction: try LongTapAction(from: decoder), parameters: [:]) } } @@ -165,9 +312,9 @@ class SupportedTypesHolder { supportedTypes[typename] = decoder } - func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType, shortPressAction: ShortPressAction) { + func register(typename: String, item: ItemType, action: ActionType? = .none, tapAction: TapAction? = TapAction(actionType: TapActionType.none), longTapAction: LongTapAction? = LongTapAction(actionType: TapActionType.none)) { register(typename: typename) { _ in - return (item: item, action: action, longAction: longAction, shortPressAction: shortPressAction, parameters: [:]) + return (item: item, action: action!, tapAction: tapAction!, longTapAction: longTapAction!, parameters: [:]) } } } @@ -306,59 +453,6 @@ 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) { @@ -391,26 +485,6 @@ 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) @@ -513,8 +587,8 @@ enum Align: String, Decodable { case right } -struct ShortPressAction: Codable { - let actionType: PressActionType +struct TapAction: Codable { + let actionType: TapActionType let url: String? let keycode: Int let appleScript: String? @@ -531,7 +605,7 @@ struct ShortPressAction: Codable { case shellArguments } - init(actionType: PressActionType, url: String? = "", keycode: Int? = -1, appleScript: String? = "", executablePath: String? = "", shellArguments: [String]? = [], custom: @escaping () -> Void? = {return}) { + init(actionType: TapActionType, url: String? = "", keycode: Int? = -1, appleScript: String? = "", executablePath: String? = "", shellArguments: [String]? = [], custom: @escaping () -> Void? = {return}) { self.actionType = actionType self.url = url self.keycode = keycode! @@ -543,7 +617,7 @@ struct ShortPressAction: Codable { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - let actionType = try container.decode(PressActionType.self, forKey: .actionType) + let actionType = try container.decode(TapActionType.self, forKey: .actionType) let url = try container.decodeIfPresent(String.self, forKey: .url) ?? "" let keycode = try container.decodeIfPresent(Int.self, forKey: .keycode) ?? -1 var appleScript = try container.decodeIfPresent(String.self, forKey: .appleScript) @@ -560,49 +634,98 @@ struct ShortPressAction: Codable { let shellArguments = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? [] self.init(actionType: actionType, url: url, keycode: keycode, appleScript: appleScript, executablePath: executablePath, shellArguments: shellArguments) } - - enum PressActionType: Codable { - func encode(to encoder: Encoder) throws { +} +struct LongTapAction: Codable { + let actionType: TapActionType + let url: String? + let keycode: Int + let appleScript: String? + let executablePath: String? + let shellArguments: [String]? + let custom: () -> ()? + + enum CodingKeys: String, CodingKey { + case actionType = "type" + case url + case keycode + case appleScript + case executablePath + case shellArguments + } + + init(actionType: TapActionType, url: String? = "", keycode: Int? = -1, appleScript: String? = "", executablePath: String? = "", shellArguments: [String]? = [], custom: @escaping () -> Void? = {return}) { + self.actionType = actionType + self.url = url + self.keycode = keycode! + self.appleScript = appleScript + self.executablePath = executablePath + self.shellArguments = shellArguments + self.custom = custom + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let actionType = try container.decode(TapActionType.self, forKey: .actionType) + let url = try container.decodeIfPresent(String.self, forKey: .url) ?? "" + let keycode = try container.decodeIfPresent(Int.self, forKey: .keycode) ?? -1 + var appleScript = try container.decodeIfPresent(String.self, forKey: .appleScript) + do { + if let test = appleScript?.fileData?.utf8string { + appleScript = test + } else if let test = appleScript?.base64Data?.utf8string { + appleScript = test + } + } catch { + print("error") } + let executablePath = try container.decodeIfPresent(String.self, forKey: .executablePath) ?? "" + let shellArguments = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? [] + self.init(actionType: actionType, url: url, keycode: keycode, appleScript: appleScript, executablePath: executablePath, shellArguments: shellArguments) + } +} + +enum TapActionType: Codable { + func encode(to encoder: Encoder) throws { - case none + } + + case none + case hidKey + case keyPress + case appleScript + case shellScript + case custom + case openUrl + + private enum CodingKeys: String, CodingKey { + case type + } + + private enum ActionTypeRaw: String, Decodable { case hidKey case keyPress case appleScript case shellScript - case custom case openUrl + } + + init(from decoder: Decoder) throws { + self = .none - private enum CodingKeys: String, CodingKey { - case type - } - - private enum ActionTypeRaw: String, Decodable { - case hidKey - case keyPress - case appleScript - case shellScript - case openUrl - } - - init(from decoder: Decoder) throws { - self = .none - - let container = try decoder.singleValueContainer() - let actionType = try container.decode(ActionTypeRaw.self) - switch actionType { - case .hidKey: - self = .hidKey - case .keyPress: - self = .keyPress - case .appleScript: - self = .appleScript - case .shellScript: - self = .shellScript - case .openUrl: - self = .openUrl - } + let container = try decoder.singleValueContainer() + let actionType = try container.decode(ActionTypeRaw.self) + switch actionType { + case .hidKey: + self = .hidKey + case .keyPress: + self = .keyPress + case .appleScript: + self = .appleScript + case .shellScript: + self = .shellScript + case .openUrl: + self = .openUrl } } } diff --git a/MTMR/TouchBarController.swift b/MTMR/TouchBarController.swift index 2586d62..6ea8f5b 100644 --- a/MTMR/TouchBarController.swift +++ b/MTMR/TouchBarController.swift @@ -63,7 +63,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()}), longAction: .none, shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none )) + SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in self?.dismissTouchBar()})) createAndUpdatePreset() } @@ -106,7 +106,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate { let jsonData = presetPath.fileData - return jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, shortPressAction: ShortPressAction(actionType: ShortPressAction.PressActionType.none), additionalParameters: [:])] + return jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"))] } func loadItemDefinitions(jsonItems: [BarItemDefinition]) { @@ -166,20 +166,24 @@ 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) - let shortPressAction = self.shortPressAction(forItem: item) + var action = self.action(forItem: item) + let longTapAction = self.longTapAction(forItem: item) + let tapAction = self.tapAction(forItem: item) + + if item.action == .none { + action = tapAction + } var barItem: NSTouchBarItem! switch item.type { case .staticButton(title: let title): - barItem = CustomButtonTouchBarItem(identifier: identifier, title: title, onTap: shortPressAction, onLongTap: longAction, bezelColor: NSColor.controlColor) + barItem = CustomButtonTouchBarItem(identifier: identifier, title: title, onTap: action, onLongTap: longTapAction, bezelColor: NSColor.controlColor) case .appleScriptTitledButton(source: let source, refreshInterval: let interval): - barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: shortPressAction, onLongTap: longAction) + barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action, onLongTap: longTapAction) case .timeButton(formatTemplate: let template): - barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, onTap: shortPressAction, onLongTap: longAction) + barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, onTap: action, onLongTap: longTapAction) case .battery(): - barItem = BatteryBarItem(identifier: identifier, onTap: shortPressAction, onLongTap: longAction) + barItem = BatteryBarItem(identifier: identifier, onTap: action, onLongTap: longTapAction) case .dock: barItem = AppScrubberTouchBarItem(identifier: identifier) case .volume: @@ -195,11 +199,11 @@ 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: shortPressAction, onLongTap: longAction) + barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type, onTap: action, onLongTap: longTapAction) case .currency(interval: let interval, from: let from, to: let to): - barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, onTap: shortPressAction, onLongTap: longAction) + barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, onTap: action, onLongTap: longTapAction) case .inputsource(): - barItem = InputSourceBarItem(identifier: identifier, onLongTap: longAction) + barItem = InputSourceBarItem(identifier: identifier, onLongTap: longTapAction) } if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth { @@ -258,84 +262,78 @@ 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 {} - } - } - func shortPressAction(forItem item: BarItemDefinition) -> ()->() { - switch item.shortPressAction.actionType { - case ShortPressAction.PressActionType.hidKey: - return { HIDPostAuxKey(item.shortPressAction.keycode) } - case ShortPressAction.PressActionType.keyPress: - return { GenericKeyPress(keyCode: CGKeyCode(item.shortPressAction.keycode)).send() } - case ShortPressAction.PressActionType.appleScript: + func longTapAction(forItem item: BarItemDefinition) -> ()->() { + switch item.longTapAction.actionType { + case TapActionType.hidKey: + return { HIDPostAuxKey(item.longTapAction.keycode) } + case TapActionType.keyPress: + return { GenericKeyPress(keyCode: CGKeyCode(item.longTapAction.keycode)).send() } + case TapActionType.appleScript: return { - let appleScriptSource = NSAppleScript(source: item.shortPressAction.appleScript!) + let appleScriptSource = NSAppleScript(source: item.longTapAction.appleScript!) var error: NSDictionary? appleScriptSource?.executeAndReturnError(&error) if let error = error { print("error \(error) when handling \(item) ") } } - case ShortPressAction.PressActionType.shellScript: + case TapActionType.shellScript: return { let task = Process() - task.launchPath = item.shortPressAction.executablePath - task.arguments = item.shortPressAction.shellArguments + task.launchPath = item.longTapAction.executablePath + task.arguments = item.longTapAction.shellArguments task.launch() } - case ShortPressAction.PressActionType.openUrl: + case TapActionType.openUrl: return { - if let url = URL(string: item.shortPressAction.url!), NSWorkspace.shared.open(url) { + if let url = URL(string: item.longTapAction.url!), NSWorkspace.shared.open(url) { #if DEBUG print("URL was successfully opened") #endif } } - case ShortPressAction.PressActionType.none: + case TapActionType.none: return {} - case ShortPressAction.PressActionType.custom: - return item.shortPressAction.custom as! () -> () - case .none: + case TapActionType.custom: + return item.longTapAction.custom as! () -> () + } + } + + func tapAction(forItem item: BarItemDefinition) -> ()->() { + switch item.tapAction.actionType { + case TapActionType.hidKey: + return { HIDPostAuxKey(item.tapAction.keycode) } + case TapActionType.keyPress: + return { GenericKeyPress(keyCode: CGKeyCode(item.tapAction.keycode)).send() } + case TapActionType.appleScript: + return { + let appleScriptSource = NSAppleScript(source: item.tapAction.appleScript!) + var error: NSDictionary? + appleScriptSource?.executeAndReturnError(&error) + if let error = error { + print("error \(error) when handling \(item) ") + } + } + case TapActionType.shellScript: + return { + let task = Process() + task.launchPath = item.tapAction.executablePath + task.arguments = item.tapAction.shellArguments + task.launch() + } + case TapActionType.openUrl: + return { + if let url = URL(string: item.tapAction.url!), NSWorkspace.shared.open(url) { + #if DEBUG + print("URL was successfully opened") + #endif + } + } + case TapActionType.none: return {} + case TapActionType.custom: + return item.tapAction.custom as! () -> () } } }