mirror of
https://github.com/Toxblh/MTMR.git
synced 2026-01-10 17:08:39 +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:
parent
c51b6acdf1
commit
101a81bf3b
@ -5,11 +5,43 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
private let interval: TimeInterval
|
||||
private var forceHideConstraint: NSLayoutConstraint!
|
||||
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]) {
|
||||
self.interval = interval
|
||||
self.alternativeImages = alternativeImages
|
||||
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)
|
||||
title = "scheduled"
|
||||
DispatchQueue.appleScriptQueue.async {
|
||||
@ -38,10 +70,6 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func refreshAndSchedule() {
|
||||
#if DEBUG
|
||||
print("refresh happened (interval \(interval)), self \(identifier.rawValue))")
|
||||
@ -62,7 +90,7 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
func updateIcon(iconLabel: String) {
|
||||
if alternativeImages[iconLabel] != nil {
|
||||
DispatchQueue.main.async {
|
||||
self.image = self.alternativeImages[iconLabel]!.image
|
||||
self.setImage(self.alternativeImages[iconLabel]!.image)
|
||||
}
|
||||
} else {
|
||||
print("Cannot find icon with label \"\(iconLabel)\"")
|
||||
|
||||
@ -15,6 +15,7 @@ class BasicView: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
||||
var threefingers: NSPanGestureRecognizer!
|
||||
var fourfingers: NSPanGestureRecognizer!
|
||||
var swipeItems: [SwipeItem] = []
|
||||
var items: [NSTouchBarItem] = []
|
||||
var prevPositions: [Int: CGFloat] = [2:0, 3:0, 4:0]
|
||||
|
||||
// legacy gesture positions
|
||||
@ -25,6 +26,7 @@ class BasicView: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
||||
init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem], swipeItems: [SwipeItem]) {
|
||||
super.init(identifier: identifier)
|
||||
self.swipeItems = swipeItems
|
||||
self.items = items
|
||||
let views = items.compactMap { $0.view }
|
||||
let stackView = NSStackView(views: views)
|
||||
stackView.spacing = 1
|
||||
|
||||
@ -8,24 +8,130 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
||||
var tapClosure: (() -> Void)?
|
||||
var longTapClosure: (() -> Void)? {
|
||||
didSet {
|
||||
longClick.isEnabled = longTapClosure != nil
|
||||
enum Align: String, Decodable {
|
||||
case left
|
||||
case center
|
||||
case right
|
||||
}
|
||||
|
||||
// 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: ()->() = {}
|
||||
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 singleClick: HapticClickGestureRecognizer!
|
||||
private var longClick: LongPressGestureRecognizer!
|
||||
private var attributedTitle: NSAttributedString
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, title: String) {
|
||||
attributedTitle = title.defaultTouchbarAttributedString
|
||||
|
||||
super.init(identifier: identifier)
|
||||
|
||||
initButton(title: title, imageSource: nil)
|
||||
}
|
||||
|
||||
func initButton(title: String, imageSource: Source?) {
|
||||
button = CustomHeightButton(title: title, target: nil, action: nil)
|
||||
self.setImage(imageSource?.image)
|
||||
|
||||
longClick = LongPressGestureRecognizer(target: self, action: #selector(handleGestureLong))
|
||||
longClick.isEnabled = false
|
||||
@ -33,6 +139,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
longClick.delegate = self
|
||||
|
||||
singleClick = HapticClickGestureRecognizer(target: self, action: #selector(handleGestureSingle))
|
||||
singleClick.isEnabled = false
|
||||
singleClick.allowedTouchTypes = .direct
|
||||
singleClick.delegate = self
|
||||
|
||||
@ -40,6 +147,45 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
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) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
@ -58,27 +204,35 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
|
||||
var title: String {
|
||||
get {
|
||||
return attributedTitle.string
|
||||
return getAttributedTitle().string
|
||||
}
|
||||
set {
|
||||
attributedTitle = newValue.defaultTouchbarAttributedString
|
||||
setAttributedTitle(newValue.defaultTouchbarAttributedString)
|
||||
}
|
||||
}
|
||||
|
||||
var attributedTitle: NSAttributedString {
|
||||
didSet {
|
||||
button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
|
||||
button?.attributedTitle = attributedTitle
|
||||
}
|
||||
|
||||
func getAttributedTitle() -> NSAttributedString {
|
||||
return attributedTitle
|
||||
}
|
||||
|
||||
func setAttributedTitle(_ attributedTitle: NSAttributedString) {
|
||||
self.attributedTitle = attributedTitle
|
||||
button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
|
||||
button?.attributedTitle = attributedTitle
|
||||
}
|
||||
|
||||
private var image: NSImage?
|
||||
|
||||
func getImage() -> NSImage? {
|
||||
return image
|
||||
}
|
||||
|
||||
func setImage(_ image: NSImage?) {
|
||||
self.image = image
|
||||
button.image = image
|
||||
}
|
||||
|
||||
var image: NSImage? {
|
||||
didSet {
|
||||
button.image = image
|
||||
}
|
||||
}
|
||||
|
||||
private func reinstallButton() {
|
||||
func reinstallButton() {
|
||||
let title = button.attributedTitle
|
||||
let image = button.image
|
||||
let cell = CustomButtonCell(parentItem: self)
|
||||
@ -116,7 +270,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
@objc func handleGestureSingle(gr: NSClickGestureRecognizer) {
|
||||
switch gr.state {
|
||||
case .ended:
|
||||
tapClosure?()
|
||||
self.tapAction?.closure(self)
|
||||
break
|
||||
default:
|
||||
break
|
||||
@ -126,7 +280,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
@objc func handleGestureLong(gr: NSPressGestureRecognizer) {
|
||||
switch gr.state {
|
||||
case .possible: // tiny hack because we're calling action manually
|
||||
(self.longTapClosure ?? self.tapClosure)?()
|
||||
(self.longTapAction?.closure ?? self.tapAction?.closure)?(self)
|
||||
break
|
||||
default:
|
||||
break
|
||||
@ -156,7 +310,7 @@ class CustomButtonCell: NSButtonCell {
|
||||
if flag {
|
||||
setAttributedTitle(attributedTitle, withColor: .lightGray)
|
||||
} else if let parentItem = self.parentItem {
|
||||
attributedTitle = parentItem.attributedTitle
|
||||
attributedTitle = parentItem.getAttributedTitle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
184
MTMR/EventActions.swift
Normal file
184
MTMR/EventActions.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.25</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>402</string>
|
||||
<string>622</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@ -3,550 +3,90 @@ import Foundation
|
||||
|
||||
extension Data {
|
||||
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 {
|
||||
let type: ItemType
|
||||
let action: ActionType
|
||||
let longAction: LongActionType
|
||||
let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]
|
||||
let obj: CustomTouchBarItem
|
||||
|
||||
enum ParsingErrors: Error {
|
||||
case noMatchingType(description: String)
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case objtype = "type"
|
||||
}
|
||||
|
||||
init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]) {
|
||||
self.type = type
|
||||
self.action = action
|
||||
self.longAction = longAction
|
||||
self.additionalParameters = additionalParameters
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try container.decode(String.self, forKey: .type)
|
||||
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type)
|
||||
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 -> (
|
||||
item: ItemType,
|
||||
action: ActionType,
|
||||
longAction: LongActionType,
|
||||
parameters: [GeneralParameters.CodingKeys: GeneralParameter]
|
||||
)
|
||||
|
||||
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 (
|
||||
item: .staticButton(title: "del"),
|
||||
action: .keyPress(keycode: 117),
|
||||
longAction: .none,
|
||||
parameters: [:]
|
||||
) },
|
||||
|
||||
"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: [:]
|
||||
) },
|
||||
|
||||
|
||||
static let types: [CustomTouchBarItem.Type] = [
|
||||
|
||||
// custom buttons
|
||||
CustomButtonTouchBarItem.self,
|
||||
AppleScriptTouchBarItem.self,
|
||||
ShellScriptTouchBarItem.self,
|
||||
|
||||
|
||||
// basic widget buttons
|
||||
EscapeBarItem.self,
|
||||
DeleteBarItem.self,
|
||||
BrightnessUpBarItem.self,
|
||||
BrightnessDownBarItem.self,
|
||||
IlluminationUpBarItem.self,
|
||||
IlluminationDownBarItem.self,
|
||||
VolumeUpBarItem.self,
|
||||
VolumeDownBarItem.self,
|
||||
MuteBarItem.self,
|
||||
PreviousBarItem.self,
|
||||
PlayBarItem.self,
|
||||
NextBarItem.self,
|
||||
SleepBarItem.self,
|
||||
DisplaySleepBarItem.self,
|
||||
|
||||
|
||||
// custom widgets
|
||||
TimeTouchBarItem.self,
|
||||
BatteryBarItem.self,
|
||||
AppScrubberTouchBarItem.self,
|
||||
VolumeViewController.self,
|
||||
BrightnessViewController.self,
|
||||
WeatherBarItem.self,
|
||||
YandexWeatherBarItem.self,
|
||||
CurrencyBarItem.self,
|
||||
InputSourceBarItem.self,
|
||||
MusicBarItem.self,
|
||||
NightShiftBarItem.self,
|
||||
DnDBarItem.self,
|
||||
PomodoroBarItem.self,
|
||||
NetworkBarItem.self,
|
||||
DarkModeBarItem.self,
|
||||
|
||||
|
||||
// custom-custom objects!
|
||||
SwipeItem.self,
|
||||
GroupBarItem.self,
|
||||
ExitTouchbarBarItem.self,
|
||||
CloseBarItem.self,
|
||||
]
|
||||
|
||||
static let sharedInstance = SupportedTypesHolder()
|
||||
|
||||
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(obj: CustomTouchBarItem) {
|
||||
self.obj = obj
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try container.decode(ItemTypeRaw.self, forKey: .type)
|
||||
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:
|
||||
let title = try container.decode(String.self, forKey: .title)
|
||||
self = .staticButton(title: title)
|
||||
|
||||
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)
|
||||
let objType = try container.decode(String.self, forKey: .objtype)
|
||||
|
||||
|
||||
for obj in BarItemDefinition.types {
|
||||
if obj.typeIdentifier == objType {
|
||||
self.obj = try obj.init(from: decoder)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
result[.align] = .align(align)
|
||||
|
||||
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
|
||||
|
||||
|
||||
print("Cannot find preset mapping for \(objType)")
|
||||
throw ParsingErrors.noMatchingType(description: "Cannot find preset mapping for \(objType)")
|
||||
}
|
||||
}
|
||||
|
||||
@ -646,12 +186,6 @@ extension Data {
|
||||
}
|
||||
}
|
||||
|
||||
enum Align: String, Decodable {
|
||||
case left
|
||||
case center
|
||||
case right
|
||||
}
|
||||
|
||||
extension URL {
|
||||
var appleScript: NSAppleScript? {
|
||||
guard FileManager.default.fileExists(atPath: path) else { return nil }
|
||||
|
||||
@ -12,11 +12,40 @@ class ShellScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
private let source: String
|
||||
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) {
|
||||
self.interval = interval
|
||||
self.source = source.string ?? "echo No \"source\""
|
||||
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)
|
||||
|
||||
DispatchQueue.shellScriptQueue.async {
|
||||
@ -24,10 +53,6 @@ class ShellScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func refreshAndSchedule() {
|
||||
// Execute script and get result
|
||||
let scriptResult = execute(source)
|
||||
@ -45,7 +70,7 @@ class ShellScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
if (newBackgoundColor != self?.backgroundColor) { // performance optimization because of reinstallButton
|
||||
self?.backgroundColor = newBackgoundColor
|
||||
}
|
||||
self?.attributedTitle = title
|
||||
self?.setAttributedTitle(title)
|
||||
self?.forceHideConstraint.isActive = scriptResult == ""
|
||||
}
|
||||
|
||||
|
||||
@ -9,12 +9,25 @@
|
||||
import Foundation
|
||||
import Foundation
|
||||
|
||||
class SwipeItem: NSCustomTouchBarItem {
|
||||
class SwipeItem: CustomTouchBarItem {
|
||||
private var scriptApple: NSAppleScript?
|
||||
private var scriptBash: String?
|
||||
private var direction: String
|
||||
private var fingers: Int
|
||||
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?) {
|
||||
self.direction = direction
|
||||
self.fingers = fingers
|
||||
@ -28,6 +41,19 @@ class SwipeItem: NSCustomTouchBarItem {
|
||||
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) {
|
||||
if direction == "right" && Float(offset) > self.minOffset && self.fingers == fingers {
|
||||
self.execute()
|
||||
|
||||
@ -16,52 +16,6 @@ struct ExactItem {
|
||||
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
||||
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 {
|
||||
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
|
||||
@ -73,12 +27,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
var touchBar: NSTouchBar!
|
||||
|
||||
fileprivate var lastPresetPath = ""
|
||||
var jsonItems: [BarItemDefinition] = []
|
||||
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
||||
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
|
||||
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var centerIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var items: [CustomTouchBarItem] = []
|
||||
var basicViewIdentifier = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollView.".appending(UUID().uuidString))
|
||||
var basicView: BasicView?
|
||||
var swipeItems: [SwipeItem] = []
|
||||
@ -90,14 +39,6 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in self?.dismissTouchBar() }), longAction: .none)
|
||||
|
||||
SupportedTypesHolder.sharedInstance.register(typename: "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
|
||||
|
||||
@ -108,21 +49,23 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
reloadStandardConfig()
|
||||
}
|
||||
|
||||
func createAndUpdatePreset(newJsonItems: [BarItemDefinition]) {
|
||||
func createAndUpdatePreset(newItems: [BarItemDefinition]) {
|
||||
if let oldBar = self.touchBar {
|
||||
minimizeSystemModal(oldBar)
|
||||
}
|
||||
touchBar = NSTouchBar()
|
||||
jsonItems = newJsonItems
|
||||
itemDefinitions = [:]
|
||||
items = [:]
|
||||
(items, swipeItems) = getItems(newItems: newItems)
|
||||
|
||||
loadItemDefinitions(jsonItems: jsonItems)
|
||||
createItems()
|
||||
|
||||
let centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||
items[identifier]
|
||||
let leftItems = items.compactMap({ (item) -> CustomTouchBarItem? in
|
||||
item.align == .left ? item : nil
|
||||
})
|
||||
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 scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
|
||||
@ -130,15 +73,17 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
touchBar.delegate = self
|
||||
touchBar.defaultItemIdentifiers = [basicViewIdentifier]
|
||||
|
||||
let leftItems = leftIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||
items[identifier]
|
||||
})
|
||||
let rightItems = rightIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||
items[identifier]
|
||||
})
|
||||
|
||||
basicView = BasicView(identifier: basicViewIdentifier, items:leftItems + [scrollArea] + rightItems, swipeItems: swipeItems)
|
||||
basicView = BasicView(identifier: basicViewIdentifier, items: leftItems + [scrollArea] + rightItems, swipeItems: swipeItems)
|
||||
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()
|
||||
}
|
||||
@ -166,41 +111,26 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
reloadPreset(path: presetPath)
|
||||
}
|
||||
|
||||
func reloadPreset(path: String) {
|
||||
lastPresetPath = path
|
||||
let items = path.fileData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
|
||||
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)
|
||||
}
|
||||
func reloadPreset(path: String?) {
|
||||
if path != nil {
|
||||
lastPresetPath = path!
|
||||
}
|
||||
|
||||
let items = lastPresetPath.fileData?.barItemDefinitions() ?? [BarItemDefinition(obj: CustomButtonTouchBarItem(title: "bad preset"))]
|
||||
createAndUpdatePreset(newItems: items)
|
||||
}
|
||||
|
||||
func createItems() {
|
||||
for (identifier, definition) in itemDefinitions {
|
||||
let item = createItem(forIdentifier: identifier, definition: definition)
|
||||
if item is SwipeItem {
|
||||
swipeItems.append(item as! SwipeItem)
|
||||
func getItems(newItems: [BarItemDefinition]) -> ([CustomTouchBarItem], [SwipeItem]) {
|
||||
var items: [CustomTouchBarItem] = []
|
||||
var swipeItems: [SwipeItem] = []
|
||||
for item in newItems {
|
||||
if item.obj is SwipeItem {
|
||||
swipeItems.append(item.obj as! SwipeItem)
|
||||
} else {
|
||||
items[identifier] = item
|
||||
items.append(item.obj)
|
||||
}
|
||||
}
|
||||
return (items, swipeItems)
|
||||
}
|
||||
|
||||
@objc func setupControlStripPresence() {
|
||||
@ -224,7 +154,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func dismissTouchBar() {
|
||||
@objc func dismissTouchBar() {
|
||||
minimizeSystemModal(touchBar)
|
||||
updateControlStripPresence()
|
||||
}
|
||||
@ -241,198 +171,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
class AppScrubberTouchBarItem: NSCustomTouchBarItem {
|
||||
class AppScrubberTouchBarItem: CustomTouchBarItem {
|
||||
private var scrollView = NSScrollView()
|
||||
private var autoResize: Bool = false
|
||||
private var widthConstraint: NSLayoutConstraint?
|
||||
@ -19,6 +19,20 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
|
||||
private var frontmostApplicationIdentifier: String? {
|
||||
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 items: [DockBarItem] = []
|
||||
@ -29,6 +43,35 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
|
||||
self.autoResize = autoResize
|
||||
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.didTerminateApplicationNotification, object: nil)
|
||||
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(softReloadItems), name: NSWorkspace.didActivateApplicationNotification, object: nil)
|
||||
@ -36,10 +79,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
|
||||
persistentAppIdentifiers = AppSettings.dockPersistentAppIds
|
||||
hardReloadItems()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
@objc func hardReloadItems() {
|
||||
applications = launchedApplications()
|
||||
@ -82,12 +122,17 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
|
||||
public func createAppButton(for app: DockItem) -> DockBarItem {
|
||||
let item = DockBarItem(app)
|
||||
item.isBordered = false
|
||||
item.tapClosure = { [weak self] in
|
||||
self?.switchToApp(app: app)
|
||||
}
|
||||
item.longTapClosure = { [weak self] in
|
||||
self?.handleHalfLongPress(item: app)
|
||||
}
|
||||
|
||||
item.setTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.switchToApp(app: app)
|
||||
} )
|
||||
)
|
||||
item.setLongTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.handleHalfLongPress(item: app)
|
||||
} )
|
||||
)
|
||||
item.killAppClosure = {[weak self] in
|
||||
self?.handleLongPress(item: app)
|
||||
}
|
||||
@ -212,8 +257,8 @@ class DockBarItem: CustomButtonTouchBarItem {
|
||||
super.init(identifier: .init(app.bundleIdentifier), title: "")
|
||||
dotView.wantsLayer = true
|
||||
|
||||
image = app.icon
|
||||
image?.size = NSSize(width: iconWidth, height: iconWidth)
|
||||
self.setImage(app.icon)
|
||||
self.getImage()?.size = NSSize(width: iconWidth, height: iconWidth)
|
||||
|
||||
killGestureRecognizer = LongPressGestureRecognizer(target: self, action: #selector(firePanGestureRecognizer))
|
||||
killGestureRecognizer.allowedTouchTypes = .direct
|
||||
@ -243,4 +288,8 @@ class DockBarItem: CustomButtonTouchBarItem {
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) {
|
||||
fatalError("init(from decoder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,21 +12,35 @@ import IOKit.ps
|
||||
class BatteryBarItem: CustomButtonTouchBarItem {
|
||||
private let batteryInfo = BatteryInfo()
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "battery"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier, title: " ")
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
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()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func refresh() {
|
||||
attributedTitle = batteryInfo.formattedInfo()
|
||||
setAttributedTitle(batteryInfo.formattedInfo())
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
27
MTMR/Widgets/BrightnessDownBarItem.swift
Normal file
27
MTMR/Widgets/BrightnessDownBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/BrightnessUpBarItem.swift
Normal file
27
MTMR/Widgets/BrightnessUpBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,12 +3,39 @@ import AVFoundation
|
||||
import Cocoa
|
||||
import CoreAudio
|
||||
|
||||
class BrightnessViewController: NSCustomTouchBarItem {
|
||||
class BrightnessViewController: CustomTouchBarItem {
|
||||
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) {
|
||||
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 {
|
||||
sliderItem = CustomSlider()
|
||||
} else {
|
||||
@ -22,14 +49,10 @@ class BrightnessViewController: NSCustomTouchBarItem {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
sliderItem.unbind(NSBindingName.value)
|
||||
}
|
||||
|
||||
48
MTMR/Widgets/CloseBarItem.swift
Normal file
48
MTMR/Widgets/CloseBarItem.swift
Normal 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)
|
||||
|
||||
} ))
|
||||
}
|
||||
|
||||
}
|
||||
@ -58,6 +58,18 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
"LTC": 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) {
|
||||
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||
@ -87,7 +99,51 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
|
||||
|
||||
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.qualityOfService = .utility
|
||||
activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in
|
||||
@ -97,10 +153,6 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
updateCurrency()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func updateCurrency() {
|
||||
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)
|
||||
}
|
||||
|
||||
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])
|
||||
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
|
||||
attributedTitle = newTitle
|
||||
setAttributedTitle(newTitle)
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
@ -1,26 +1,43 @@
|
||||
import Foundation
|
||||
|
||||
class DarkModeBarItem: CustomButtonTouchBarItem, Widget {
|
||||
static var name: String = "darkmode"
|
||||
static var identifier: String = "com.toxblh.mtmr.darkmode"
|
||||
|
||||
class DarkModeBarItem: CustomButtonTouchBarItem {
|
||||
private var timer: Timer!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "darkMode"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier, title: "")
|
||||
isBordered = false
|
||||
setWidth(value: 24)
|
||||
|
||||
tapClosure = { [weak self] in self?.DarkModeToggle() }
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||
|
||||
refresh()
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
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() {
|
||||
DarkMode.isEnabled = !DarkMode.isEnabled
|
||||
@ -28,7 +45,7 @@ class DarkModeBarItem: CustomButtonTouchBarItem, Widget {
|
||||
}
|
||||
|
||||
@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"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
MTMR/Widgets/DeleteBarItem.swift
Normal file
27
MTMR/Widgets/DeleteBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
27
MTMR/Widgets/DisplaySleepBarItem.swift
Normal file
27
MTMR/Widgets/DisplaySleepBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,21 +11,39 @@ import Foundation
|
||||
class DnDBarItem: CustomButtonTouchBarItem {
|
||||
private var timer: Timer!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "dnd"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier, title: "")
|
||||
isBordered = false
|
||||
setWidth(value: 32)
|
||||
|
||||
tapClosure = { [weak self] in self?.DnDToggle() }
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||
|
||||
refresh()
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
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() {
|
||||
DoNotDisturb.isEnabled = !DoNotDisturb.isEnabled
|
||||
@ -33,7 +51,7 @@ class DnDBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
|
||||
@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"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
MTMR/Widgets/EscapeBarItem.swift
Normal file
27
MTMR/Widgets/EscapeBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
50
MTMR/Widgets/ExitTouchbarBarItem.swift
Normal file
50
MTMR/Widgets/ExitTouchbarBarItem.swift
Normal 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()
|
||||
|
||||
} ))
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,50 +7,96 @@
|
||||
//
|
||||
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 items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
|
||||
var items: [CustomTouchBarItem] = []
|
||||
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var centerIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var centerItems: [NSTouchBarItem] = []
|
||||
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
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
|
||||
popoverItem = NSPopoverTouchBarItem(identifier: identifier)
|
||||
super.init(identifier: identifier)
|
||||
popoverTouchBar.delegate = self
|
||||
|
||||
self.setup(title: title)
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
popoverItem = NSPopoverTouchBarItem(identifier: identifier)
|
||||
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.jsonItems = try container.decode([BarItemDefinition].self, forKey: .items)
|
||||
let title = try container.decodeIfPresent(String.self, forKey: .title) ?? " "
|
||||
|
||||
self.setup(title: title)
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
deinit {}
|
||||
if getWidth() == 0.0 {
|
||||
setWidth(value: 60)
|
||||
}
|
||||
}
|
||||
|
||||
@objc override func showPopover(_: Any?) {
|
||||
itemDefinitions = [:]
|
||||
items = [:]
|
||||
leftIdentifiers = []
|
||||
centerItems = []
|
||||
rightIdentifiers = []
|
||||
|
||||
loadItemDefinitions(jsonItems: jsonItems)
|
||||
createItems()
|
||||
|
||||
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||
items[identifier]
|
||||
@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))
|
||||
scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
|
||||
|
||||
let centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
||||
let scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
|
||||
|
||||
TouchBarController.shared.touchBar.delegate = self
|
||||
TouchBarController.shared.touchBar.defaultItemIdentifiers = []
|
||||
TouchBarController.shared.touchBar.defaultItemIdentifiers = leftIdentifiers + [centerScrollArea] + rightIdentifiers
|
||||
TouchBarController.shared.touchBar.defaultItemIdentifiers = [basicViewIdentifier]
|
||||
|
||||
basicView = BasicView(identifier: basicViewIdentifier, items: leftItems + [scrollArea] + rightItems, swipeItems: swipeItems)
|
||||
basicView?.legacyGesturesEnabled = AppSettings.multitouchGestures
|
||||
|
||||
|
||||
if AppSettings.showControlStripState {
|
||||
presentSystemModal(TouchBarController.shared.touchBar, systemTrayItemIdentifier: .controlStripItem)
|
||||
@ -60,41 +106,23 @@ class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
|
||||
}
|
||||
|
||||
func touchBar(_: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
||||
if identifier == centerScrollArea {
|
||||
return scrollArea
|
||||
if identifier == basicViewIdentifier {
|
||||
return basicView
|
||||
}
|
||||
|
||||
guard let item = self.items[identifier],
|
||||
let definition = self.itemDefinitions[identifier],
|
||||
definition.align != .center else {
|
||||
return nil
|
||||
}
|
||||
return item
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
func getItems(newItems: [BarItemDefinition]) -> [CustomTouchBarItem] {
|
||||
var items: [CustomTouchBarItem] = []
|
||||
for item in newItems {
|
||||
if item.obj is SwipeItem {
|
||||
swipeItems.append(item.obj as! SwipeItem)
|
||||
} else {
|
||||
items.append(item.obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createItems() {
|
||||
for (identifier, definition) in itemDefinitions {
|
||||
items[identifier] = TouchBarController.shared.createItem(forIdentifier: identifier, definition: definition)
|
||||
}
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
MTMR/Widgets/IlluminationDownBarItem.swift
Normal file
27
MTMR/Widgets/IlluminationDownBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/IlluminationUpBarItem.swift
Normal file
27
MTMR/Widgets/IlluminationUpBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,22 +11,39 @@ import Cocoa
|
||||
class InputSourceBarItem: CustomButtonTouchBarItem {
|
||||
fileprivate var notificationCenter: CFNotificationCenter
|
||||
let buttonSize = NSSize(width: 21, height: 21)
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "inputsource"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
notificationCenter = CFNotificationCenterGetDistributedCenter()
|
||||
super.init(identifier: identifier, title: "⏳")
|
||||
|
||||
observeIputSourceChangedNotification()
|
||||
textInputSourceDidChange()
|
||||
tapClosure = { [weak self] in
|
||||
self?.switchInputSource()
|
||||
}
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
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 {
|
||||
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()))
|
||||
}
|
||||
@ -45,7 +62,7 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
|
||||
|
||||
if let iconImage = iconImage {
|
||||
iconImage.size = buttonSize
|
||||
image = iconImage
|
||||
self.setImage(iconImage)
|
||||
title = ""
|
||||
} else {
|
||||
title = currentSource.name
|
||||
|
||||
@ -33,17 +33,36 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
||||
private var songTitle: String?
|
||||
private var timer: Timer?
|
||||
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) {
|
||||
self.interval = interval
|
||||
self.disableMarquee = disableMarquee
|
||||
|
||||
super.init(identifier: identifier, title: "⏳")
|
||||
isBordered = false
|
||||
|
||||
tapClosure = { [weak self] in self?.playPause() }
|
||||
longTapClosure = { [weak self] in self?.nextTrack() }
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
self.setTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.playPause()
|
||||
} )
|
||||
)
|
||||
self.setLongTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.nextTrack()
|
||||
} )
|
||||
)
|
||||
|
||||
refreshAndSchedule()
|
||||
}
|
||||
@ -60,11 +79,23 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
||||
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.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() {
|
||||
for ident in playerBundleIdentifiers {
|
||||
print("checking \(ident)")
|
||||
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
|
||||
print("musicPlayer \(musicPlayer)")
|
||||
if musicPlayer.isRunning {
|
||||
print("musicPlayer.isRunning \(musicPlayer.isRunning)")
|
||||
if ident == .Spotify {
|
||||
let mp = (musicPlayer as SpotifyApplication)
|
||||
mp.playpause!()
|
||||
@ -194,6 +225,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
||||
for ident in playerBundleIdentifiers {
|
||||
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
|
||||
if musicPlayer.isRunning {
|
||||
print("musicPlayer \(musicPlayer)")
|
||||
var tempTitle = ""
|
||||
if ident == .Spotify {
|
||||
tempTitle = (musicPlayer as SpotifyApplication).title
|
||||
@ -268,7 +300,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
||||
let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: ident.rawValue) {
|
||||
let image = NSWorkspace.shared.icon(forFile: appPath)
|
||||
image.size = self.iconSize
|
||||
self.image = image
|
||||
self.setImage(image)
|
||||
iconUpdated = true
|
||||
}
|
||||
break
|
||||
@ -278,7 +310,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if !iconUpdated {
|
||||
self.image = nil
|
||||
self.setImage(nil)
|
||||
}
|
||||
|
||||
if !titleUpdated {
|
||||
|
||||
27
MTMR/Widgets/MuteBarItem.swift
Normal file
27
MTMR/Widgets/MuteBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,12 +8,17 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
class NetworkBarItem: CustomButtonTouchBarItem, Widget {
|
||||
static var name: String = "network"
|
||||
static var identifier: String = "com.toxblh.mtmr.network"
|
||||
|
||||
class NetworkBarItem: CustomButtonTouchBarItem {
|
||||
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) {
|
||||
self.flip = flip
|
||||
super.init(identifier: identifier, title: " ")
|
||||
@ -23,6 +28,14 @@ class NetworkBarItem: CustomButtonTouchBarItem, Widget {
|
||||
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.flip = try container.decodeIfPresent(Bool.self, forKey: .flip) ?? false
|
||||
|
||||
try super.init(from: decoder)
|
||||
startMonitoringProcess()
|
||||
}
|
||||
|
||||
func startMonitoringProcess() {
|
||||
var pipe: Pipe
|
||||
@ -144,6 +157,6 @@ class NetworkBarItem: CustomButtonTouchBarItem, Widget {
|
||||
}
|
||||
|
||||
|
||||
self.attributedTitle = newTitle
|
||||
self.setAttributedTitle(newTitle)
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/NextBarItem.swift
Normal file
27
MTMR/Widgets/NextBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,10 @@ import Foundation
|
||||
class NightShiftBarItem: CustomButtonTouchBarItem {
|
||||
private let nsclient = CBBlueLightClient()
|
||||
private var timer: Timer!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "nightShift"
|
||||
}
|
||||
|
||||
private var blueLightStatus: Status {
|
||||
var status: Status = Status()
|
||||
@ -28,19 +32,33 @@ class NightShiftBarItem: CustomButtonTouchBarItem {
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier, title: "")
|
||||
isBordered = false
|
||||
setWidth(value: 28)
|
||||
|
||||
tapClosure = { [weak self] in self?.nightShiftAction() }
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||
|
||||
refresh()
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
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() {
|
||||
setNightShift(state: !isNightShiftEnabled)
|
||||
@ -48,6 +66,6 @@ class NightShiftBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
|
||||
@objc func refresh() {
|
||||
image = isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff")
|
||||
self.setImage(isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff"))
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/PlayBarItem.swift
Normal file
27
MTMR/Widgets/PlayBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,25 +8,14 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
class PomodoroBarItem: CustomButtonTouchBarItem, Widget {
|
||||
static let identifier = "com.toxblh.mtmr.pomodoro."
|
||||
static let name = "pomodoro"
|
||||
static let decoder: ParametersDecoder = { decoder in
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case workTime
|
||||
case restTime
|
||||
}
|
||||
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime)
|
||||
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime)
|
||||
|
||||
return (
|
||||
item: .pomodoro(workTime: workTime ?? 1500.00, restTime: restTime ?? 300),
|
||||
action: .none,
|
||||
longAction: .none,
|
||||
parameters: [:]
|
||||
)
|
||||
class PomodoroBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "pomodoro"
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case workTime
|
||||
case restTime
|
||||
}
|
||||
|
||||
private enum TimeTypes {
|
||||
@ -45,18 +34,44 @@ class PomodoroBarItem: CustomButtonTouchBarItem, Widget {
|
||||
private var timeLeftString: String {
|
||||
return String(format: "%.2i:%.2i", timeLeft / 60, timeLeft % 60)
|
||||
}
|
||||
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, workTime: TimeInterval, restTime: TimeInterval) {
|
||||
self.workTime = workTime
|
||||
self.restTime = restTime
|
||||
super.init(identifier: identifier, title: defaultTitle)
|
||||
tapClosure = { [weak self] in self?.startStopWork() }
|
||||
longTapClosure = { [weak self] in self?.startStopRest() }
|
||||
|
||||
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.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 {
|
||||
timer?.cancel()
|
||||
|
||||
27
MTMR/Widgets/PreviousBarItem.swift
Normal file
27
MTMR/Widgets/PreviousBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/SleepBarItem.swift
Normal file
27
MTMR/Widgets/SleepBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,40 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
|
||||
private let dateFormatter = DateFormatter()
|
||||
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) {
|
||||
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
|
||||
if let locale = locale {
|
||||
dateFormatter.locale = Locale(identifier: locale)
|
||||
@ -12,14 +45,10 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
|
||||
if let abbr = timeZone {
|
||||
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) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
|
||||
func setupTimer() {
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
|
||||
}
|
||||
|
||||
@objc func updateTime() {
|
||||
|
||||
27
MTMR/Widgets/VolumeDownBarItem.swift
Normal file
27
MTMR/Widgets/VolumeDownBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/VolumeUpBarItem.swift
Normal file
27
MTMR/Widgets/VolumeUpBarItem.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,12 +3,33 @@ import AVFoundation
|
||||
import Cocoa
|
||||
import CoreAudio
|
||||
|
||||
class VolumeViewController: NSCustomTouchBarItem {
|
||||
class VolumeViewController: CustomTouchBarItem {
|
||||
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) {
|
||||
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(
|
||||
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
|
||||
mScope: kAudioDevicePropertyScopeOutput,
|
||||
@ -36,6 +57,7 @@ class VolumeViewController: NSCustomTouchBarItem {
|
||||
self.sliderItem.floatValue = self.getInputGain() * 100
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
|
||||
@ -21,10 +21,21 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||
private var iconsSource: Dictionary<String, String>
|
||||
|
||||
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") {
|
||||
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||
activity.interval = interval
|
||||
self.activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||
self.activity.interval = interval
|
||||
self.units = units
|
||||
self.api_key = api_key
|
||||
|
||||
@ -43,7 +54,44 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||
}
|
||||
|
||||
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()
|
||||
if status == .restricted || status == .denied {
|
||||
print("User permission not given")
|
||||
@ -69,10 +117,6 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||
manager.startUpdatingLocation()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func updateWeather() {
|
||||
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)")!)
|
||||
|
||||
@ -19,13 +19,38 @@ class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate
|
||||
private var location: CLLocation!
|
||||
private var prevLocation: CLLocation!
|
||||
private var manager: CLLocationManager!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "weather"
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case refreshInterval
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
|
||||
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||
activity.interval = interval
|
||||
|
||||
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()
|
||||
if status == .restricted || status == .denied {
|
||||
print("User permission not given")
|
||||
@ -50,11 +75,12 @@ class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate
|
||||
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
|
||||
manager.startUpdatingLocation()
|
||||
|
||||
tapClosure = tapClosure ?? defaultTapAction
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
|
||||
self.setTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.defaultTapAction()
|
||||
} )
|
||||
)
|
||||
}
|
||||
|
||||
@objc func updateWeather() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user