mirror of
https://github.com/Toxblh/MTMR.git
synced 2026-01-10 17:08:39 +00:00
commit
347bad8058
@ -7,6 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368EDDE620812A1D00E10953 /* ScrollViewItem.swift */; };
|
||||||
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */; };
|
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */; };
|
||||||
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */; };
|
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */; };
|
||||||
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
|
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
|
||||||
@ -49,6 +50,7 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewItem.swift; sourceTree = "<group>"; };
|
||||||
36BDC22F207CDA8600FCFEBE /* TECHNICAL_DEBT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TECHNICAL_DEBT.md; sourceTree = "<group>"; };
|
36BDC22F207CDA8600FCFEBE /* TECHNICAL_DEBT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TECHNICAL_DEBT.md; sourceTree = "<group>"; };
|
||||||
36C2ECD2207B3B1D003CDA33 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
36C2ECD2207B3B1D003CDA33 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeTouchBarItem.swift; sourceTree = "<group>"; };
|
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeTouchBarItem.swift; sourceTree = "<group>"; };
|
||||||
@ -160,6 +162,7 @@
|
|||||||
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */,
|
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */,
|
||||||
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
|
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
|
||||||
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
|
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
|
||||||
|
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */,
|
||||||
);
|
);
|
||||||
path = MTMR;
|
path = MTMR;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -329,6 +332,7 @@
|
|||||||
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */,
|
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */,
|
||||||
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */,
|
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */,
|
||||||
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */,
|
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */,
|
||||||
|
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,13 +12,13 @@ extension Data {
|
|||||||
struct BarItemDefinition: Decodable {
|
struct BarItemDefinition: Decodable {
|
||||||
let type: ItemType
|
let type: ItemType
|
||||||
let action: ActionType
|
let action: ActionType
|
||||||
let additionalParameters: [GeneralParameter]
|
let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case type
|
case type
|
||||||
}
|
}
|
||||||
|
|
||||||
init(type: ItemType, action: ActionType, additionalParameters: [GeneralParameter]) {
|
init(type: ItemType, action: ActionType, additionalParameters: [GeneralParameters.CodingKeys:GeneralParameter]) {
|
||||||
self.type = type
|
self.type = type
|
||||||
self.action = action
|
self.action = action
|
||||||
self.additionalParameters = additionalParameters
|
self.additionalParameters = additionalParameters
|
||||||
@ -28,10 +28,11 @@ struct BarItemDefinition: Decodable {
|
|||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
let type = try container.decode(String.self, forKey: .type)
|
let type = try container.decode(String.self, forKey: .type)
|
||||||
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type)
|
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type)
|
||||||
let additionalParameters = try GeneralParameters(from: decoder).parameters
|
var additionalParameters = try GeneralParameters(from: decoder).parameters
|
||||||
if let result = try? parametersDecoder(decoder),
|
if let result = try? parametersDecoder(decoder),
|
||||||
case let (itemType, action, parameters) = result {
|
case let (itemType, action, parameters) = result {
|
||||||
self.init(type: itemType, action: action, additionalParameters: additionalParameters + parameters)
|
parameters.forEach { additionalParameters[$0] = $1 }
|
||||||
|
self.init(type: itemType, action: action, additionalParameters: additionalParameters)
|
||||||
} else {
|
} else {
|
||||||
self.init(type: .staticButton(title: "unknown"), action: .none, additionalParameters: additionalParameters)
|
self.init(type: .staticButton(title: "unknown"), action: .none, additionalParameters: additionalParameters)
|
||||||
}
|
}
|
||||||
@ -40,36 +41,36 @@ struct BarItemDefinition: Decodable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SupportedTypesHolder {
|
class SupportedTypesHolder {
|
||||||
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, parameters: [GeneralParameter])
|
typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType, parameters: [GeneralParameters.CodingKeys: GeneralParameter])
|
||||||
private var supportedTypes: [String: ParametersDecoder] = [
|
private var supportedTypes: [String: ParametersDecoder] = [
|
||||||
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), parameters: []) },
|
"escape": { _ in return (item: .staticButton(title: "esc"), action: .keyPress(keycode: 53), parameters: [.align: .align(.left)]) },
|
||||||
"brightnessUp": { _ in
|
"brightnessUp": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
|
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
|
||||||
return (item: .staticButton(title: ""), action: .keyPress(keycode: 113), parameters: [imageParameter])
|
return (item: .staticButton(title: ""), action: .keyPress(keycode: 113), parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
"brightnessDown": { _ in
|
"brightnessDown": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
|
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
|
||||||
return (item: .staticButton(title: ""), action: .keyPress(keycode: 107), parameters: [imageParameter])
|
return (item: .staticButton(title: ""), action: .keyPress(keycode: 107), parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
"volumeDown": { _ in
|
"volumeDown": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), parameters: [imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN), parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
"volumeUp": { _ in
|
"volumeUp": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeUpTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeUpTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), parameters: [imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP), parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
"previous": { _ in
|
"previous": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarRewindTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarRewindTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), parameters: [imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS), parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
"play": { _ in
|
"play": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarPlayPauseTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarPlayPauseTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), parameters: [imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_PLAY), parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
"next": { _ in
|
"next": { _ in
|
||||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarFastForwardTemplate)!)
|
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarFastForwardTemplate)!)
|
||||||
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), parameters: [imageParameter])
|
return (item: .staticButton(title: ""), action: .hidKey(keycode: NX_KEYTYPE_NEXT), parameters: [.image: imageParameter])
|
||||||
},
|
},
|
||||||
"weather": { decoder in
|
"weather": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval }
|
enum CodingKeys: String, CodingKey { case refreshInterval }
|
||||||
@ -77,13 +78,13 @@ class SupportedTypesHolder {
|
|||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||||
let scriptPath = Bundle.main.path(forResource: "Weather", ofType: "scpt")!
|
let scriptPath = Bundle.main.path(forResource: "Weather", ofType: "scpt")!
|
||||||
let item = ItemType.appleScriptTitledButton(source: Source(filePath: scriptPath), refreshInterval: interval ?? 1800.0)
|
let item = ItemType.appleScriptTitledButton(source: Source(filePath: scriptPath), refreshInterval: interval ?? 1800.0)
|
||||||
return (item: item, action: .none, parameters: [])
|
return (item: item, action: .none, parameters: [:])
|
||||||
},
|
},
|
||||||
"brightness": { decoder in
|
"brightness": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval }
|
enum CodingKeys: String, CodingKey { case refreshInterval }
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||||
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, parameters: [])
|
return (item: .brightness(refreshInterval: interval ?? 0.5), action: .none, parameters: [:])
|
||||||
},
|
},
|
||||||
"battery": { decoder in
|
"battery": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval }
|
enum CodingKeys: String, CodingKey { case refreshInterval }
|
||||||
@ -91,17 +92,17 @@ class SupportedTypesHolder {
|
|||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||||
let scriptPath = Bundle.main.path(forResource: "Battery", ofType: "scpt")!
|
let scriptPath = Bundle.main.path(forResource: "Battery", ofType: "scpt")!
|
||||||
let item = ItemType.appleScriptTitledButton(source: Source(filePath: scriptPath), refreshInterval: interval ?? 1800.0)
|
let item = ItemType.appleScriptTitledButton(source: Source(filePath: scriptPath), refreshInterval: interval ?? 1800.0)
|
||||||
return (item: item, action: .none, parameters: [])
|
return (item: item, action: .none, parameters: [:])
|
||||||
},
|
},
|
||||||
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), parameters: []) },
|
"sleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]), parameters: [:]) },
|
||||||
"displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), parameters: []) },
|
"displaySleep": { _ in return (item: .staticButton(title: "☕️"), action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]), parameters: [:]) },
|
||||||
]
|
]
|
||||||
|
|
||||||
static let sharedInstance = SupportedTypesHolder()
|
static let sharedInstance = SupportedTypesHolder()
|
||||||
|
|
||||||
func lookup(by type: String) -> ParametersDecoder {
|
func lookup(by type: String) -> ParametersDecoder {
|
||||||
return supportedTypes[type] ?? { decoder in
|
return supportedTypes[type] ?? { decoder in
|
||||||
return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), parameters: [])
|
return (item: try ItemType(from: decoder), action: try ActionType(from: decoder), parameters: [:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +112,7 @@ class SupportedTypesHolder {
|
|||||||
|
|
||||||
func register(typename: String, item: ItemType, action: ActionType) {
|
func register(typename: String, item: ItemType, action: ActionType) {
|
||||||
register(typename: typename) { _ in
|
register(typename: typename) { _ in
|
||||||
return (item: item, action: action, parameters: [])
|
return (item: item, action: action, parameters: [:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,7 +121,6 @@ enum ItemType: Decodable {
|
|||||||
case staticButton(title: String)
|
case staticButton(title: String)
|
||||||
case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double)
|
case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double)
|
||||||
case timeButton(formatTemplate: String)
|
case timeButton(formatTemplate: String)
|
||||||
case flexSpace()
|
|
||||||
case volume()
|
case volume()
|
||||||
case brightness(refreshInterval: Double)
|
case brightness(refreshInterval: Double)
|
||||||
|
|
||||||
@ -137,7 +137,6 @@ enum ItemType: Decodable {
|
|||||||
case staticButton
|
case staticButton
|
||||||
case appleScriptTitledButton
|
case appleScriptTitledButton
|
||||||
case timeButton
|
case timeButton
|
||||||
case flexSpace
|
|
||||||
case volume
|
case volume
|
||||||
case brightness
|
case brightness
|
||||||
}
|
}
|
||||||
@ -156,8 +155,6 @@ enum ItemType: Decodable {
|
|||||||
case .timeButton:
|
case .timeButton:
|
||||||
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
|
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
|
||||||
self = .timeButton(formatTemplate: template)
|
self = .timeButton(formatTemplate: template)
|
||||||
case .flexSpace:
|
|
||||||
self = .flexSpace()
|
|
||||||
case .volume:
|
case .volume:
|
||||||
self = .volume()
|
self = .volume()
|
||||||
case .brightness:
|
case .brightness:
|
||||||
@ -218,8 +215,6 @@ func ==(lhs: ItemType, rhs: ItemType) -> Bool {
|
|||||||
switch (lhs, rhs) {
|
switch (lhs, rhs) {
|
||||||
case let (.staticButton(a), .staticButton(b)):
|
case let (.staticButton(a), .staticButton(b)):
|
||||||
return a == b
|
return a == b
|
||||||
case let (.flexSpace(a), .flexSpace(b)):
|
|
||||||
return a == b
|
|
||||||
case let (.appleScriptTitledButton(a, b), .appleScriptTitledButton(c, d)):
|
case let (.appleScriptTitledButton(a, b), .appleScriptTitledButton(c, d)):
|
||||||
return a == c && b == d
|
return a == c && b == d
|
||||||
|
|
||||||
@ -248,24 +243,28 @@ func ==(lhs: ActionType, rhs: ActionType) -> Bool {
|
|||||||
enum GeneralParameter {
|
enum GeneralParameter {
|
||||||
case width(_: CGFloat)
|
case width(_: CGFloat)
|
||||||
case image(source: SourceProtocol)
|
case image(source: SourceProtocol)
|
||||||
|
case align(_: Align)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GeneralParameters: Decodable {
|
struct GeneralParameters: Decodable {
|
||||||
let parameters: [GeneralParameter]
|
let parameters: [GeneralParameters.CodingKeys: GeneralParameter]
|
||||||
|
|
||||||
fileprivate enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case width
|
case width
|
||||||
case image
|
case image
|
||||||
|
case align
|
||||||
}
|
}
|
||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
var result: [GeneralParameter] = []
|
var result: [GeneralParameters.CodingKeys: GeneralParameter] = [:]
|
||||||
if let value = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
|
if let value = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
|
||||||
result.append(.width(value))
|
result[.width] = .width(value)
|
||||||
}
|
}
|
||||||
if let imageSource = try container.decodeIfPresent(Source.self, forKey: .image) {
|
if let imageSource = try container.decodeIfPresent(Source.self, forKey: .image) {
|
||||||
result.append(.image(source: imageSource))
|
result[.image] = .image(source: imageSource)
|
||||||
}
|
}
|
||||||
|
let align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center
|
||||||
|
result[.align] = .align(align)
|
||||||
parameters = result
|
parameters = result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,3 +335,9 @@ extension Data {
|
|||||||
return NSImage(data: self)?.resize(maxSize: NSSize(width: 24, height: 24))
|
return NSImage(data: self)?.resize(maxSize: NSSize(width: 24, height: 24))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Align: String, Decodable {
|
||||||
|
case left
|
||||||
|
case center
|
||||||
|
case right
|
||||||
|
}
|
||||||
|
|||||||
19
MTMR/ScrollViewItem.swift
Normal file
19
MTMR/ScrollViewItem.swift
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ScrollViewItem: NSCustomTouchBarItem {
|
||||||
|
|
||||||
|
init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem]) {
|
||||||
|
super.init(identifier: identifier)
|
||||||
|
let views = items.flatMap { $0.view }
|
||||||
|
let stackView = NSStackView(views: views)
|
||||||
|
stackView.orientation = .horizontal
|
||||||
|
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
|
||||||
|
scrollView.documentView = stackView
|
||||||
|
self.view = scrollView
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -23,8 +23,6 @@ extension ItemType {
|
|||||||
return "com.toxblh.mtmr.appleScriptButton."
|
return "com.toxblh.mtmr.appleScriptButton."
|
||||||
case .timeButton(formatTemplate: _):
|
case .timeButton(formatTemplate: _):
|
||||||
return "com.toxblh.mtmr.timeButton."
|
return "com.toxblh.mtmr.timeButton."
|
||||||
case .flexSpace():
|
|
||||||
return "NSTouchBarItem.Identifier.flexibleSpace"
|
|
||||||
case .volume():
|
case .volume():
|
||||||
return "com.toxblh.mtmr.volume"
|
return "com.toxblh.mtmr.volume"
|
||||||
case .brightness(refreshInterval: _):
|
case .brightness(refreshInterval: _):
|
||||||
@ -36,6 +34,7 @@ extension ItemType {
|
|||||||
|
|
||||||
extension NSTouchBarItem.Identifier {
|
extension NSTouchBarItem.Identifier {
|
||||||
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
|
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
|
||||||
|
static let centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea")
|
||||||
}
|
}
|
||||||
|
|
||||||
class TouchBarController: NSObject, NSTouchBarDelegate {
|
class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||||
@ -44,7 +43,11 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
|
|
||||||
let touchBar = NSTouchBar()
|
let touchBar = NSTouchBar()
|
||||||
|
|
||||||
var items: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
||||||
|
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
|
||||||
|
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||||
|
var centerItems: [NSTouchBarItem] = []
|
||||||
|
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||||
|
|
||||||
private override init() {
|
private override init() {
|
||||||
super.init()
|
super.init()
|
||||||
@ -52,13 +55,18 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
self?.dismissTouchBar()
|
self?.dismissTouchBar()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
loadItems()
|
loadItemDefinitions()
|
||||||
|
createItems()
|
||||||
|
centerItems = self.itemDefinitions.flatMap { (identifier, definition) -> NSTouchBarItem? in
|
||||||
|
return definition.align == .center ? items[identifier] : nil
|
||||||
|
}
|
||||||
|
|
||||||
touchBar.delegate = self
|
touchBar.delegate = self
|
||||||
|
touchBar.defaultItemIdentifiers = self.leftIdentifiers + [.centerScrollArea] + self.rightIdentifiers
|
||||||
self.presentTouchBar()
|
self.presentTouchBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadItems() {
|
func loadItemDefinitions() {
|
||||||
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
||||||
let presetPath = appSupportDirectory.appending("/items.json")
|
let presetPath = appSupportDirectory.appending("/items.json")
|
||||||
if !FileManager.default.fileExists(atPath: presetPath),
|
if !FileManager.default.fileExists(atPath: presetPath),
|
||||||
@ -67,18 +75,27 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
try? FileManager.default.copyItem(atPath: defaultPreset, toPath: presetPath)
|
try? FileManager.default.copyItem(atPath: defaultPreset, toPath: presetPath)
|
||||||
}
|
}
|
||||||
let jsonData = presetPath.fileData
|
let jsonData = presetPath.fileData
|
||||||
let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, additionalParameters: [])]
|
let jsonItems = jsonData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, additionalParameters: [:])]
|
||||||
|
|
||||||
for item in jsonItems {
|
for item in jsonItems {
|
||||||
let identifierString = item.type.identifierBase.appending(UUID().uuidString)
|
let identifierString = item.type.identifierBase.appending(UUID().uuidString)
|
||||||
let identifier = item.type == ItemType.flexSpace()
|
let identifier = NSTouchBarItem.Identifier(identifierString)
|
||||||
? NSTouchBarItem.Identifier.flexibleSpace
|
itemDefinitions[identifier] = item
|
||||||
: NSTouchBarItem.Identifier(identifierString)
|
if item.align == .left {
|
||||||
items[identifier] = item
|
leftIdentifiers.append(identifier)
|
||||||
touchBar.defaultItemIdentifiers += [identifier]
|
}
|
||||||
|
if item.align == .right {
|
||||||
|
rightIdentifiers.append(identifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createItems() {
|
||||||
|
for (identifier, definition) in self.itemDefinitions {
|
||||||
|
self.items[identifier] = self.createItem(forIdentifier: identifier, definition: definition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func setupControlStripPresence() {
|
func setupControlStripPresence() {
|
||||||
DFRSystemModalShowsCloseBoxWhenFrontMost(false)
|
DFRSystemModalShowsCloseBoxWhenFrontMost(false)
|
||||||
let item = NSCustomTouchBarItem(identifier: .controlStripItem)
|
let item = NSCustomTouchBarItem(identifier: .controlStripItem)
|
||||||
@ -100,9 +117,19 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
||||||
guard let item = self.items[identifier] else {
|
if identifier == .centerScrollArea {
|
||||||
|
return ScrollViewItem(identifier: identifier, items: centerItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let item = self.items[identifier],
|
||||||
|
let definition = self.itemDefinitions[identifier],
|
||||||
|
definition.align != .center else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? {
|
||||||
let action = self.action(forItem: item)
|
let action = self.action(forItem: item)
|
||||||
|
|
||||||
var barItem: NSTouchBarItem!
|
var barItem: NSTouchBarItem!
|
||||||
@ -113,24 +140,20 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action)
|
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action)
|
||||||
case .timeButton(formatTemplate: let template):
|
case .timeButton(formatTemplate: let template):
|
||||||
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template)
|
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template)
|
||||||
case .flexSpace:
|
|
||||||
barItem = nil
|
|
||||||
case .volume:
|
case .volume:
|
||||||
barItem = VolumeViewController(identifier: identifier)
|
barItem = VolumeViewController(identifier: identifier)
|
||||||
case .brightness(refreshInterval: let interval):
|
case .brightness(refreshInterval: let interval):
|
||||||
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
|
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
|
||||||
}
|
}
|
||||||
for parameter in item.additionalParameters {
|
if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
|
||||||
if case .width(let value) = parameter, let widthBarItem = barItem as? CanSetWidth {
|
widthBarItem.setWidth(value: value)
|
||||||
widthBarItem.setWidth(value: value)
|
}
|
||||||
}
|
if case .image(let source)? = item.additionalParameters[.image], let item = barItem as? CustomButtonTouchBarItem {
|
||||||
if case .image(let source) = parameter, let item = barItem as? CustomButtonTouchBarItem {
|
let button = item.button!
|
||||||
let button = item.button!
|
button.image = source.image
|
||||||
button.image = source.image
|
button.imagePosition = .imageLeading
|
||||||
button.imagePosition = .imageLeading
|
button.imageHugsTitle = true
|
||||||
button.imageHugsTitle = true
|
button.bezelColor = .clear
|
||||||
button.bezelColor = .clear
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return barItem
|
return barItem
|
||||||
}
|
}
|
||||||
@ -179,3 +202,11 @@ extension NSCustomTouchBarItem: CanSetWidth {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension BarItemDefinition {
|
||||||
|
var align: Align {
|
||||||
|
if case .align(let result)? = self.additionalParameters[.align] {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return .center
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -11,7 +11,6 @@
|
|||||||
"keycode": 113,
|
"keycode": 113,
|
||||||
"width": 36,
|
"width": 36,
|
||||||
},
|
},
|
||||||
{ "type": "flexSpace" },
|
|
||||||
{ "type": "volumeDown", "width": 44 },
|
{ "type": "volumeDown", "width": 44 },
|
||||||
{ "type": "volumeUp", "width": 44 },
|
{ "type": "volumeUp", "width": 44 },
|
||||||
{ "type": "previous" },
|
{ "type": "previous" },
|
||||||
|
|||||||
61
README.md
61
README.md
@ -25,7 +25,7 @@ My the idea is to create the program like a platform for plugins for customizati
|
|||||||
- [x] Status menu: "preferences", "quit"
|
- [x] Status menu: "preferences", "quit"
|
||||||
- [x] JSON or another approch for save preset, maybe in `~/Library/Application Support/MTMR/`
|
- [x] JSON or another approch for save preset, maybe in `~/Library/Application Support/MTMR/`
|
||||||
- [x] Custom buttons size, actions by click
|
- [x] Custom buttons size, actions by click
|
||||||
- [ ] Layout: [always left, NSSliderView for center, always right]
|
- [x] Layout: [always left, NSSliderView for center, always right]
|
||||||
- [ ] Overwrite default values from item types (e.g. title for brightness)
|
- [ ] Overwrite default values from item types (e.g. title for brightness)
|
||||||
- [ ] System for autoupdate (maybe https://sparkle-project.org/)
|
- [ ] System for autoupdate (maybe https://sparkle-project.org/)
|
||||||
|
|
||||||
@ -101,11 +101,6 @@ File for customize your preset for MTMR: `open ~/Library/Application Support/MTM
|
|||||||
"formatTemplate": "HH:mm" //optional
|
"formatTemplate": "HH:mm" //optional
|
||||||
```
|
```
|
||||||
|
|
||||||
- `flexSpace` – to easily split touch bar in two parts: left and right
|
|
||||||
```json
|
|
||||||
"type": "flexSpace"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Actions:
|
## Actions:
|
||||||
- `hidKey`
|
- `hidKey`
|
||||||
```json
|
```json
|
||||||
@ -143,17 +138,24 @@ File for customize your preset for MTMR: `open ~/Library/Application Support/MTM
|
|||||||
"width": 34
|
"width": 34
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- `align` can stick the time to the side. default is center
|
||||||
|
```js
|
||||||
|
"align": "left" //or "right" or "center"
|
||||||
|
```
|
||||||
|
|
||||||
## Example configuration:
|
## Example configuration:
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{ "type": "escape", "width": 110 },
|
{ "type": "escape", "width": 110 },
|
||||||
{ "type": "exitTouchbar" },
|
{ "type": "exitTouchbar", "align": "left" },
|
||||||
{
|
{
|
||||||
"type": "brightnessUp",
|
"type": "brightnessUp",
|
||||||
|
"align": "left",
|
||||||
"width": 36
|
"width": 36
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "staticButton",
|
"type": "staticButton",
|
||||||
|
"align": "left",
|
||||||
"title": "🔆",
|
"title": "🔆",
|
||||||
"action": "keyPress",
|
"action": "keyPress",
|
||||||
"keycode": 113,
|
"keycode": 113,
|
||||||
@ -169,6 +171,7 @@ File for customize your preset for MTMR: `open ~/Library/Application Support/MTM
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "staticButton",
|
"type": "staticButton",
|
||||||
|
"align": "left",
|
||||||
"image": { "base64" : "%base64Finder%"},
|
"image": { "base64" : "%base64Finder%"},
|
||||||
"action": "appleScript",
|
"action": "appleScript",
|
||||||
"actionAppleScript": {
|
"actionAppleScript": {
|
||||||
@ -183,18 +186,17 @@ File for customize your preset for MTMR: `open ~/Library/Application Support/MTM
|
|||||||
},
|
},
|
||||||
"refreshInterval": 1
|
"refreshInterval": 1
|
||||||
},
|
},
|
||||||
{ "type": "flexSpace" },
|
{ "type": "previous", "width": 36, "align": "right" },
|
||||||
{ "type": "previous", "width": 36 },
|
{ "type": "play", "width": 36, "align": "right" },
|
||||||
{ "type": "play", "width": 36 },
|
{ "type": "next", "width": 36, "align": "right" },
|
||||||
{ "type": "next", "width": 36 },
|
{ "type": "sleep", "width": 36 , "align": "right"},
|
||||||
{ "type": "sleep", "width": 36 },
|
{ "type": "displaySleep", "align": "right" },
|
||||||
{ "type": "displaySleep" },
|
{ "type": "weather", "refreshInterval": 1800, "width": 70, "align": "right" },
|
||||||
{ "type": "weather", "refreshInterval": 1800, "width": 70 },
|
{ "type": "volumeDown", "width": 36 , "align": "right"},
|
||||||
{ "type": "volumeDown", "width": 36 },
|
{ "type": "volumeUp", "width": 36 , "align": "right"},
|
||||||
{ "type": "volumeUp", "width": 36 },
|
{ "type": "battery", "refreshInterval": 60 , "align": "right"},
|
||||||
{ "type": "battery", "refreshInterval": 60 },
|
{ "type": "appleScriptTitledButton", "refreshInterval": 1800, "source": { "filePath": "/Users/redetection/Library/Application Support/MTMR/Weather.scpt"} , "align": "right"},
|
||||||
{ "type": "appleScriptTitledButton", "refreshInterval": 1800, "source": { "filePath": "/Users/redetection/Library/Application Support/MTMR/Weather.scpt"} },
|
{ "type": "timeButton", "formatTemplate": "HH:mm", "width": 64, "align": "right" }
|
||||||
{ "type": "timeButton", "formatTemplate": "HH:mm", "width": 64 }
|
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -203,26 +205,7 @@ File for customize your preset for MTMR: `open ~/Library/Application Support/MTM
|
|||||||
|
|
||||||
[@Toxblh preset](Resources/toxblh.json)
|
[@Toxblh preset](Resources/toxblh.json)
|
||||||
|
|
||||||
[@ReDetection preset](Resources/ReDetection.json):
|
[@ReDetection preset](Resources/ReDetection.json)
|
||||||
```json
|
|
||||||
[
|
|
||||||
{ "type": "escape", "width": 110 },
|
|
||||||
{ "type": "exitTouchbar" },
|
|
||||||
{ "type": "brightnessDown", "width": 40 },
|
|
||||||
{ "type": "brightnessUp", "width": 40 },
|
|
||||||
{ "type": "appleScriptTitledButton", "refreshInterval": 15, "source": { "inline": "if application \"Safari\" is running then\r\ttell application \"Safari\"\r\t\trepeat with t in tabs of windows\r\t\t\ttell t\r\t\t\t\tif URL starts with \"https:\/\/music.yandex.ru\" and name does not end with \"на Яндекс.Музыке\" then\r\t\t\t\t\treturn name of t as text\r\t\t\t\tend if\r\t\t\tend tell\r\t\tend repeat\r\tend tell\rend if\rreturn \"\"" },
|
|
||||||
"action": "appleScript", "actionAppleScript": {"inline": "if application \"Safari\" is running then\r\ttell application \"Safari\"\r\t\trepeat with w in windows\r\t\t\trepeat with t in tabs of w\r\t\t\t\ttell t\r\t\t\t\t\tif URL starts with \"https:\/\/music.yandex.ru\" and name does not end with \"на Яндекс.Музыке\" then --последнее условие проверяет, запущена ли музыка\r\t\t\t\t\t\tactivate\r\t\t\t\t\t\tset index of w to 1\r\t\t\t\t\t\tdelay 0.1\r\t\t\t\t\t\tset current tab of w to t\r\t\t\t\t\tend if\r\t\t\t\tend tell\r\t\t\tend repeat\r\t\tend repeat\r\tend tell\rend if"},
|
|
||||||
},
|
|
||||||
{ "type": "appleScriptTitledButton", "source": { "inline": "tell application \"Reminders\"\r\tset activeReminders to name of (reminders of list \"Напоминания\" whose completed is false)\r\tif activeReminders is not {} then\r\t\treturn first item of activeReminders\r\telse\r\t\treturn \"\"\r\tend if\rend tell" }, "refreshInterval": 30},
|
|
||||||
{ "type": "flexSpace" },
|
|
||||||
{ "type": "appleScriptTitledButton", "source": { "inline": "if application \"iTunes\" is running then\r\ttell application \"iTunes\"\r\t\tif player state is not stopped then return \"\"\r\tend tell\rend if\rif application \"Safari\" is running then\r\ttell application \"Safari\"\r\t\trepeat with t in tabs of windows\r\t\t\ttell t\r\t\t\t\tif URL starts with \"https:\/\/music.yandex.ru\" and name does not end with \"на Яндекс.Музыке\" then\r\t\t\t\t\treturn \"\"\r\t\t\t\tend if\r\t\t\tend tell\r\t\tend repeat\r\tend tell\rend if\rreturn \"▶\"" }, "refreshInterval": 30, "width": 40},
|
|
||||||
{ "type": "volumeDown", "width": 44 },
|
|
||||||
{ "type": "volumeUp", "width": 44 },
|
|
||||||
{ "type": "displaySleep" },
|
|
||||||
{ "type": "appleScriptTitledButton", "refreshInterval": 1800, "source": { "filePath": "/Users/redetection/Library/Application Support/MTMR/Weather.scpt"} },
|
|
||||||
{ "type": "timeButton" },
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user