diff --git a/MTMR/AppDelegate.swift b/MTMR/AppDelegate.swift index 6c55260..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, 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 b564cd3..60699cd 100644 --- a/MTMR/ItemsParsing.swift +++ b/MTMR/ItemsParsing.swift @@ -3,114 +3,261 @@ 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"))] } } struct BarItemDefinition: Decodable { let type: ItemType let action: ActionType - let longAction: LongActionType + let longTapAction: LongTapAction + let tapAction: TapAction let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter] - + private enum CodingKeys: String, CodingKey { case type + case tapAction + case longTapAction } - init(type: ItemType, action: ActionType, longAction: LongActionType, 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.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 tapAction = try container.decodeIfPresent(TapAction.self, forKey: .tapAction) +// let longTapAction = try container.decodeIfPresent(LongTapAction.self, forKey: .longTapAction) 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, tapAction, longTapAction, parameters) = result { + parameters.forEach { additionalParameters[$0] = $1 } + self.init(type: itemType, action: action, tapAction: tapAction, longTapAction: longTapAction, additionalParameters: additionalParameters) + } else { + self.init(type: .staticButton(title: "unknown"), 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, 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)]) }, - "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), + 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]) + return ( + item: .staticButton(title: ""), + action: .hidKey(keycode: Int(NX_KEYTYPE_BRIGHTNESS_UP)), + tapAction: TapAction(actionType: TapActionType.hidKey, keycode: Int(NX_KEYTYPE_BRIGHTNESS_UP)), + 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]) + return ( + item: .staticButton(title: ""), + action: .hidKey(keycode: Int(NX_KEYTYPE_BRIGHTNESS_DOWN)), + tapAction: TapAction(actionType: TapActionType.hidKey, keycode: Int(NX_KEYTYPE_BRIGHTNESS_DOWN)), + 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]) + 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]) + 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]) + 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]) + 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]) + 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]) + 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 } + 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 action = try ActionType(from: decoder) - let longAction = try LongActionType(from: decoder) - return (item: .weather(interval: interval ?? 1800.00, units: units ?? "metric", api_key: api_key ?? "32c4256d09a4c52b38aecddba7a078f6", icon_type: icon_type ?? "text"), action: action, longAction: longAction, parameters: [:]) + 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 } + 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 action = try ActionType(from: decoder) - let longAction = try LongActionType(from: decoder) - return (item: .currency(interval: interval ?? 600.00, from: from ?? "RUB", to: to ?? "USD"), action: action, longAction: longAction, parameters: [:]) + 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: [:]) + 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: [:]) + 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 tapAction; case longTapAction } + let container = try decoder.container(keyedBy: CodingKeys.self) + 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 tapAction; case longTapAction } + let container = try decoder.container(keyedBy: CodingKeys.self) + let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm" + 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)]) + 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: [:]) + return ( + item: .volume(), + action: .none, + tapAction: TapAction(actionType: TapActionType.none), + longTapAction: LongTapAction(actionType: TapActionType.none), + parameters: [:] + ) } }, "brightness": { decoder in @@ -118,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)]) + 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: [:]) + 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: [:]) }, - "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"]), + 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), parameters: [:]) + return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), tapAction: try TapAction(from: decoder), longTapAction: try LongTapAction(from: decoder), parameters: [:]) } } @@ -139,13 +312,14 @@ class SupportedTypesHolder { supportedTypes[typename] = decoder } - func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) { + 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, parameters: [:]) + return (item: item, action: action!, tapAction: tapAction!, longTapAction: longTapAction!, parameters: [:]) } } } + enum ItemType: Decodable { case staticButton(title: String) case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double) @@ -279,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) { @@ -364,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) @@ -485,3 +586,146 @@ enum Align: String, Decodable { case center case right } + +struct TapAction: 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) + } +} + +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 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/KeyPress.swift b/MTMR/KeyPress.swift index fe0f109..02bbaab 100644 --- a/MTMR/KeyPress.swift +++ b/MTMR/KeyPress.swift @@ -56,18 +56,30 @@ func HIDPostAuxKey(_ key: Int) { // hidsystem/ev_keymap.h -let NX_KEYTYPE_SOUND_UP = 0 +let NX_KEYTYPE_SOUND_UP = 0 let NX_KEYTYPE_SOUND_DOWN = 1 +let NX_KEYTYPE_BRIGHTNESS_UP = 2 +let NX_KEYTYPE_BRIGHTNESS_DOWN = 3 +let NX_KEYTYPE_CAPS_LOCK = 4 +let NX_KEYTYPE_HELP = 5 +let NX_POWER_KEY = 6 let NX_KEYTYPE_MUTE = 7 +let NX_UP_ARROW_KEY = 8 +let NX_DOWN_ARROW_KEY = 9 +let NX_KEYTYPE_NUM_LOCK = 10 -let NX_KEYTYPE_BRIGHTNESS_UP = 2 -let NX_KEYTYPE_BRIGHTNESS_DOWN = 3 - -let NX_KEYTYPE_PLAY = 16 -let NX_KEYTYPE_NEXT = 17 -let NX_KEYTYPE_PREVIOUS = 18 - - - +let NX_KEYTYPE_CONTRAST_UP = 11 +let NX_KEYTYPE_CONTRAST_DOWN = 12 +let NX_KEYTYPE_LAUNCH_PANEL = 13 +let NX_KEYTYPE_EJECT = 14 +let NX_KEYTYPE_VIDMIRROR = 15 +let NX_KEYTYPE_PLAY = 16 +let NX_KEYTYPE_NEXT = 17 +let NX_KEYTYPE_PREVIOUS = 18 +let NX_KEYTYPE_FAST = 19 +let NX_KEYTYPE_REWIND = 20 +let NX_KEYTYPE_ILLUMINATION_UP = 21 +let NX_KEYTYPE_ILLUMINATION_DOWN = 22 +let NX_KEYTYPE_ILLUMINATION_TOGGLE = 23 diff --git a/MTMR/TouchBarController.swift b/MTMR/TouchBarController.swift index a3b28ca..50b2129 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()})) createAndUpdatePreset() } @@ -110,7 +110,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"))] } func loadItemDefinitions(jsonItems: [BarItemDefinition]) { @@ -173,19 +173,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) + 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: action, 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: action, 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: action, onLongTap: longAction) + barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, onTap: action, onLongTap: longTapAction) case .battery(): - barItem = BatteryBarItem(identifier: identifier, onTap: action, onLongTap: longAction) + barItem = BatteryBarItem(identifier: identifier, onTap: action, onLongTap: longTapAction) case .dock: barItem = AppScrubberTouchBarItem(identifier: identifier) case .volume: @@ -201,11 +206,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: 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: action, onLongTap: longAction) + barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, onTap: action, onLongTap: longTapAction) case .inputsource(): - barItem = InputSourceBarItem(identifier: identifier, onTap: action, onLongTap: longAction) + barItem = InputSourceBarItem(identifier: identifier, onLongTap: longTapAction) } if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth { @@ -263,47 +268,79 @@ class TouchBarController: NSObject, NSTouchBarDelegate { return {} } } - - 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 {} - } + + 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.longTapAction.appleScript!) var error: NSDictionary? - appleScript.executeAndReturnError(&error) + appleScriptSource?.executeAndReturnError(&error) if let error = error { print("error \(error) when handling \(item) ") } } - case .shellScript(executable: let executable, parameters: let parameters): + case TapActionType.shellScript: return { let task = Process() - task.launchPath = executable - task.arguments = parameters + task.launchPath = item.longTapAction.executablePath + task.arguments = item.longTapAction.shellArguments task.launch() } - case .openUrl(url: let url): + case TapActionType.openUrl: return { - if let url = URL(string: 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 - } else { - print("error", url) } } - case .custom(closure: let closure): - return closure - case .none: + case TapActionType.none: return {} + 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! () -> () } } } diff --git a/MTMR/Widgets/InputSourceBarItem.swift b/MTMR/Widgets/InputSourceBarItem.swift index f32abd0..0e208d0 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()