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

Refactored parsing

Rewrote presets parsing. Instead of parsing presets in a separate file using bunch of huge enums it is now parsed inside each object.

To implement a new class:
1. Derive your class from CustomTouchBarItem (for static) or CustomButtonTouchBarItem (for buttons)
2. Override class var typeIdentifier with your object identificator
3. Override init(from decoder: Decoder) and read all custom json params you need
4. Don't forget to call super.init(identifier: CustomTouchBarItem.createIdentifier(type)) in the init() function
5. Add your new class to BarItemDefinition.types in ItemParsing.swift

Good example is PomodoroBarItem

If you want to inherid from some other NS class (NSSlider or NSPopoverTouchBarItem or other) then look into GroupBarItem and BrightnessViewController
This commit is contained in:
Fedor Zaitsev 2020-04-29 22:45:36 -07:00
parent c51b6acdf1
commit 101a81bf3b
41 changed files with 1628 additions and 1042 deletions

View File

@ -6,10 +6,42 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
private var forceHideConstraint: NSLayoutConstraint! private var forceHideConstraint: NSLayoutConstraint!
private let alternativeImages: [String: SourceProtocol] private let alternativeImages: [String: SourceProtocol]
private enum CodingKeys: String, CodingKey {
case source
case alternativeImages
case refreshInterval
}
override class var typeIdentifier: String {
return "appleScriptTitledButton"
}
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, alternativeImages: [String: SourceProtocol]) { init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, alternativeImages: [String: SourceProtocol]) {
self.interval = interval self.interval = interval
self.alternativeImages = alternativeImages self.alternativeImages = alternativeImages
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
initScripts(source: source)
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let source = try container.decode(Source.self, forKey: .source)
self.interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
self.alternativeImages = try container.decodeIfPresent([String: Source].self, forKey: .alternativeImages) ?? [:]
print("AppleScriptTouchBarItem.init(from decoder)")
try super.init(from: decoder)
self.title = ""
initScripts(source: source)
}
func initScripts(source: SourceProtocol) {
forceHideConstraint = view.widthAnchor.constraint(equalToConstant: 0) forceHideConstraint = view.widthAnchor.constraint(equalToConstant: 0)
title = "scheduled" title = "scheduled"
DispatchQueue.appleScriptQueue.async { DispatchQueue.appleScriptQueue.async {
@ -38,10 +70,6 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
} }
} }
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func refreshAndSchedule() { func refreshAndSchedule() {
#if DEBUG #if DEBUG
print("refresh happened (interval \(interval)), self \(identifier.rawValue))") print("refresh happened (interval \(interval)), self \(identifier.rawValue))")
@ -62,7 +90,7 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
func updateIcon(iconLabel: String) { func updateIcon(iconLabel: String) {
if alternativeImages[iconLabel] != nil { if alternativeImages[iconLabel] != nil {
DispatchQueue.main.async { DispatchQueue.main.async {
self.image = self.alternativeImages[iconLabel]!.image self.setImage(self.alternativeImages[iconLabel]!.image)
} }
} else { } else {
print("Cannot find icon with label \"\(iconLabel)\"") print("Cannot find icon with label \"\(iconLabel)\"")

View File

@ -15,6 +15,7 @@ class BasicView: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
var threefingers: NSPanGestureRecognizer! var threefingers: NSPanGestureRecognizer!
var fourfingers: NSPanGestureRecognizer! var fourfingers: NSPanGestureRecognizer!
var swipeItems: [SwipeItem] = [] var swipeItems: [SwipeItem] = []
var items: [NSTouchBarItem] = []
var prevPositions: [Int: CGFloat] = [2:0, 3:0, 4:0] var prevPositions: [Int: CGFloat] = [2:0, 3:0, 4:0]
// legacy gesture positions // legacy gesture positions
@ -25,6 +26,7 @@ class BasicView: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem], swipeItems: [SwipeItem]) { init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem], swipeItems: [SwipeItem]) {
super.init(identifier: identifier) super.init(identifier: identifier)
self.swipeItems = swipeItems self.swipeItems = swipeItems
self.items = items
let views = items.compactMap { $0.view } let views = items.compactMap { $0.view }
let stackView = NSStackView(views: views) let stackView = NSStackView(views: views)
stackView.spacing = 1 stackView.spacing = 1

View File

@ -8,24 +8,130 @@
import Cocoa import Cocoa
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate { enum Align: String, Decodable {
var tapClosure: (() -> Void)? case left
var longTapClosure: (() -> Void)? { case center
didSet { case right
longClick.isEnabled = longTapClosure != nil }
// CustomTouchBarItem is a base class for all widgets
// This class provides some basic parameter parsing (width, align)
// To implement a new class:
// 1. Derive your class from CustomTouchBarItem (for static) or CustomButtonTouchBarItem (for buttons)
// 2. Override class var typeIdentifier with your object identificator
// 3. Override init(from decoder: Decoder) and read all custom json params you need
// 4. Don't forget to call super.init(identifier: CustomTouchBarItem.createIdentifier(type)) in the init() function
// 5. Add your new class to BarItemDefinition.types in ItemParsing.swift
//
// Good example is PomodoroBarItem
//
// If you want to inherid from some other NS class (NSSlider or NSPopoverTouchBarItem or other) then
// look into GroupBarItem and BrightnessViewController
class CustomTouchBarItem: NSCustomTouchBarItem, Decodable {
var align: Align
private var width: NSLayoutConstraint?
class var typeIdentifier: String {
return "NOTDEFINED"
}
func setWidth(value: CGFloat) {
guard value > 0 else {
return
}
if let width = self.width {
width.isActive = false
}
self.width = view.widthAnchor.constraint(equalToConstant: value)
self.width!.isActive = true
}
func getWidth() -> CGFloat {
return width?.constant ?? 0.0
}
private enum CodingKeys: String, CodingKey {
case type
case width
case align
// TODO move bordered and background from custom button class
//case bordered
//case background
//case title
}
override init(identifier: NSTouchBarItem.Identifier) {
self.align = .center
// setting width here wouldn't make any affect
super.init(identifier: identifier)
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
self.align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center
super.init(identifier: CustomTouchBarItem.createIdentifier(type))
if let width = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
self.setWidth(value: width)
} }
} }
static func identifierBase(_ type: String) -> String {
return "com.toxblh.mtmr." + type
}
static func createIdentifier(_ type: String) -> NSTouchBarItem.Identifier {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH-mm-ss"
let time = dateFormatter.string(from: Date())
let identifierString = CustomTouchBarItem.identifierBase(type).appending(time + "--" + UUID().uuidString)
return NSTouchBarItem.Identifier(identifierString)
}
}
class CustomButtonTouchBarItem: CustomTouchBarItem, NSGestureRecognizerDelegate {
private var tapAction: EventAction?
private var longTapAction: EventAction?
var finishViewConfiguration: ()->() = {} var finishViewConfiguration: ()->() = {}
override class var typeIdentifier: String {
return "staticButton"
}
private enum CodingKeys: String, CodingKey {
case title
case bordered
case background
case image
case action
case longAction
}
private var button: NSButton! private var button: NSButton!
private var singleClick: HapticClickGestureRecognizer! private var singleClick: HapticClickGestureRecognizer!
private var longClick: LongPressGestureRecognizer! private var longClick: LongPressGestureRecognizer!
private var attributedTitle: NSAttributedString
init(identifier: NSTouchBarItem.Identifier, title: String) { init(identifier: NSTouchBarItem.Identifier, title: String) {
attributedTitle = title.defaultTouchbarAttributedString attributedTitle = title.defaultTouchbarAttributedString
super.init(identifier: identifier) super.init(identifier: identifier)
initButton(title: title, imageSource: nil)
}
func initButton(title: String, imageSource: Source?) {
button = CustomHeightButton(title: title, target: nil, action: nil) button = CustomHeightButton(title: title, target: nil, action: nil)
self.setImage(imageSource?.image)
longClick = LongPressGestureRecognizer(target: self, action: #selector(handleGestureLong)) longClick = LongPressGestureRecognizer(target: self, action: #selector(handleGestureLong))
longClick.isEnabled = false longClick.isEnabled = false
@ -33,6 +139,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
longClick.delegate = self longClick.delegate = self
singleClick = HapticClickGestureRecognizer(target: self, action: #selector(handleGestureSingle)) singleClick = HapticClickGestureRecognizer(target: self, action: #selector(handleGestureSingle))
singleClick.isEnabled = false
singleClick.allowedTouchTypes = .direct singleClick.allowedTouchTypes = .direct
singleClick.delegate = self singleClick.delegate = self
@ -40,6 +147,45 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
button.attributedTitle = attributedTitle button.attributedTitle = attributedTitle
} }
required init(from decoder: Decoder) throws {
attributedTitle = "".defaultTouchbarAttributedString
let container = try decoder.container(keyedBy: CodingKeys.self)
let title = try container.decodeIfPresent(String.self, forKey: .title) ?? ""
try super.init(from: decoder)
if let borderedFlag = try container.decodeIfPresent(Bool.self, forKey: .bordered) {
self.isBordered = borderedFlag
}
if let bgColor = try container.decodeIfPresent(String.self, forKey: .background)?.hexColor {
self.backgroundColor = bgColor
}
let imageSource = try container.decodeIfPresent(Source.self, forKey: .image)
initButton(title: title, imageSource: imageSource)
self.setTapAction(try? SingleTapEventAction(from: decoder))
self.setLongTapAction(try? LongTapEventAction(from: decoder))
}
// From for static buttons
convenience init(title: String) {
self.init(identifier: CustomTouchBarItem.createIdentifier(CustomButtonTouchBarItem.typeIdentifier), title: title)
}
func setTapAction(_ action: EventAction?) {
self.tapAction = action
self.singleClick?.isEnabled = action != nil
}
func setLongTapAction(_ action: EventAction?) {
self.longTapAction = action
self.longClick?.isEnabled = action != nil
}
required init?(coder _: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -58,27 +204,35 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
var title: String { var title: String {
get { get {
return attributedTitle.string return getAttributedTitle().string
} }
set { set {
attributedTitle = newValue.defaultTouchbarAttributedString setAttributedTitle(newValue.defaultTouchbarAttributedString)
} }
} }
var attributedTitle: NSAttributedString { func getAttributedTitle() -> NSAttributedString {
didSet { return attributedTitle
button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
button?.attributedTitle = attributedTitle
}
} }
var image: NSImage? { func setAttributedTitle(_ attributedTitle: NSAttributedString) {
didSet { self.attributedTitle = attributedTitle
button.image = image button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
} button?.attributedTitle = attributedTitle
} }
private func reinstallButton() { private var image: NSImage?
func getImage() -> NSImage? {
return image
}
func setImage(_ image: NSImage?) {
self.image = image
button.image = image
}
func reinstallButton() {
let title = button.attributedTitle let title = button.attributedTitle
let image = button.image let image = button.image
let cell = CustomButtonCell(parentItem: self) let cell = CustomButtonCell(parentItem: self)
@ -116,7 +270,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
@objc func handleGestureSingle(gr: NSClickGestureRecognizer) { @objc func handleGestureSingle(gr: NSClickGestureRecognizer) {
switch gr.state { switch gr.state {
case .ended: case .ended:
tapClosure?() self.tapAction?.closure(self)
break break
default: default:
break break
@ -126,7 +280,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
@objc func handleGestureLong(gr: NSPressGestureRecognizer) { @objc func handleGestureLong(gr: NSPressGestureRecognizer) {
switch gr.state { switch gr.state {
case .possible: // tiny hack because we're calling action manually case .possible: // tiny hack because we're calling action manually
(self.longTapClosure ?? self.tapClosure)?() (self.longTapAction?.closure ?? self.tapAction?.closure)?(self)
break break
default: default:
break break
@ -156,7 +310,7 @@ class CustomButtonCell: NSButtonCell {
if flag { if flag {
setAttributedTitle(attributedTitle, withColor: .lightGray) setAttributedTitle(attributedTitle, withColor: .lightGray)
} else if let parentItem = self.parentItem { } else if let parentItem = self.parentItem {
attributedTitle = parentItem.attributedTitle attributedTitle = parentItem.getAttributedTitle()
} }
} }
} }

184
MTMR/EventActions.swift Normal file
View File

@ -0,0 +1,184 @@
//
// EventActions.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class EventAction {
var closure: ((_ caller: CustomButtonTouchBarItem) -> Void)
func setHidKeyClosure(keycode: Int32) -> EventAction {
closure = { (_ caller: CustomButtonTouchBarItem) in
HIDPostAuxKey(keycode)
}
return self
}
func setKeyPressClosure(keycode: Int) -> EventAction {
closure = { (_ caller: CustomButtonTouchBarItem) in
GenericKeyPress(keyCode: CGKeyCode(keycode)).send()
}
return self
}
func setAppleScriptClosure(appleScript: NSAppleScript) -> EventAction {
closure = { (_ caller: CustomButtonTouchBarItem) in
DispatchQueue.appleScriptQueue.async {
var error: NSDictionary?
appleScript.executeAndReturnError(&error)
if let error = error {
print("error \(error) when handling apple script ")
}
}
}
return self
}
func setShellScriptClosure(executable: String, parameters: [String]) -> EventAction {
closure = { (_ caller: CustomButtonTouchBarItem) in
let task = Process()
task.launchPath = executable
task.arguments = parameters
task.launch()
}
return self
}
func setOpenUrlClosure(url: String) -> EventAction {
closure = { (_ caller: CustomButtonTouchBarItem) in
if let url = URL(string: url), NSWorkspace.shared.open(url) {
#if DEBUG
print("URL was successfully opened")
#endif
} else {
print("error", url)
}
}
return self
}
init() {
self.closure = { (_ caller: CustomButtonTouchBarItem) in }
}
init(_ closure: @escaping (_ caller: CustomButtonTouchBarItem) -> Void) {
self.closure = closure
}
}
class LongTapEventAction: EventAction, Decodable {
private enum CodingKeys: String, CodingKey {
case longAction
case longKeycode
case longActionAppleScript
case longExecutablePath
case longShellArguments
case longUrl
}
private enum LongActionTypeRaw: String, Decodable {
case hidKey
case keyPress
case appleScript
case shellScript
case openUrl
}
required init(from decoder: Decoder) throws {
super.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction)
switch type {
case .some(.hidKey):
let keycode = try container.decode(Int32.self, forKey: .longKeycode)
_ = setHidKeyClosure(keycode: keycode)
case .some(.keyPress):
let keycode = try container.decode(Int.self, forKey: .longKeycode)
_ = setKeyPressClosure(keycode: keycode)
case .some(.appleScript):
let source = try container.decode(Source.self, forKey: .longActionAppleScript)
guard let appleScript = source.appleScript else {
print("cannot create apple script")
return
}
_ = setAppleScriptClosure(appleScript: appleScript)
case .some(.shellScript):
let executable = try container.decode(String.self, forKey: .longExecutablePath)
let parameters = try container.decodeIfPresent([String].self, forKey: .longShellArguments) ?? []
_ = setShellScriptClosure(executable: executable, parameters: parameters)
case .some(.openUrl):
let url = try container.decode(String.self, forKey: .longUrl)
_ = setOpenUrlClosure(url: url)
case .none:
break
}
}
}
class SingleTapEventAction: EventAction, Decodable {
private enum CodingKeys: String, CodingKey {
case action
case keycode
case actionAppleScript
case executablePath
case shellArguments
case url
}
private enum ActionTypeRaw: String, Decodable {
case hidKey
case keyPress
case appleScript
case shellScript
case openUrl
}
required init(from decoder: Decoder) throws {
super.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
switch type {
case .some(.hidKey):
let keycode = try container.decode(Int32.self, forKey: .keycode)
_ = setHidKeyClosure(keycode: keycode)
case .some(.keyPress):
let keycode = try container.decode(Int.self, forKey: .keycode)
_ = setKeyPressClosure(keycode: keycode)
case .some(.appleScript):
let source = try container.decode(Source.self, forKey: .actionAppleScript)
guard let appleScript = source.appleScript else {
print("cannot create apple script")
return
}
_ = setAppleScriptClosure(appleScript: appleScript)
case .some(.shellScript):
let executable = try container.decode(String.self, forKey: .executablePath)
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
_ = setShellScriptClosure(executable: executable, parameters: parameters)
case .some(.openUrl):
let url = try container.decode(String.self, forKey: .url)
_ = setOpenUrlClosure(url: url)
case .none:
break
}
}
}

View File

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.25</string> <string>0.25</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>402</string> <string>622</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string> <string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>

View File

@ -3,550 +3,90 @@ import Foundation
extension Data { extension Data {
func barItemDefinitions() -> [BarItemDefinition]? { func barItemDefinitions() -> [BarItemDefinition]? {
return try? JSONDecoder().decode([BarItemDefinition].self, from: utf8string!.stripComments().data(using: .utf8)!) return try? JSONDecoder().decode([BarItemDefinition].self, from: utf8string!.stripComments().data(using: .utf8)!)
} }
} }
struct BarItemDefinition: Decodable { struct BarItemDefinition: Decodable {
let type: ItemType let obj: CustomTouchBarItem
let action: ActionType
let longAction: LongActionType enum ParsingErrors: Error {
let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter] case noMatchingType(description: String)
}
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case type case objtype = "type"
} }
init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]) { static let types: [CustomTouchBarItem.Type] = [
self.type = type
self.action = action
self.longAction = longAction
self.additionalParameters = additionalParameters
}
init(from decoder: Decoder) throws { // custom buttons
let container = try decoder.container(keyedBy: CodingKeys.self) CustomButtonTouchBarItem.self,
let type = try container.decode(String.self, forKey: .type) AppleScriptTouchBarItem.self,
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type) ShellScriptTouchBarItem.self,
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)
}
}
}
typealias ParametersDecoder = (Decoder) throws -> ( // basic widget buttons
item: ItemType, EscapeBarItem.self,
action: ActionType, DeleteBarItem.self,
longAction: LongActionType, BrightnessUpBarItem.self,
parameters: [GeneralParameters.CodingKeys: GeneralParameter] BrightnessDownBarItem.self,
) IlluminationUpBarItem.self,
IlluminationDownBarItem.self,
VolumeUpBarItem.self,
VolumeDownBarItem.self,
MuteBarItem.self,
PreviousBarItem.self,
PlayBarItem.self,
NextBarItem.self,
SleepBarItem.self,
DisplaySleepBarItem.self,
class SupportedTypesHolder {
private var supportedTypes: [String: ParametersDecoder] = [
"escape": { _ in (
item: .staticButton(title: "esc"),
action: .keyPress(keycode: 53),
longAction: .none,
parameters: [.align: .align(.left)]
) },
"delete": { _ in ( // custom widgets
item: .staticButton(title: "del"), TimeTouchBarItem.self,
action: .keyPress(keycode: 117), BatteryBarItem.self,
longAction: .none, AppScrubberTouchBarItem.self,
parameters: [:] VolumeViewController.self,
) }, BrightnessViewController.self,
WeatherBarItem.self,
YandexWeatherBarItem.self,
CurrencyBarItem.self,
InputSourceBarItem.self,
MusicBarItem.self,
NightShiftBarItem.self,
DnDBarItem.self,
PomodoroBarItem.self,
NetworkBarItem.self,
DarkModeBarItem.self,
"brightnessUp": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
return (
item: .staticButton(title: ""),
action: .keyPress(keycode: 144),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"brightnessDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
return (
item: .staticButton(title: ""),
action: .keyPress(keycode: 145),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"illuminationUp": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_up"))
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_UP),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"illuminationDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_down"))
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_DOWN),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"volumeDown": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeDownTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"volumeUp": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeUpTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"mute": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarAudioOutputMuteTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_MUTE),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"previous": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarRewindTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"play": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarPlayPauseTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_PLAY),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"next": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarFastForwardTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_NEXT),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"sleep": { _ in (
item: .staticButton(title: "☕️"),
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]),
longAction: .none,
parameters: [:]
) },
"displaySleep": { _ in (
item: .staticButton(title: "☕️"),
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]),
longAction: .none,
parameters: [:]
) },
// custom-custom objects!
SwipeItem.self,
GroupBarItem.self,
ExitTouchbarBarItem.self,
CloseBarItem.self,
] ]
static let sharedInstance = SupportedTypesHolder() init(obj: CustomTouchBarItem) {
self.obj = obj
func lookup(by type: String) -> ParametersDecoder {
return supportedTypes[type] ?? { decoder in (
item: try ItemType(from: decoder),
action: try ActionType(from: decoder),
longAction: try LongActionType(from: decoder),
parameters: [:]
) }
}
func register(typename: String, decoder: @escaping ParametersDecoder) {
supportedTypes[typename] = decoder
}
func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) {
register(typename: typename) { _ in
(
item: item,
action,
longAction,
parameters: [:]
)
}
}
}
enum ItemType: Decodable {
case staticButton(title: String)
case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double, alternativeImages: [String: SourceProtocol])
case shellScriptTitledButton(source: SourceProtocol, refreshInterval: Double)
case timeButton(formatTemplate: String, timeZone: String?, locale: String?)
case battery
case dock(autoResize: Bool, filter: String?)
case volume
case brightness(refreshInterval: Double)
case weather(interval: Double, units: String, api_key: String, icon_type: String)
case yandexWeather(interval: Double)
case currency(interval: Double, from: String, to: String, full: Bool)
case inputsource
case music(interval: Double, disableMarquee: Bool)
case group(items: [BarItemDefinition])
case nightShift
case dnd
case pomodoro(workTime: Double, restTime: Double)
case network(flip: Bool)
case darkMode
case swipe(direction: String, fingers: Int, minOffset: Float, sourceApple: SourceProtocol?, sourceBash: SourceProtocol?)
private enum CodingKeys: String, CodingKey {
case type
case title
case source
case refreshInterval
case from
case to
case full
case timeZone
case units
case api_key
case icon_type
case formatTemplate
case locale
case image
case url
case longUrl
case items
case workTime
case restTime
case flip
case autoResize
case filter
case disableMarquee
case alternativeImages
case sourceApple
case sourceBash
case direction
case fingers
case minOffset
}
enum ItemTypeRaw: String, Decodable {
case staticButton
case appleScriptTitledButton
case shellScriptTitledButton
case timeButton
case battery
case dock
case volume
case brightness
case weather
case yandexWeather
case currency
case inputsource
case music
case group
case nightShift
case dnd
case pomodoro
case network
case darkMode
case swipe
} }
init(from decoder: Decoder) throws { init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(ItemTypeRaw.self, forKey: .type) let objType = try container.decode(String.self, forKey: .objtype)
switch type {
case .appleScriptTitledButton:
let source = try container.decode(Source.self, forKey: .source)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
let alternativeImages = try container.decodeIfPresent([String: Source].self, forKey: .alternativeImages) ?? [:]
self = .appleScriptTitledButton(source: source, refreshInterval: interval, alternativeImages: alternativeImages)
case .shellScriptTitledButton:
let source = try container.decode(Source.self, forKey: .source)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
self = .shellScriptTitledButton(source: source, refreshInterval: interval)
case .staticButton: for obj in BarItemDefinition.types {
let title = try container.decode(String.self, forKey: .title) if obj.typeIdentifier == objType {
self = .staticButton(title: title) self.obj = try obj.init(from: decoder)
return
case .timeButton: }
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
let timeZone = try container.decodeIfPresent(String.self, forKey: .timeZone) ?? nil
let locale = try container.decodeIfPresent(String.self, forKey: .locale) ?? nil
self = .timeButton(formatTemplate: template, timeZone: timeZone, locale: locale)
case .battery:
self = .battery
case .dock:
let autoResize = try container.decodeIfPresent(Bool.self, forKey: .autoResize) ?? false
let filterRegexString = try container.decodeIfPresent(String.self, forKey: .filter)
self = .dock(autoResize: autoResize, filter: filterRegexString)
case .volume:
self = .volume
case .brightness:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
self = .brightness(refreshInterval: interval)
case .weather:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
let units = try container.decodeIfPresent(String.self, forKey: .units) ?? "metric"
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
self = .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type)
case .yandexWeather:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
self = .yandexWeather(interval: interval)
case .currency:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
let full = try container.decodeIfPresent(Bool.self, forKey: .full) ?? false
self = .currency(interval: interval, from: from, to: to, full: full)
case .inputsource:
self = .inputsource
case .music:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 5.0
let disableMarquee = try container.decodeIfPresent(Bool.self, forKey: .disableMarquee) ?? false
self = .music(interval: interval, disableMarquee: disableMarquee)
case .group:
let items = try container.decode([BarItemDefinition].self, forKey: .items)
self = .group(items: items)
case .nightShift:
self = .nightShift
case .dnd:
self = .dnd
case .pomodoro:
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime) ?? 1500.0
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime) ?? 600.0
self = .pomodoro(workTime: workTime, restTime: restTime)
case .network:
let flip = try container.decodeIfPresent(Bool.self, forKey: .flip) ?? false
self = .network(flip: flip)
case .darkMode:
self = .darkMode
case .swipe:
let sourceApple = try container.decodeIfPresent(Source.self, forKey: .sourceApple)
let sourceBash = try container.decodeIfPresent(Source.self, forKey: .sourceBash)
let direction = try container.decode(String.self, forKey: .direction)
let fingers = try container.decode(Int.self, forKey: .fingers)
let minOffset = try container.decodeIfPresent(Float.self, forKey: .minOffset) ?? 0.0
self = .swipe(direction: direction, fingers: fingers, minOffset: minOffset, sourceApple: sourceApple, sourceBash: sourceBash)
}
}
}
enum ActionType: Decodable {
case none
case hidKey(keycode: Int32)
case keyPress(keycode: Int)
case appleScript(source: SourceProtocol)
case shellScript(executable: String, parameters: [String])
case custom(closure: () -> Void)
case openUrl(url: String)
private enum CodingKeys: String, CodingKey {
case action
case keycode
case actionAppleScript
case executablePath
case shellArguments
case url
}
private enum ActionTypeRaw: 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 type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
switch type {
case .some(.hidKey):
let keycode = try container.decode(Int32.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 = .appleScript(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 url = try container.decode(String.self, forKey: .url)
self = .openUrl(url: url)
case .none:
self = .none
}
}
}
enum LongActionType: Decodable {
case none
case hidKey(keycode: Int32)
case keyPress(keycode: Int)
case appleScript(source: SourceProtocol)
case shellScript(executable: String, parameters: [String])
case custom(closure: () -> Void)
case openUrl(url: String)
private enum CodingKeys: String, CodingKey {
case longAction
case longKeycode
case longActionAppleScript
case longExecutablePath
case longShellArguments
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(Int32.self, forKey: .longKeycode)
self = .hidKey(keycode: keycode)
case .some(.keyPress):
let keycode = try container.decode(Int.self, forKey: .longKeycode)
self = .keyPress(keycode: keycode)
case .some(.appleScript):
let source = try container.decode(Source.self, forKey: .longActionAppleScript)
self = .appleScript(source: source)
case .some(.shellScript):
let executable = try container.decode(String.self, forKey: .longExecutablePath)
let parameters = try container.decodeIfPresent([String].self, forKey: .longShellArguments) ?? []
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
}
}
}
enum GeneralParameter {
case width(_: CGFloat)
case image(source: SourceProtocol)
case align(_: Align)
case bordered(_: Bool)
case background(_: NSColor)
case title(_: String)
}
struct GeneralParameters: Decodable {
let parameters: [GeneralParameters.CodingKeys: GeneralParameter]
enum CodingKeys: String, CodingKey {
case width
case image
case align
case bordered
case background
case title
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var result: [GeneralParameters.CodingKeys: GeneralParameter] = [:]
if let value = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
result[.width] = .width(value)
} }
if let imageSource = try container.decodeIfPresent(Source.self, forKey: .image) {
result[.image] = .image(source: imageSource)
}
let align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center print("Cannot find preset mapping for \(objType)")
result[.align] = .align(align) throw ParsingErrors.noMatchingType(description: "Cannot find preset mapping for \(objType)")
if let borderedFlag = try container.decodeIfPresent(Bool.self, forKey: .bordered) {
result[.bordered] = .bordered(borderedFlag)
}
if let backgroundColor = try container.decodeIfPresent(String.self, forKey: .background)?.hexColor {
result[.background] = .background(backgroundColor)
}
if let title = try container.decodeIfPresent(String.self, forKey: .title) {
result[.title] = .title(title)
}
parameters = result
} }
} }
@ -646,12 +186,6 @@ extension Data {
} }
} }
enum Align: String, Decodable {
case left
case center
case right
}
extension URL { extension URL {
var appleScript: NSAppleScript? { var appleScript: NSAppleScript? {
guard FileManager.default.fileExists(atPath: path) else { return nil } guard FileManager.default.fileExists(atPath: path) else { return nil }

View File

@ -12,11 +12,40 @@ class ShellScriptTouchBarItem: CustomButtonTouchBarItem {
private let source: String private let source: String
private var forceHideConstraint: NSLayoutConstraint! private var forceHideConstraint: NSLayoutConstraint!
private enum CodingKeys: String, CodingKey {
case source
case refreshInterval
}
override class var typeIdentifier: String {
return "shellScriptTitledButton"
}
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval) { init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval) {
self.interval = interval self.interval = interval
self.source = source.string ?? "echo No \"source\"" self.source = source.string ?? "echo No \"source\""
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
initScripts()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let source = try container.decode(Source.self, forKey: .source)
self.source = source.string ?? "echo No \"source\""
self.interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
try super.init(from: decoder)
self.title = ""
initScripts()
}
func initScripts() {
forceHideConstraint = view.widthAnchor.constraint(equalToConstant: 0) forceHideConstraint = view.widthAnchor.constraint(equalToConstant: 0)
DispatchQueue.shellScriptQueue.async { DispatchQueue.shellScriptQueue.async {
@ -24,10 +53,6 @@ class ShellScriptTouchBarItem: CustomButtonTouchBarItem {
} }
} }
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func refreshAndSchedule() { func refreshAndSchedule() {
// Execute script and get result // Execute script and get result
let scriptResult = execute(source) let scriptResult = execute(source)
@ -45,7 +70,7 @@ class ShellScriptTouchBarItem: CustomButtonTouchBarItem {
if (newBackgoundColor != self?.backgroundColor) { // performance optimization because of reinstallButton if (newBackgoundColor != self?.backgroundColor) { // performance optimization because of reinstallButton
self?.backgroundColor = newBackgoundColor self?.backgroundColor = newBackgoundColor
} }
self?.attributedTitle = title self?.setAttributedTitle(title)
self?.forceHideConstraint.isActive = scriptResult == "" self?.forceHideConstraint.isActive = scriptResult == ""
} }

View File

@ -9,12 +9,25 @@
import Foundation import Foundation
import Foundation import Foundation
class SwipeItem: NSCustomTouchBarItem { class SwipeItem: CustomTouchBarItem {
private var scriptApple: NSAppleScript? private var scriptApple: NSAppleScript?
private var scriptBash: String? private var scriptBash: String?
private var direction: String private var direction: String
private var fingers: Int private var fingers: Int
private var minOffset: Float private var minOffset: Float
private enum CodingKeys: String, CodingKey {
case sourceApple
case sourceBash
case direction
case fingers
case minOffset
}
override class var typeIdentifier: String {
return "swipe"
}
init?(identifier: NSTouchBarItem.Identifier, direction: String, fingers: Int, minOffset: Float, sourceApple: SourceProtocol?, sourceBash: SourceProtocol?) { init?(identifier: NSTouchBarItem.Identifier, direction: String, fingers: Int, minOffset: Float, sourceApple: SourceProtocol?, sourceBash: SourceProtocol?) {
self.direction = direction self.direction = direction
self.fingers = fingers self.fingers = fingers
@ -28,6 +41,19 @@ class SwipeItem: NSCustomTouchBarItem {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.scriptApple = try container.decodeIfPresent(Source.self, forKey: .sourceApple)?.appleScript
self.scriptBash = try container.decodeIfPresent(Source.self, forKey: .sourceBash)?.string
self.direction = try container.decode(String.self, forKey: .direction)
self.fingers = try container.decode(Int.self, forKey: .fingers)
self.minOffset = try container.decodeIfPresent(Float.self, forKey: .minOffset) ?? 0.0
try super.init(from: decoder)
}
func processEvent(offset: CGFloat, fingers: Int) { func processEvent(offset: CGFloat, fingers: Int) {
if direction == "right" && Float(offset) > self.minOffset && self.fingers == fingers { if direction == "right" && Float(offset) > self.minOffset && self.fingers == fingers {
self.execute() self.execute()

View File

@ -16,52 +16,6 @@ struct ExactItem {
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR") let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
let standardConfigPath = appSupportDirectory.appending("/items.json") let standardConfigPath = appSupportDirectory.appending("/items.json")
extension ItemType {
var identifierBase: String {
switch self {
case .staticButton(title: _):
return "com.toxblh.mtmr.staticButton."
case .appleScriptTitledButton(source: _):
return "com.toxblh.mtmr.appleScriptButton."
case .shellScriptTitledButton(source: _):
return "com.toxblh.mtmr.shellScriptButton."
case .timeButton(formatTemplate: _, timeZone: _, locale: _):
return "com.toxblh.mtmr.timeButton."
case .battery:
return "com.toxblh.mtmr.battery."
case .dock(autoResize: _, filter: _):
return "com.toxblh.mtmr.dock"
case .volume:
return "com.toxblh.mtmr.volume"
case .brightness(refreshInterval: _):
return "com.toxblh.mtmr.brightness"
case .weather(interval: _, units: _, api_key: _, icon_type: _):
return "com.toxblh.mtmr.weather"
case .yandexWeather(interval: _):
return "com.toxblh.mtmr.yandexWeather"
case .currency(interval: _, from: _, to: _, full: _):
return "com.toxblh.mtmr.currency"
case .inputsource:
return "com.toxblh.mtmr.inputsource."
case .music(interval: _):
return "com.toxblh.mtmr.music."
case .group(items: _):
return "com.toxblh.mtmr.groupBar."
case .nightShift:
return "com.toxblh.mtmr.nightShift."
case .dnd:
return "com.toxblh.mtmr.dnd."
case .pomodoro(interval: _):
return PomodoroBarItem.identifier
case .network(flip: _):
return NetworkBarItem.identifier
case .darkMode:
return DarkModeBarItem.identifier
case .swipe(direction: _, fingers: _, minOffset: _, sourceApple: _, sourceBash: _):
return "com.toxblh.mtmr.swipe."
}
}
}
extension NSTouchBarItem.Identifier { extension NSTouchBarItem.Identifier {
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip") static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
@ -73,12 +27,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
var touchBar: NSTouchBar! var touchBar: NSTouchBar!
fileprivate var lastPresetPath = "" fileprivate var lastPresetPath = ""
var jsonItems: [BarItemDefinition] = [] var items: [CustomTouchBarItem] = []
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
var centerIdentifiers: [NSTouchBarItem.Identifier] = []
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
var basicViewIdentifier = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollView.".appending(UUID().uuidString)) var basicViewIdentifier = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollView.".appending(UUID().uuidString))
var basicView: BasicView? var basicView: BasicView?
var swipeItems: [SwipeItem] = [] var swipeItems: [SwipeItem] = []
@ -90,14 +39,6 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
private override init() { private override init() {
super.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: "close") { _ in
(item: .staticButton(title: ""), action: .custom(closure: { [weak self] in
guard let `self` = self else { return }
self.reloadPreset(path: self.lastPresetPath)
}), longAction: .none, parameters: [.width: .width(30), .image: .image(source: (NSImage(named: NSImage.stopProgressFreestandingTemplateName))!)])
}
blacklistAppIdentifiers = AppSettings.blacklistedAppIds blacklistAppIdentifiers = AppSettings.blacklistedAppIds
@ -108,21 +49,23 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
reloadStandardConfig() reloadStandardConfig()
} }
func createAndUpdatePreset(newJsonItems: [BarItemDefinition]) { func createAndUpdatePreset(newItems: [BarItemDefinition]) {
if let oldBar = self.touchBar { if let oldBar = self.touchBar {
minimizeSystemModal(oldBar) minimizeSystemModal(oldBar)
} }
touchBar = NSTouchBar() touchBar = NSTouchBar()
jsonItems = newJsonItems (items, swipeItems) = getItems(newItems: newItems)
itemDefinitions = [:]
items = [:]
loadItemDefinitions(jsonItems: jsonItems) let leftItems = items.compactMap({ (item) -> CustomTouchBarItem? in
createItems() item.align == .left ? item : nil
let centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
items[identifier]
}) })
let centerItems = items.compactMap({ (item) -> CustomTouchBarItem? in
item.align == .center ? item : nil
})
let rightItems = items.compactMap({ (item) -> CustomTouchBarItem? in
item.align == .right ? item : nil
})
let centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString)) let centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
let scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems) let scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
@ -130,16 +73,18 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
touchBar.delegate = self touchBar.delegate = self
touchBar.defaultItemIdentifiers = [basicViewIdentifier] touchBar.defaultItemIdentifiers = [basicViewIdentifier]
let leftItems = leftIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in basicView = BasicView(identifier: basicViewIdentifier, items: leftItems + [scrollArea] + rightItems, swipeItems: swipeItems)
items[identifier]
})
let rightItems = rightIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
items[identifier]
})
basicView = BasicView(identifier: basicViewIdentifier, items:leftItems + [scrollArea] + rightItems, swipeItems: swipeItems)
basicView?.legacyGesturesEnabled = AppSettings.multitouchGestures basicView?.legacyGesturesEnabled = AppSettings.multitouchGestures
// it seems that we need to set width only after we added them to the view
// so lets reset width here
for item in items {
item.setWidth(value: item.getWidth())
if item is CustomButtonTouchBarItem {
(item as! CustomButtonTouchBarItem).reinstallButton()
}
}
updateActiveApp() updateActiveApp()
} }
@ -166,41 +111,26 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
reloadPreset(path: presetPath) reloadPreset(path: presetPath)
} }
func reloadPreset(path: String) { func reloadPreset(path: String?) {
lastPresetPath = path if path != nil {
let items = path.fileData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])] lastPresetPath = path!
createAndUpdatePreset(newJsonItems: items)
}
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH-mm-ss"
let time = dateFormatter.string(from: Date())
for item in jsonItems {
let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString)
let identifier = NSTouchBarItem.Identifier(identifierString)
itemDefinitions[identifier] = item
if item.align == .left {
leftIdentifiers.append(identifier)
}
if item.align == .right {
rightIdentifiers.append(identifier)
}
if item.align == .center {
centerIdentifiers.append(identifier)
}
} }
let items = lastPresetPath.fileData?.barItemDefinitions() ?? [BarItemDefinition(obj: CustomButtonTouchBarItem(title: "bad preset"))]
createAndUpdatePreset(newItems: items)
} }
func createItems() { func getItems(newItems: [BarItemDefinition]) -> ([CustomTouchBarItem], [SwipeItem]) {
for (identifier, definition) in itemDefinitions { var items: [CustomTouchBarItem] = []
let item = createItem(forIdentifier: identifier, definition: definition) var swipeItems: [SwipeItem] = []
if item is SwipeItem { for item in newItems {
swipeItems.append(item as! SwipeItem) if item.obj is SwipeItem {
swipeItems.append(item.obj as! SwipeItem)
} else { } else {
items[identifier] = item items.append(item.obj)
} }
} }
return (items, swipeItems)
} }
@objc func setupControlStripPresence() { @objc func setupControlStripPresence() {
@ -224,7 +154,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
} }
} }
@objc private func dismissTouchBar() { @objc func dismissTouchBar() {
minimizeSystemModal(touchBar) minimizeSystemModal(touchBar)
updateControlStripPresence() updateControlStripPresence()
} }
@ -241,198 +171,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
return nil return nil
} }
func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? {
var barItem: NSTouchBarItem!
switch item.type {
case let .staticButton(title: title):
barItem = CustomButtonTouchBarItem(identifier: identifier, title: title)
case let .appleScriptTitledButton(source: source, refreshInterval: interval, alternativeImages: alternativeImages):
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, alternativeImages: alternativeImages)
case let .shellScriptTitledButton(source: source, refreshInterval: interval):
barItem = ShellScriptTouchBarItem(identifier: identifier, source: source, interval: interval)
case let .timeButton(formatTemplate: template, timeZone: timeZone, locale: locale):
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, timeZone: timeZone, locale: locale)
case .battery:
barItem = BatteryBarItem(identifier: identifier)
case let .dock(autoResize: autoResize, filter: regexString):
if let regexString = regexString {
guard let regex = try? NSRegularExpression(pattern: regexString, options: []) else {
barItem = CustomButtonTouchBarItem(identifier: identifier, title: "Bad regex")
break
}
barItem = AppScrubberTouchBarItem(identifier: identifier, autoResize: autoResize, filter: regex)
} else {
barItem = AppScrubberTouchBarItem(identifier: identifier, autoResize: autoResize)
}
case .volume:
if case let .image(source)? = item.additionalParameters[.image] {
barItem = VolumeViewController(identifier: identifier, image: source.image)
} else {
barItem = VolumeViewController(identifier: identifier)
}
case let .brightness(refreshInterval: interval):
if case let .image(source)? = item.additionalParameters[.image] {
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval, image: source.image)
} else {
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
}
case let .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type):
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type)
case let .yandexWeather(interval: interval):
barItem = YandexWeatherBarItem(identifier: identifier, interval: interval)
case let .currency(interval: interval, from: from, to: to, full: full):
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, full: full)
case .inputsource:
barItem = InputSourceBarItem(identifier: identifier)
case let .music(interval: interval, disableMarquee: disableMarquee):
barItem = MusicBarItem(identifier: identifier, interval: interval, disableMarquee: disableMarquee)
case let .group(items: items):
barItem = GroupBarItem(identifier: identifier, items: items)
case .nightShift:
barItem = NightShiftBarItem(identifier: identifier)
case .dnd:
barItem = DnDBarItem(identifier: identifier)
case let .pomodoro(workTime: workTime, restTime: restTime):
barItem = PomodoroBarItem(identifier: identifier, workTime: workTime, restTime: restTime)
case let .network(flip: flip):
barItem = NetworkBarItem(identifier: identifier, flip: flip)
case .darkMode:
barItem = DarkModeBarItem(identifier: identifier)
case let .swipe(direction: direction, fingers: fingers, minOffset: minOffset, sourceApple: sourceApple, sourceBash: sourceBash):
barItem = SwipeItem(identifier: identifier, direction: direction, fingers: fingers, minOffset: minOffset, sourceApple: sourceApple, sourceBash: sourceBash)
}
if let action = self.action(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
item.tapClosure = action
}
if let longAction = self.longAction(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
item.longTapClosure = longAction
}
if case let .bordered(bordered)? = item.additionalParameters[.bordered], let item = barItem as? CustomButtonTouchBarItem {
item.isBordered = bordered
}
if case let .background(color)? = item.additionalParameters[.background], let item = barItem as? CustomButtonTouchBarItem {
item.backgroundColor = color
}
if case let .width(value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
widthBarItem.setWidth(value: value)
}
if case let .image(source)? = item.additionalParameters[.image], let item = barItem as? CustomButtonTouchBarItem {
item.image = source.image
}
if case let .title(value)? = item.additionalParameters[.title] {
if let item = barItem as? GroupBarItem {
item.collapsedRepresentationLabel = value
} else if let item = barItem as? CustomButtonTouchBarItem {
item.title = value
}
}
return barItem
}
func action(forItem item: BarItemDefinition) -> (() -> Void)? {
switch item.action {
case let .hidKey(keycode: keycode):
return { HIDPostAuxKey(keycode) }
case let .keyPress(keycode: keycode):
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
case let .appleScript(source: source):
guard let appleScript = source.appleScript else {
print("cannot create apple script for item \(item)")
return {}
}
return {
DispatchQueue.appleScriptQueue.async {
var error: NSDictionary?
appleScript.executeAndReturnError(&error)
if let error = error {
print("error \(error) when handling \(item) ")
}
}
}
case let .shellScript(executable: executable, parameters: parameters):
return {
let task = Process()
task.launchPath = executable
task.arguments = parameters
task.launch()
}
case let .openUrl(url: 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 let .custom(closure: closure):
return closure
case .none:
return nil
}
}
func longAction(forItem item: BarItemDefinition) -> (() -> Void)? {
switch item.longAction {
case let .hidKey(keycode: keycode):
return { HIDPostAuxKey(keycode) }
case let .keyPress(keycode: keycode):
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
case let .appleScript(source: 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 let .shellScript(executable: executable, parameters: parameters):
return {
let task = Process()
task.launchPath = executable
task.arguments = parameters
task.launch()
}
case let .openUrl(url: 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 let .custom(closure: closure):
return closure
case .none:
return nil
}
}
} }
protocol CanSetWidth { protocol CanSetWidth {
func setWidth(value: CGFloat) func setWidth(value: CGFloat)
} }
extension NSCustomTouchBarItem: CanSetWidth {
func setWidth(value: CGFloat) {
view.widthAnchor.constraint(equalToConstant: value).isActive = true
}
}
extension BarItemDefinition {
var align: Align {
if case let .align(result)? = additionalParameters[.align] {
return result
}
return .center
}
}

View File

@ -7,7 +7,7 @@
import Cocoa import Cocoa
class AppScrubberTouchBarItem: NSCustomTouchBarItem { class AppScrubberTouchBarItem: CustomTouchBarItem {
private var scrollView = NSScrollView() private var scrollView = NSScrollView()
private var autoResize: Bool = false private var autoResize: Bool = false
private var widthConstraint: NSLayoutConstraint? private var widthConstraint: NSLayoutConstraint?
@ -20,6 +20,20 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
return NSWorkspace.shared.frontmostApplication?.bundleIdentifier return NSWorkspace.shared.frontmostApplication?.bundleIdentifier
} }
enum ParsingErrors: Error {
case IncorrectRegex(description: String)
}
private enum CodingKeys: String, CodingKey {
case autoResize
case filter
}
override class var typeIdentifier: String {
return "dock"
}
private var applications: [DockItem] = [] private var applications: [DockItem] = []
private var items: [DockBarItem] = [] private var items: [DockBarItem] = []
@ -29,6 +43,35 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
self.autoResize = autoResize self.autoResize = autoResize
view = scrollView view = scrollView
self.setup()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.autoResize = try container.decodeIfPresent(Bool.self, forKey: .autoResize) ?? false
let filterRegexString = try container.decodeIfPresent(String.self, forKey: .filter)
if let filterRegexString = filterRegexString {
let regex = try? NSRegularExpression(pattern: filterRegexString, options: [])
if regex == nil {
throw ParsingErrors.IncorrectRegex(description: "incorrect regex")
}
self.filter = regex
} else {
self.filter = nil
}
try super.init(from: decoder)
view = scrollView
self.setup()
}
func setup() {
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didLaunchApplicationNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didLaunchApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didTerminateApplicationNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didTerminateApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(softReloadItems), name: NSWorkspace.didActivateApplicationNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(softReloadItems), name: NSWorkspace.didActivateApplicationNotification, object: nil)
@ -37,9 +80,6 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
hardReloadItems() hardReloadItems()
} }
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func hardReloadItems() { @objc func hardReloadItems() {
applications = launchedApplications() applications = launchedApplications()
@ -82,12 +122,17 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
public func createAppButton(for app: DockItem) -> DockBarItem { public func createAppButton(for app: DockItem) -> DockBarItem {
let item = DockBarItem(app) let item = DockBarItem(app)
item.isBordered = false item.isBordered = false
item.tapClosure = { [weak self] in
self?.switchToApp(app: app) item.setTapAction(
} EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
item.longTapClosure = { [weak self] in self?.switchToApp(app: app)
self?.handleHalfLongPress(item: app) } )
} )
item.setLongTapAction(
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.handleHalfLongPress(item: app)
} )
)
item.killAppClosure = {[weak self] in item.killAppClosure = {[weak self] in
self?.handleLongPress(item: app) self?.handleLongPress(item: app)
} }
@ -212,8 +257,8 @@ class DockBarItem: CustomButtonTouchBarItem {
super.init(identifier: .init(app.bundleIdentifier), title: "") super.init(identifier: .init(app.bundleIdentifier), title: "")
dotView.wantsLayer = true dotView.wantsLayer = true
image = app.icon self.setImage(app.icon)
image?.size = NSSize(width: iconWidth, height: iconWidth) self.getImage()?.size = NSSize(width: iconWidth, height: iconWidth)
killGestureRecognizer = LongPressGestureRecognizer(target: self, action: #selector(firePanGestureRecognizer)) killGestureRecognizer = LongPressGestureRecognizer(target: self, action: #selector(firePanGestureRecognizer))
killGestureRecognizer.allowedTouchTypes = .direct killGestureRecognizer.allowedTouchTypes = .direct
@ -243,4 +288,8 @@ class DockBarItem: CustomButtonTouchBarItem {
required init?(coder _: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
required init(from decoder: Decoder) {
fatalError("init(from decoder:) has not been implemented")
}
} }

View File

@ -12,21 +12,35 @@ import IOKit.ps
class BatteryBarItem: CustomButtonTouchBarItem { class BatteryBarItem: CustomButtonTouchBarItem {
private let batteryInfo = BatteryInfo() private let batteryInfo = BatteryInfo()
override class var typeIdentifier: String {
return "battery"
}
init(identifier: NSTouchBarItem.Identifier) { init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: " ") super.init(identifier: identifier, title: " ")
batteryInfo.start { [weak self] in self.setup()
self?.refresh()
}
refresh()
} }
required init?(coder _: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setup()
}
func setup() {
batteryInfo.start { [weak self] in
self?.refresh()
}
refresh()
}
func refresh() { func refresh() {
attributedTitle = batteryInfo.formattedInfo() setAttributedTitle(batteryInfo.formattedInfo())
} }
deinit { deinit {

View File

@ -0,0 +1,27 @@
//
// BrightnessDownBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class BrightnessDownBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "brightnessDown"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setImage(#imageLiteral(resourceName: "brightnessDown"))
self.setTapAction(EventAction().setKeyPressClosure(keycode: 145))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,27 @@
//
// BrightnessUpBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class BrightnessUpBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "brightnessUp"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setImage(#imageLiteral(resourceName: "brightnessUp"))
self.setTapAction(EventAction().setKeyPressClosure(keycode: 144))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -3,12 +3,39 @@ import AVFoundation
import Cocoa import Cocoa
import CoreAudio import CoreAudio
class BrightnessViewController: NSCustomTouchBarItem { class BrightnessViewController: CustomTouchBarItem {
private(set) var sliderItem: CustomSlider! private(set) var sliderItem: CustomSlider!
override class var typeIdentifier: String {
return "brightness"
}
private enum CodingKeys: String, CodingKey {
case image
case refreshInterval
}
init(identifier: NSTouchBarItem.Identifier, refreshInterval: Double, image: NSImage? = nil) { init(identifier: NSTouchBarItem.Identifier, refreshInterval: Double, image: NSImage? = nil) {
super.init(identifier: identifier) super.init(identifier: identifier)
self.setup(image: nil, interval: refreshInterval)
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let image = try container.decodeIfPresent(Source.self, forKey: .image)?.image
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
try super.init(from: decoder)
self.setup(image: image, interval: interval)
}
func setup(image: NSImage?, interval: Double) {
if image == nil { if image == nil {
sliderItem = CustomSlider() sliderItem = CustomSlider()
} else { } else {
@ -22,14 +49,10 @@ class BrightnessViewController: NSCustomTouchBarItem {
view = sliderItem view = sliderItem
let timer = Timer.scheduledTimer(timeInterval: refreshInterval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true) let timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true)
RunLoop.current.add(timer, forMode: RunLoop.Mode.common) RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
} }
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit { deinit {
sliderItem.unbind(NSBindingName.value) sliderItem.unbind(NSBindingName.value)
} }

View File

@ -0,0 +1,48 @@
//
// CloseBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/30/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class CloseBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "close"
}
init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "")
if self.title == "" {
self.title = "close"
}
self.setTapAction(EventAction.init({_ in
TouchBarController.shared.reloadPreset(path: nil)
} ))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
if self.title == "" {
self.title = "close"
}
self.setTapAction(EventAction.init({_ in
TouchBarController.shared.reloadPreset(path: nil)
} ))
}
}

View File

@ -59,6 +59,18 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
"ETH": 2, "ETH": 2,
] ]
override class var typeIdentifier: String {
return "currency"
}
private enum CodingKeys: String, CodingKey {
case refreshInterval
case from
case to
case full
}
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, full: Bool) { init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, full: Bool) {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck") activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval activity.interval = interval
@ -87,7 +99,51 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
self.setup()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
let full = try container.decodeIfPresent(Bool.self, forKey: .full) ?? false
activity = NSBackgroundActivityScheduler(identifier: CustomTouchBarItem.createIdentifier("Currency.updatecheck").rawValue)
activity.interval = interval
self.from = from
self.to = to
self.full = full
if let prefix = currencies[from] {
self.prefix = prefix
} else {
prefix = from
}
if let postfix = currencies[to] {
self.postfix = postfix
} else {
postfix = to
}
if let decimal = decimals[to] {
self.decimal = decimal
} else {
decimal = 2
}
try super.init(from: decoder)
self.setup()
}
func setup() {
activity.repeats = true activity.repeats = true
activity.qualityOfService = .utility activity.qualityOfService = .utility
activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in
@ -97,10 +153,6 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
updateCurrency() updateCurrency()
} }
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func updateCurrency() { @objc func updateCurrency() {
let urlRequest = URLRequest(url: URL(string: "https://api.coinbase.com/v2/exchange-rates?currency=\(from)")!) let urlRequest = URLRequest(url: URL(string: "https://api.coinbase.com/v2/exchange-rates?currency=\(from)")!)
@ -153,10 +205,10 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
title = String(format: "%@%.2f", prefix, value) title = String(format: "%@%.2f", prefix, value)
} }
let regularFont = attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15) let regularFont = getAttributedTitle().attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont, .baselineOffset: 1]) let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont, .baselineOffset: 1])
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count)) newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
attributedTitle = newTitle setAttributedTitle(newTitle)
} }
deinit { deinit {

View File

@ -1,34 +1,51 @@
import Foundation import Foundation
class DarkModeBarItem: CustomButtonTouchBarItem, Widget { class DarkModeBarItem: CustomButtonTouchBarItem {
static var name: String = "darkmode"
static var identifier: String = "com.toxblh.mtmr.darkmode"
private var timer: Timer! private var timer: Timer!
override class var typeIdentifier: String {
return "darkMode"
}
init(identifier: NSTouchBarItem.Identifier) { init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
isBordered = false
setWidth(value: 24)
tapClosure = { [weak self] in self?.DarkModeToggle() } self.setup()
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
refresh()
} }
required init?(coder _: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setup()
}
func setup() {
if getWidth() == 0.0 {
setWidth(value: 24)
}
self.setTapAction(
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.DarkModeToggle()
} )
)
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
refresh()
}
func DarkModeToggle() { func DarkModeToggle() {
DarkMode.isEnabled = !DarkMode.isEnabled DarkMode.isEnabled = !DarkMode.isEnabled
refresh() refresh()
} }
@objc func refresh() { @objc func refresh() {
image = DarkMode.isEnabled ? #imageLiteral(resourceName: "dark-mode-on") : #imageLiteral(resourceName: "dark-mode-off") self.setImage(DarkMode.isEnabled ? #imageLiteral(resourceName: "dark-mode-on") : #imageLiteral(resourceName: "dark-mode-off"))
} }
} }

View File

@ -0,0 +1,27 @@
//
// DeleteBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class DeleteBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "delete"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
print("DeleteBarItem.init")
self.title = "del"
self.setTapAction(EventAction().setKeyPressClosure(keycode: 117))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,27 @@
//
// DisplaySleep.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class DisplaySleepBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "displaySleep"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.title = "☕️"
self.setTapAction(EventAction().setShellScriptClosure(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -11,29 +11,47 @@ import Foundation
class DnDBarItem: CustomButtonTouchBarItem { class DnDBarItem: CustomButtonTouchBarItem {
private var timer: Timer! private var timer: Timer!
override class var typeIdentifier: String {
return "dnd"
}
init(identifier: NSTouchBarItem.Identifier) { init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
isBordered = false self.setup()
setWidth(value: 32)
tapClosure = { [weak self] in self?.DnDToggle() }
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
refresh()
} }
required init?(coder _: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setup()
}
func setup() {
if getWidth() == 0.0 {
setWidth(value: 32)
}
self.setTapAction(
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.DnDToggle()
} )
)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
refresh()
}
func DnDToggle() { func DnDToggle() {
DoNotDisturb.isEnabled = !DoNotDisturb.isEnabled DoNotDisturb.isEnabled = !DoNotDisturb.isEnabled
refresh() refresh()
} }
@objc func refresh() { @objc func refresh() {
image = DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off") self.setImage(DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off"))
} }
} }

View File

@ -0,0 +1,27 @@
//
// EscapeBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class EscapeBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "escape"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.title = "escape"
self.setTapAction(EventAction().setKeyPressClosure(keycode: 53))
self.align = .left
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,50 @@
//
// ExitTouchbarBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/30/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class ExitTouchbarBarItem: CustomButtonTouchBarItem {
private var timer: Timer!
override class var typeIdentifier: String {
return "exitTouchbar"
}
init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "")
if self.title == "" {
self.title = "exit"
}
self.setTapAction(EventAction.init({_ in
TouchBarController.shared.dismissTouchBar()
} ))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
if self.title == "" {
self.title = "exit"
}
self.setTapAction(EventAction.init({_ in
TouchBarController.shared.dismissTouchBar()
} ))
}
}

View File

@ -7,50 +7,96 @@
// //
import Cocoa import Cocoa
class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
var jsonItems: [BarItemDefinition] class GroupBarItem: CustomTouchBarItem, NSTouchBarDelegate {
private(set) var popoverItem: NSPopoverTouchBarItem!
var jsonItems: [BarItemDefinition] = []
override class var typeIdentifier: String {
return "group"
}
private enum CodingKeys: String, CodingKey {
case items
case title
}
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:] var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:] var items: [CustomTouchBarItem] = []
var leftIdentifiers: [NSTouchBarItem.Identifier] = [] var leftIdentifiers: [NSTouchBarItem.Identifier] = []
var centerIdentifiers: [NSTouchBarItem.Identifier] = [] var centerIdentifiers: [NSTouchBarItem.Identifier] = []
var centerItems: [NSTouchBarItem] = [] var centerItems: [NSTouchBarItem] = []
var rightIdentifiers: [NSTouchBarItem.Identifier] = [] var rightIdentifiers: [NSTouchBarItem.Identifier] = []
var scrollArea: NSCustomTouchBarItem? var scrollArea: NSCustomTouchBarItem?
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString)) var swipeItems: [SwipeItem] = []
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.GroupScrollArea.".appending(UUID().uuidString))
var basicView: BasicView?
var basicViewIdentifier = NSTouchBarItem.Identifier("com.toxblh.mtmr.GroupScrollView.".appending(UUID().uuidString))
init(identifier: NSTouchBarItem.Identifier, items: [BarItemDefinition]) {
init(identifier: NSTouchBarItem.Identifier, items: [BarItemDefinition], title: String) {
jsonItems = items jsonItems = items
popoverItem = NSPopoverTouchBarItem(identifier: identifier)
super.init(identifier: identifier) super.init(identifier: identifier)
popoverTouchBar.delegate = self
self.setup(title: title)
} }
required init?(coder _: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
deinit {} required init(from decoder: Decoder) throws {
try super.init(from: decoder)
popoverItem = NSPopoverTouchBarItem(identifier: identifier)
@objc override func showPopover(_: Any?) { let container = try decoder.container(keyedBy: CodingKeys.self)
itemDefinitions = [:] self.jsonItems = try container.decode([BarItemDefinition].self, forKey: .items)
items = [:] let title = try container.decodeIfPresent(String.self, forKey: .title) ?? " "
leftIdentifiers = []
centerItems = []
rightIdentifiers = []
loadItemDefinitions(jsonItems: jsonItems) self.setup(title: title)
createItems() }
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
items[identifier] func setup(title: String) {
let button = NSButton(title: title, target: self,
action: #selector(GroupBarItem.showPopover(_:)))
// Use the built-in gesture recognizer for tap and hold to open our popover's NSTouchBar.
let gestureRecognizer = popoverItem.makeStandardActivatePopoverGestureRecognizer()
button.addGestureRecognizer(gestureRecognizer)
popoverItem.collapsedRepresentation = button
view = button
if getWidth() == 0.0 {
setWidth(value: 60)
}
}
@objc func showPopover(_: Any?) {
items = getItems(newItems: jsonItems)
let leftItems = items.compactMap({ (item) -> CustomTouchBarItem? in
item.align == .left || item.align == .center ? item : nil
})
let centerItems = items.compactMap({ (item) -> CustomTouchBarItem? in
item.align == .center && false ? item : nil
})
let rightItems = items.compactMap({ (item) -> CustomTouchBarItem? in
item.align == .right ? item : nil
}) })
centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString)) let centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems) let scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
TouchBarController.shared.touchBar.delegate = self TouchBarController.shared.touchBar.delegate = self
TouchBarController.shared.touchBar.defaultItemIdentifiers = [] TouchBarController.shared.touchBar.defaultItemIdentifiers = [basicViewIdentifier]
TouchBarController.shared.touchBar.defaultItemIdentifiers = leftIdentifiers + [centerScrollArea] + rightIdentifiers
basicView = BasicView(identifier: basicViewIdentifier, items: leftItems + [scrollArea] + rightItems, swipeItems: swipeItems)
basicView?.legacyGesturesEnabled = AppSettings.multitouchGestures
if AppSettings.showControlStripState { if AppSettings.showControlStripState {
presentSystemModal(TouchBarController.shared.touchBar, systemTrayItemIdentifier: .controlStripItem) presentSystemModal(TouchBarController.shared.touchBar, systemTrayItemIdentifier: .controlStripItem)
@ -60,41 +106,23 @@ class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
} }
func touchBar(_: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? { func touchBar(_: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
if identifier == centerScrollArea { if identifier == basicViewIdentifier {
return scrollArea return basicView
} }
guard let item = self.items[identifier], return nil
let definition = self.itemDefinitions[identifier],
definition.align != .center else {
return nil
}
return item
} }
func loadItemDefinitions(jsonItems: [BarItemDefinition]) { func getItems(newItems: [BarItemDefinition]) -> [CustomTouchBarItem] {
let dateFormatter = DateFormatter() var items: [CustomTouchBarItem] = []
dateFormatter.dateFormat = "HH-mm-ss" for item in newItems {
let time = dateFormatter.string(from: Date()) if item.obj is SwipeItem {
for item in jsonItems { swipeItems.append(item.obj as! SwipeItem)
let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString) } else {
let identifier = NSTouchBarItem.Identifier(identifierString) items.append(item.obj)
itemDefinitions[identifier] = item
if item.align == .left {
leftIdentifiers.append(identifier)
}
if item.align == .right {
rightIdentifiers.append(identifier)
}
if item.align == .center {
centerIdentifiers.append(identifier)
} }
} }
} return items
func createItems() {
for (identifier, definition) in itemDefinitions {
items[identifier] = TouchBarController.shared.createItem(forIdentifier: identifier, definition: definition)
}
} }
} }

View File

@ -0,0 +1,27 @@
//
// illuminationDownBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class IlluminationDownBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "illuminationDown"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setImage(#imageLiteral(resourceName: "ill_down"))
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_ILLUMINATION_DOWN))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,27 @@
//
// illuminationUpBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class IlluminationUpBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "illuminationUp"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setImage(#imageLiteral(resourceName: "ill_up"))
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_ILLUMINATION_UP))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -12,21 +12,38 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
fileprivate var notificationCenter: CFNotificationCenter fileprivate var notificationCenter: CFNotificationCenter
let buttonSize = NSSize(width: 21, height: 21) let buttonSize = NSSize(width: 21, height: 21)
override class var typeIdentifier: String {
return "inputsource"
}
init(identifier: NSTouchBarItem.Identifier) { init(identifier: NSTouchBarItem.Identifier) {
notificationCenter = CFNotificationCenterGetDistributedCenter() notificationCenter = CFNotificationCenterGetDistributedCenter()
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
observeIputSourceChangedNotification() self.setup()
textInputSourceDidChange()
tapClosure = { [weak self] in
self?.switchInputSource()
}
} }
required init?(coder _: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
required init(from decoder: Decoder) throws {
notificationCenter = CFNotificationCenterGetDistributedCenter()
try super.init(from: decoder)
self.setup()
}
func setup() {
observeIputSourceChangedNotification()
textInputSourceDidChange()
self.setTapAction(
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.switchInputSource()
}
))
}
deinit { deinit {
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())) CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()))
} }
@ -45,7 +62,7 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
if let iconImage = iconImage { if let iconImage = iconImage {
iconImage.size = buttonSize iconImage.size = buttonSize
image = iconImage self.setImage(iconImage)
title = "" title = ""
} else { } else {
title = currentSource.name title = currentSource.name

View File

@ -34,16 +34,35 @@ class MusicBarItem: CustomButtonTouchBarItem {
private var timer: Timer? private var timer: Timer?
private let iconSize = NSSize(width: 21, height: 21) private let iconSize = NSSize(width: 21, height: 21)
private enum CodingKeys: String, CodingKey {
case refreshInterval
case disableMarquee
}
override class var typeIdentifier: String {
return "music"
}
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, disableMarquee: Bool) { init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, disableMarquee: Bool) {
self.interval = interval self.interval = interval
self.disableMarquee = disableMarquee self.disableMarquee = disableMarquee
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
isBordered = false
tapClosure = { [weak self] in self?.playPause() } self.setup()
longTapClosure = { [weak self] in self?.nextTrack() } }
func setup() {
self.setTapAction(
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.playPause()
} )
)
self.setLongTapAction(
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.nextTrack()
} )
)
refreshAndSchedule() refreshAndSchedule()
} }
@ -61,10 +80,22 @@ class MusicBarItem: CustomButtonTouchBarItem {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 5.0
self.disableMarquee = try container.decodeIfPresent(Bool.self, forKey: .disableMarquee) ?? false
try super.init(from: decoder)
self.setup()
}
@objc func playPause() { @objc func playPause() {
for ident in playerBundleIdentifiers { for ident in playerBundleIdentifiers {
print("checking \(ident)")
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) { if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
print("musicPlayer \(musicPlayer)")
if musicPlayer.isRunning { if musicPlayer.isRunning {
print("musicPlayer.isRunning \(musicPlayer.isRunning)")
if ident == .Spotify { if ident == .Spotify {
let mp = (musicPlayer as SpotifyApplication) let mp = (musicPlayer as SpotifyApplication)
mp.playpause!() mp.playpause!()
@ -194,6 +225,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
for ident in playerBundleIdentifiers { for ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) { if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
if musicPlayer.isRunning { if musicPlayer.isRunning {
print("musicPlayer \(musicPlayer)")
var tempTitle = "" var tempTitle = ""
if ident == .Spotify { if ident == .Spotify {
tempTitle = (musicPlayer as SpotifyApplication).title tempTitle = (musicPlayer as SpotifyApplication).title
@ -268,7 +300,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: ident.rawValue) { let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: ident.rawValue) {
let image = NSWorkspace.shared.icon(forFile: appPath) let image = NSWorkspace.shared.icon(forFile: appPath)
image.size = self.iconSize image.size = self.iconSize
self.image = image self.setImage(image)
iconUpdated = true iconUpdated = true
} }
break break
@ -278,7 +310,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
DispatchQueue.main.async { DispatchQueue.main.async {
if !iconUpdated { if !iconUpdated {
self.image = nil self.setImage(nil)
} }
if !titleUpdated { if !titleUpdated {

View File

@ -0,0 +1,27 @@
//
// MuteBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class MuteBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "mute"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setImage(NSImage(named: NSImage.touchBarAudioOutputMuteTemplateName)!)
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_MUTE))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -8,12 +8,17 @@
import Foundation import Foundation
class NetworkBarItem: CustomButtonTouchBarItem, Widget { class NetworkBarItem: CustomButtonTouchBarItem {
static var name: String = "network"
static var identifier: String = "com.toxblh.mtmr.network"
private let flip: Bool private let flip: Bool
private enum CodingKeys: String, CodingKey {
case flip
}
override class var typeIdentifier: String {
return "network"
}
init(identifier: NSTouchBarItem.Identifier, flip: Bool = false) { init(identifier: NSTouchBarItem.Identifier, flip: Bool = false) {
self.flip = flip self.flip = flip
super.init(identifier: identifier, title: " ") super.init(identifier: identifier, title: " ")
@ -24,6 +29,14 @@ class NetworkBarItem: CustomButtonTouchBarItem, Widget {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.flip = try container.decodeIfPresent(Bool.self, forKey: .flip) ?? false
try super.init(from: decoder)
startMonitoringProcess()
}
func startMonitoringProcess() { func startMonitoringProcess() {
var pipe: Pipe var pipe: Pipe
var outputHandle: FileHandle var outputHandle: FileHandle
@ -144,6 +157,6 @@ class NetworkBarItem: CustomButtonTouchBarItem, Widget {
} }
self.attributedTitle = newTitle self.setAttributedTitle(newTitle)
} }
} }

View File

@ -0,0 +1,27 @@
//
// NextBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class NextBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "next"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setImage(NSImage(named: NSImage.touchBarFastForwardTemplateName)!)
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_NEXT))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -12,6 +12,10 @@ class NightShiftBarItem: CustomButtonTouchBarItem {
private let nsclient = CBBlueLightClient() private let nsclient = CBBlueLightClient()
private var timer: Timer! private var timer: Timer!
override class var typeIdentifier: String {
return "nightShift"
}
private var blueLightStatus: Status { private var blueLightStatus: Status {
var status: Status = Status() var status: Status = Status()
nsclient.getBlueLightStatus(&status) nsclient.getBlueLightStatus(&status)
@ -28,26 +32,40 @@ class NightShiftBarItem: CustomButtonTouchBarItem {
init(identifier: NSTouchBarItem.Identifier) { init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
isBordered = false self.setup()
setWidth(value: 28)
tapClosure = { [weak self] in self?.nightShiftAction() }
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
refresh()
} }
required init?(coder _: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setup()
}
func setup() {
if getWidth() == 0.0 {
setWidth(value: 28)
}
self.setTapAction(
EventAction( { [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.nightShiftAction()
} )
)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
refresh()
}
func nightShiftAction() { func nightShiftAction() {
setNightShift(state: !isNightShiftEnabled) setNightShift(state: !isNightShiftEnabled)
refresh() refresh()
} }
@objc func refresh() { @objc func refresh() {
image = isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff") self.setImage(isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff"))
} }
} }

View File

@ -0,0 +1,27 @@
//
// PlayBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class PlayBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "play"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setImage(NSImage(named: NSImage.touchBarPlayPauseTemplateName)!)
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_PLAY))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -8,25 +8,14 @@
import Cocoa import Cocoa
class PomodoroBarItem: CustomButtonTouchBarItem, Widget { class PomodoroBarItem: CustomButtonTouchBarItem {
static let identifier = "com.toxblh.mtmr.pomodoro." override class var typeIdentifier: String {
static let name = "pomodoro" return "pomodoro"
static let decoder: ParametersDecoder = { decoder in }
enum CodingKeys: String, CodingKey {
case workTime
case restTime
}
let container = try decoder.container(keyedBy: CodingKeys.self) private enum CodingKeys: String, CodingKey {
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime) case workTime
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime) case restTime
return (
item: .pomodoro(workTime: workTime ?? 1500.00, restTime: restTime ?? 300),
action: .none,
longAction: .none,
parameters: [:]
)
} }
private enum TimeTypes { private enum TimeTypes {
@ -46,18 +35,44 @@ class PomodoroBarItem: CustomButtonTouchBarItem, Widget {
return String(format: "%.2i:%.2i", timeLeft / 60, timeLeft % 60) return String(format: "%.2i:%.2i", timeLeft / 60, timeLeft % 60)
} }
init(identifier: NSTouchBarItem.Identifier, workTime: TimeInterval, restTime: TimeInterval) { init(identifier: NSTouchBarItem.Identifier, workTime: TimeInterval, restTime: TimeInterval) {
self.workTime = workTime self.workTime = workTime
self.restTime = restTime self.restTime = restTime
super.init(identifier: identifier, title: defaultTitle) super.init(identifier: identifier, title: defaultTitle)
tapClosure = { [weak self] in self?.startStopWork() }
longTapClosure = { [weak self] in self?.startStopRest() } self.setup()
} }
required init?(coder _: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.workTime = try container.decodeIfPresent(Double.self, forKey: .workTime) ?? 1500.0
self.restTime = try container.decodeIfPresent(Double.self, forKey: .restTime) ?? 600.0
try super.init(from: decoder)
self.title = defaultTitle
self.setup()
}
func setup() {
self.setTapAction(
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.startStopWork()
} )
)
self.setLongTapAction(
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.startStopRest()
} )
)
}
deinit { deinit {
timer?.cancel() timer?.cancel()
timer = nil timer = nil

View File

@ -0,0 +1,27 @@
//
// PreviousBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class PreviousBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "previous"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setImage(NSImage(named: NSImage.touchBarRewindTemplateName)!)
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_PREVIOUS))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,27 @@
//
// SleepBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class SleepBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "sleep"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.title = "☕️"
self.setTapAction(EventAction().setShellScriptClosure(executable: "/usr/bin/pmset", parameters: ["sleepnow"]))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -4,7 +4,40 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
private let dateFormatter = DateFormatter() private let dateFormatter = DateFormatter()
private var timer: Timer! private var timer: Timer!
private enum CodingKeys: String, CodingKey {
case formatTemplate
case timeZone
case locale
}
override class var typeIdentifier: String {
return "timeButton"
}
init(identifier: NSTouchBarItem.Identifier, formatTemplate: String, timeZone: String? = nil, locale: String? = nil) { init(identifier: NSTouchBarItem.Identifier, formatTemplate: String, timeZone: String? = nil, locale: String? = nil) {
super.init(identifier: identifier, title: " ")
self.setupFormatter(formatTemplate: formatTemplate, timeZone: timeZone, locale: locale)
self.setupTimer()
updateTime()
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
let timeZone = try container.decodeIfPresent(String.self, forKey: .timeZone) ?? nil
let locale = try container.decodeIfPresent(String.self, forKey: .locale) ?? nil
try super.init(from: decoder)
self.setupFormatter(formatTemplate: template, timeZone: timeZone, locale: locale)
self.setupTimer()
updateTime()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupFormatter(formatTemplate: String, timeZone: String? = nil, locale: String? = nil) {
dateFormatter.dateFormat = formatTemplate dateFormatter.dateFormat = formatTemplate
if let locale = locale { if let locale = locale {
dateFormatter.locale = Locale(identifier: locale) dateFormatter.locale = Locale(identifier: locale)
@ -12,14 +45,10 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
if let abbr = timeZone { if let abbr = timeZone {
dateFormatter.timeZone = TimeZone(abbreviation: abbr) dateFormatter.timeZone = TimeZone(abbreviation: abbr)
} }
super.init(identifier: identifier, title: " ")
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
isBordered = false
updateTime()
} }
required init?(coder _: NSCoder) { func setupTimer() {
fatalError("init(coder:) has not been implemented") timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
} }
@objc func updateTime() { @objc func updateTime() {

View File

@ -0,0 +1,27 @@
//
// volumeDownBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class VolumeDownBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "volumeDown"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setImage(NSImage(named: NSImage.touchBarVolumeDownTemplateName)!)
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_SOUND_DOWN))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,27 @@
//
// VolumeUpBarItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 4/27/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class VolumeUpBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "volumeUp"
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.setImage(NSImage(named: NSImage.touchBarVolumeUpTemplateName)!)
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_SOUND_UP))
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -3,12 +3,33 @@ import AVFoundation
import Cocoa import Cocoa
import CoreAudio import CoreAudio
class VolumeViewController: NSCustomTouchBarItem { class VolumeViewController: CustomTouchBarItem {
private(set) var sliderItem: CustomSlider! private(set) var sliderItem: CustomSlider!
override class var typeIdentifier: String {
return "volume"
}
private enum CodingKeys: String, CodingKey {
case image
}
init(identifier: NSTouchBarItem.Identifier, image: NSImage? = nil) { init(identifier: NSTouchBarItem.Identifier, image: NSImage? = nil) {
super.init(identifier: identifier) super.init(identifier: identifier)
self.setup(image: image)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let image = try container.decodeIfPresent(Source.self, forKey: .image)?.image
try super.init(from: decoder)
self.setup(image: image)
}
func setup(image: NSImage?) {
var forPropertyAddress = AudioObjectPropertyAddress( var forPropertyAddress = AudioObjectPropertyAddress(
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume, mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
mScope: kAudioDevicePropertyScopeOutput, mScope: kAudioDevicePropertyScopeOutput,
@ -37,6 +58,7 @@ class VolumeViewController: NSCustomTouchBarItem {
} }
} }
required init?(coder _: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }

View File

@ -22,9 +22,20 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
private var manager: CLLocationManager! private var manager: CLLocationManager!
override class var typeIdentifier: String {
return "weather"
}
private enum CodingKeys: String, CodingKey {
case refreshInterval
case units
case api_key
case icon_type
}
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text") { init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text") {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck") self.activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval self.activity.interval = interval
self.units = units self.units = units
self.api_key = api_key self.api_key = api_key
@ -44,6 +55,43 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
self.setup()
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
self.activity = NSBackgroundActivityScheduler(identifier: CustomTouchBarItem.createIdentifier("Weather.updatecheck").rawValue)
self.activity.interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
self.units = try container.decodeIfPresent(String.self, forKey: .units) ?? "metric"
self.api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
if self.units == "metric" {
units_str = "°C"
}
if self.units == "imperial" {
units_str = "°F"
}
if icon_type == "images" {
iconsSource = iconsImages
} else {
iconsSource = iconsText
}
try super.init(from: decoder)
self.setup()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
let status = CLLocationManager.authorizationStatus() let status = CLLocationManager.authorizationStatus()
if status == .restricted || status == .denied { if status == .restricted || status == .denied {
print("User permission not given") print("User permission not given")
@ -69,10 +117,6 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
manager.startUpdatingLocation() manager.startUpdatingLocation()
} }
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func updateWeather() { @objc func updateWeather() {
if location != nil { if location != nil {
let urlRequest = URLRequest(url: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)&units=\(units)&appid=\(api_key)")!) let urlRequest = URLRequest(url: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)&units=\(units)&appid=\(api_key)")!)

View File

@ -20,12 +20,37 @@ class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate
private var prevLocation: CLLocation! private var prevLocation: CLLocation!
private var manager: CLLocationManager! private var manager: CLLocationManager!
override class var typeIdentifier: String {
return "weather"
}
private enum CodingKeys: String, CodingKey {
case refreshInterval
}
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) { init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck") activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval activity.interval = interval
super.init(identifier: identifier, title: "") super.init(identifier: identifier, title: "")
self.setup()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.activity = NSBackgroundActivityScheduler(identifier: CustomTouchBarItem.createIdentifier("YandexWeather.updatecheck").rawValue)
self.activity.interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
try super.init(from: decoder)
self.setup()
}
func setup() {
let status = CLLocationManager.authorizationStatus() let status = CLLocationManager.authorizationStatus()
if status == .restricted || status == .denied { if status == .restricted || status == .denied {
print("User permission not given") print("User permission not given")
@ -50,11 +75,12 @@ class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
manager.startUpdatingLocation() manager.startUpdatingLocation()
tapClosure = tapClosure ?? defaultTapAction
}
required init?(coder _: NSCoder) { self.setTapAction(
fatalError("init(coder:) has not been implemented") EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.defaultTapAction()
} )
)
} }
@objc func updateWeather() { @objc func updateWeather() {