1
0
mirror of https://github.com/Toxblh/MTMR.git synced 2026-01-11 17:38:38 +00:00
This commit is contained in:
Fedor Zaytsev 2020-05-01 08:06:57 +00:00 committed by GitHub
commit fe499b2b44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 1698 additions and 1044 deletions

View File

@ -69,6 +69,23 @@
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */; };
B0F54A7A2295AC7D00B4C509 /* DarkModeBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */; };
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */; };
BA6EEC332457B48F009AC2D3 /* EventActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC322457B48F009AC2D3 /* EventActions.swift */; };
BA6EEC372457B727009AC2D3 /* EscapeBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC362457B727009AC2D3 /* EscapeBarItem.swift */; };
BA6EEC392457B839009AC2D3 /* DeleteBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC382457B839009AC2D3 /* DeleteBarItem.swift */; };
BA6EEC3B2457BA7D009AC2D3 /* BrightnessUpBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC3A2457BA7D009AC2D3 /* BrightnessUpBarItem.swift */; };
BA6EEC3D2457BD2E009AC2D3 /* BrightnessDownBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC3C2457BD2E009AC2D3 /* BrightnessDownBarItem.swift */; };
BA6EEC3F2457BF79009AC2D3 /* IlluminationUpBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC3E2457BF79009AC2D3 /* IlluminationUpBarItem.swift */; };
BA6EEC412457BFCE009AC2D3 /* IlluminationDownBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC402457BFCE009AC2D3 /* IlluminationDownBarItem.swift */; };
BA6EEC432457C010009AC2D3 /* VolumeDownBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC422457C010009AC2D3 /* VolumeDownBarItem.swift */; };
BA6EEC452457C072009AC2D3 /* VolumeUpBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC442457C072009AC2D3 /* VolumeUpBarItem.swift */; };
BA6EEC472457C0C7009AC2D3 /* MuteBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC462457C0C7009AC2D3 /* MuteBarItem.swift */; };
BA6EEC492457C0FA009AC2D3 /* PreviousBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC482457C0FA009AC2D3 /* PreviousBarItem.swift */; };
BA6EEC4B2457C12E009AC2D3 /* PlayBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC4A2457C12E009AC2D3 /* PlayBarItem.swift */; };
BA6EEC4D2457C164009AC2D3 /* NextBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC4C2457C164009AC2D3 /* NextBarItem.swift */; };
BA6EEC4F2457C1AB009AC2D3 /* SleepBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC4E2457C1AB009AC2D3 /* SleepBarItem.swift */; };
BA6EEC512457C217009AC2D3 /* DisplaySleepBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC502457C217009AC2D3 /* DisplaySleepBarItem.swift */; };
BA6EEC53245AE228009AC2D3 /* ExitTouchbarBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC52245AE227009AC2D3 /* ExitTouchbarBarItem.swift */; };
BA6EEC55245B8D75009AC2D3 /* CloseBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6EEC54245B8D75009AC2D3 /* CloseBarItem.swift */; };
BAF5AB5724317B4300B04904 /* BasicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAF5AB5624317B4300B04904 /* BasicView.swift */; };
BAF5AB5924317CAF00B04904 /* SwipeItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAF5AB5824317CAF00B04904 /* SwipeItem.swift */; };
/* End PBXBuildFile section */
@ -161,6 +178,23 @@
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarkModeBarItem.swift; sourceTree = "<group>"; };
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TouchBarSupport.m; sourceTree = "<group>"; };
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarSupport.h; sourceTree = "<group>"; };
BA6EEC322457B48F009AC2D3 /* EventActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventActions.swift; sourceTree = "<group>"; };
BA6EEC362457B727009AC2D3 /* EscapeBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EscapeBarItem.swift; sourceTree = "<group>"; };
BA6EEC382457B839009AC2D3 /* DeleteBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteBarItem.swift; sourceTree = "<group>"; };
BA6EEC3A2457BA7D009AC2D3 /* BrightnessUpBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrightnessUpBarItem.swift; sourceTree = "<group>"; };
BA6EEC3C2457BD2E009AC2D3 /* BrightnessDownBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrightnessDownBarItem.swift; sourceTree = "<group>"; };
BA6EEC3E2457BF79009AC2D3 /* IlluminationUpBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IlluminationUpBarItem.swift; sourceTree = "<group>"; };
BA6EEC402457BFCE009AC2D3 /* IlluminationDownBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IlluminationDownBarItem.swift; sourceTree = "<group>"; };
BA6EEC422457C010009AC2D3 /* VolumeDownBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeDownBarItem.swift; sourceTree = "<group>"; };
BA6EEC442457C072009AC2D3 /* VolumeUpBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeUpBarItem.swift; sourceTree = "<group>"; };
BA6EEC462457C0C7009AC2D3 /* MuteBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteBarItem.swift; sourceTree = "<group>"; };
BA6EEC482457C0FA009AC2D3 /* PreviousBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviousBarItem.swift; sourceTree = "<group>"; };
BA6EEC4A2457C12E009AC2D3 /* PlayBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayBarItem.swift; sourceTree = "<group>"; };
BA6EEC4C2457C164009AC2D3 /* NextBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextBarItem.swift; sourceTree = "<group>"; };
BA6EEC4E2457C1AB009AC2D3 /* SleepBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SleepBarItem.swift; sourceTree = "<group>"; };
BA6EEC502457C217009AC2D3 /* DisplaySleepBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplaySleepBarItem.swift; sourceTree = "<group>"; };
BA6EEC52245AE227009AC2D3 /* ExitTouchbarBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExitTouchbarBarItem.swift; sourceTree = "<group>"; };
BA6EEC54245B8D75009AC2D3 /* CloseBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseBarItem.swift; sourceTree = "<group>"; };
BAF5AB5624317B4300B04904 /* BasicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicView.swift; sourceTree = "<group>"; };
BAF5AB5824317CAF00B04904 /* SwipeItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeItem.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -249,6 +283,7 @@
B05600D22083E9BB00EB218D /* CustomSlider.swift */,
36A778BD20A6C27100B38714 /* GeneralExtensions.swift */,
B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */,
BA6EEC322457B48F009AC2D3 /* EventActions.swift */,
);
path = MTMR;
sourceTree = "<group>";
@ -307,21 +342,37 @@
children = (
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */,
BA6EEC3C2457BD2E009AC2D3 /* BrightnessDownBarItem.swift */,
BA6EEC3A2457BA7D009AC2D3 /* BrightnessUpBarItem.swift */,
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */,
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */,
BA6EEC382457B839009AC2D3 /* DeleteBarItem.swift */,
BA6EEC502457C217009AC2D3 /* DisplaySleepBarItem.swift */,
B081732B213739FE005D4908 /* DnDBarItem.swift */,
BA6EEC362457B727009AC2D3 /* EscapeBarItem.swift */,
60669B4220AD8FA80074E817 /* GroupBarItem.swift */,
BA6EEC402457BFCE009AC2D3 /* IlluminationDownBarItem.swift */,
BA6EEC3E2457BF79009AC2D3 /* IlluminationUpBarItem.swift */,
60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */,
60C44AFC20A373A100C0EC91 /* MusicBarItem.swift */,
BA6EEC462457C0C7009AC2D3 /* MuteBarItem.swift */,
B0846A742220C968000288A7 /* NetworkBarItem.swift */,
BA6EEC4C2457C164009AC2D3 /* NextBarItem.swift */,
B08173262135F02B005D4908 /* NightShiftBarItem.swift */,
BA6EEC4A2457C12E009AC2D3 /* PlayBarItem.swift */,
B08126EE217BD0B900A98970 /* PomodoroBarItem.swift */,
BA6EEC482457C0FA009AC2D3 /* PreviousBarItem.swift */,
BA6EEC4E2457C1AB009AC2D3 /* SleepBarItem.swift */,
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
BA6EEC422457C010009AC2D3 /* VolumeDownBarItem.swift */,
BA6EEC442457C072009AC2D3 /* VolumeUpBarItem.swift */,
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */,
4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */,
B08126F0217BE19000A98970 /* WidgetProtocol.swift */,
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */,
4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */,
BA6EEC52245AE227009AC2D3 /* ExitTouchbarBarItem.swift */,
BA6EEC54245B8D75009AC2D3 /* CloseBarItem.swift */,
);
path = Widgets;
sourceTree = "<group>";
@ -469,28 +520,37 @@
buildActionMask = 2147483647;
files = (
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */,
BA6EEC4F2457C1AB009AC2D3 /* SleepBarItem.swift in Sources */,
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */,
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */,
BA6EEC512457C217009AC2D3 /* DisplaySleepBarItem.swift in Sources */,
BAF5AB5724317B4300B04904 /* BasicView.swift in Sources */,
B0846A752220C968000288A7 /* NetworkBarItem.swift in Sources */,
BA6EEC55245B8D75009AC2D3 /* CloseBarItem.swift in Sources */,
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */,
B08126F1217BE19000A98970 /* WidgetProtocol.swift in Sources */,
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */,
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */,
BA6EEC4B2457C12E009AC2D3 /* PlayBarItem.swift in Sources */,
BAF5AB5924317CAF00B04904 /* SwipeItem.swift in Sources */,
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
B0F54A7A2295AC7D00B4C509 /* DarkModeBarItem.swift in Sources */,
B08126EF217BD0B900A98970 /* PomodoroBarItem.swift in Sources */,
BA6EEC412457BFCE009AC2D3 /* IlluminationDownBarItem.swift in Sources */,
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */,
60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */,
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
BA6EEC492457C0FA009AC2D3 /* PreviousBarItem.swift in Sources */,
60173D3E20C0031B002C305F /* LaunchAtLoginController.m in Sources */,
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
BA6EEC3B2457BA7D009AC2D3 /* BrightnessUpBarItem.swift in Sources */,
4CDC6E5022FCA93F0069ADD4 /* ShellScriptTouchBarItem.swift in Sources */,
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
BA6EEC332457B48F009AC2D3 /* EventActions.swift in Sources */,
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */,
4CFF5E5C22E623DD00BFB1EE /* YandexWeatherBarItem.swift in Sources */,
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
BA6EEC372457B727009AC2D3 /* EscapeBarItem.swift in Sources */,
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */,
36FEF872235A1CFC00A0ABCE /* AppSettings.swift in Sources */,
@ -499,11 +559,19 @@
60669B4320AD8FA80074E817 /* GroupBarItem.swift in Sources */,
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */,
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */,
BA6EEC472457C0C7009AC2D3 /* MuteBarItem.swift in Sources */,
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */,
BA6EEC53245AE228009AC2D3 /* ExitTouchbarBarItem.swift in Sources */,
607EEA4D2087A8DA009DA5F0 /* CurrencyBarItem.swift in Sources */,
4CC9FEDC22FDEA65001512EB /* AMR_ANSIEscapeHelper.m in Sources */,
BA6EEC4D2457C164009AC2D3 /* NextBarItem.swift in Sources */,
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */,
BA6EEC392457B839009AC2D3 /* DeleteBarItem.swift in Sources */,
BA6EEC432457C010009AC2D3 /* VolumeDownBarItem.swift in Sources */,
BA6EEC452457C072009AC2D3 /* VolumeUpBarItem.swift in Sources */,
BA6EEC3D2457BD2E009AC2D3 /* BrightnessDownBarItem.swift in Sources */,
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */,
BA6EEC3F2457BF79009AC2D3 /* IlluminationUpBarItem.swift in Sources */,
B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -6,10 +6,42 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
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)\"")

View File

@ -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

View File

@ -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 {
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
}
var image: NSImage? {
didSet {
func setImage(_ image: NSImage?) {
self.image = image
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
View File

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

View File

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

View File

@ -8,545 +8,85 @@ extension Data {
}
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
}
static let types: [CustomTouchBarItem.Type] = [
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
// custom buttons
CustomButtonTouchBarItem.self,
AppleScriptTouchBarItem.self,
ShellScriptTouchBarItem.self,
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]
)
// 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,
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: [:]
) },
// 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,
"brightnessUp": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
return (
item: .staticButton(title: ""),
action: .keyPress(keycode: 144),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"brightnessDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
return (
item: .staticButton(title: ""),
action: .keyPress(keycode: 145),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"illuminationUp": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_up"))
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_UP),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"illuminationDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_down"))
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_DOWN),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"volumeDown": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeDownTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"volumeUp": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeUpTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"mute": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarAudioOutputMuteTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_MUTE),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"previous": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarRewindTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"play": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarPlayPauseTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_PLAY),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"next": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarFastForwardTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_NEXT),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"sleep": { _ in (
item: .staticButton(title: "☕️"),
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]),
longAction: .none,
parameters: [:]
) },
"displaySleep": { _ in (
item: .staticButton(title: "☕️"),
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]),
longAction: .none,
parameters: [:]
) },
// custom-custom objects!
SwipeItem.self,
GroupBarItem.self,
ExitTouchbarBarItem.self,
CloseBarItem.self,
]
static let sharedInstance = SupportedTypesHolder()
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)
let objType = try container.decode(String.self, forKey: .objtype)
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)
}
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 }

View File

@ -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 == ""
}

View File

@ -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()

View File

@ -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,16 +73,18 @@ 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?.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 reloadPreset(path: String?) {
if path != nil {
lastPresetPath = path!
}
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH-mm-ss"
let time = dateFormatter.string(from: Date())
for item in jsonItems {
let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString)
let identifier = NSTouchBarItem.Identifier(identifierString)
itemDefinitions[identifier] = item
if item.align == .left {
leftIdentifiers.append(identifier)
}
if item.align == .right {
rightIdentifiers.append(identifier)
}
if item.align == .center {
centerIdentifiers.append(identifier)
}
}
let items = lastPresetPath.fileData?.barItemDefinitions() ?? [BarItemDefinition(obj: CustomButtonTouchBarItem(title: "bad preset"))]
createAndUpdatePreset(newItems: items)
}
func createItems() {
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
}
}

View File

@ -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?
@ -20,6 +20,20 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
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)
@ -37,9 +80,6 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
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
item.setTapAction(
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
self?.switchToApp(app: app)
}
item.longTapClosure = { [weak self] in
} )
)
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")
}
}

View File

@ -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: " ")
batteryInfo.start { [weak self] in
self?.refresh()
}
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() {
batteryInfo.start { [weak self] in
self?.refresh()
}
refresh()
}
func refresh() {
attributedTitle = batteryInfo.formattedInfo()
setAttributedTitle(batteryInfo.formattedInfo())
}
deinit {

View File

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

View File

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

View File

@ -3,12 +3,39 @@ import AVFoundation
import Cocoa
import 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)
}

View File

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

View File

@ -59,6 +59,18 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
"ETH": 2,
]
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")
activity.interval = interval
@ -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 {

View File

@ -1,34 +1,51 @@
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
refresh()
}
@objc func refresh() {
image = DarkMode.isEnabled ? #imageLiteral(resourceName: "dark-mode-on") : #imageLiteral(resourceName: "dark-mode-off")
self.setImage(DarkMode.isEnabled ? #imageLiteral(resourceName: "dark-mode-on") : #imageLiteral(resourceName: "dark-mode-off"))
}
}

View File

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

View File

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

View File

@ -11,29 +11,47 @@ import Foundation
class DnDBarItem: CustomButtonTouchBarItem {
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
refresh()
}
@objc func refresh() {
image = DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off")
self.setImage(DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off"))
}
}

View File

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

View File

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

View File

@ -7,50 +7,96 @@
//
import Cocoa
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")
}
deinit {}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
popoverItem = NSPopoverTouchBarItem(identifier: identifier)
@objc override func showPopover(_: Any?) {
itemDefinitions = [:]
items = [:]
leftIdentifiers = []
centerItems = []
rightIdentifiers = []
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) ?? " "
loadItemDefinitions(jsonItems: jsonItems)
createItems()
self.setup(title: title)
}
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
items[identifier]
func setup(title: String) {
let button = NSButton(title: title, target: self,
action: #selector(GroupBarItem.showPopover(_:)))
// Use the built-in gesture recognizer for tap and hold to open our popover's NSTouchBar.
let gestureRecognizer = popoverItem.makeStandardActivatePopoverGestureRecognizer()
button.addGestureRecognizer(gestureRecognizer)
popoverItem.collapsedRepresentation = button
view = button
if getWidth() == 0.0 {
setWidth(value: 60)
}
}
@objc func showPopover(_: Any?) {
items = getItems(newItems: jsonItems)
let leftItems = items.compactMap({ (item) -> CustomTouchBarItem? in
item.align == .left || item.align == .center ? item : nil
})
let centerItems = items.compactMap({ (item) -> CustomTouchBarItem? in
item.align == .center && false ? item : nil
})
let rightItems = items.compactMap({ (item) -> CustomTouchBarItem? in
item.align == .right ? item : nil
})
centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
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
}
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)
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)
}
if item.align == .right {
rightIdentifiers.append(identifier)
}
if item.align == .center {
centerIdentifiers.append(identifier)
}
return items
}
}
func createItems() {
for (identifier, definition) in itemDefinitions {
items[identifier] = TouchBarController.shared.createItem(forIdentifier: identifier, definition: definition)
}
}
}

View File

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

View File

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

View File

@ -12,21 +12,38 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
fileprivate var notificationCenter: CFNotificationCenter
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

View File

@ -34,16 +34,35 @@ class MusicBarItem: CustomButtonTouchBarItem {
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()
}
@ -61,10 +80,22 @@ class MusicBarItem: CustomButtonTouchBarItem {
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 {

View File

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

View File

@ -8,12 +8,17 @@
import Foundation
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: " ")
@ -24,6 +29,14 @@ class NetworkBarItem: CustomButtonTouchBarItem, Widget {
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
var outputHandle: FileHandle
@ -144,6 +157,6 @@ class NetworkBarItem: CustomButtonTouchBarItem, Widget {
}
self.attributedTitle = newTitle
self.setAttributedTitle(newTitle)
}
}

View File

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

View File

@ -12,6 +12,10 @@ class NightShiftBarItem: CustomButtonTouchBarItem {
private let nsclient = CBBlueLightClient()
private var timer: Timer!
override class var typeIdentifier: String {
return "nightShift"
}
private var blueLightStatus: Status {
var status: Status = Status()
nsclient.getBlueLightStatus(&status)
@ -28,26 +32,40 @@ 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)
refresh()
}
@objc func refresh() {
image = isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff")
self.setImage(isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff"))
}
}

View File

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

View File

@ -8,25 +8,14 @@
import Cocoa
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
class PomodoroBarItem: CustomButtonTouchBarItem {
override class var typeIdentifier: String {
return "pomodoro"
}
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: [:]
)
private enum CodingKeys: String, CodingKey {
case workTime
case restTime
}
private enum TimeTypes {
@ -46,18 +35,44 @@ class PomodoroBarItem: CustomButtonTouchBarItem, Widget {
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()
timer = nil

View File

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

View File

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

View File

@ -4,17 +4,32 @@ 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) {
dateFormatter.dateFormat = formatTemplate
if let locale = locale {
dateFormatter.locale = Locale(identifier: locale)
}
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
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()
}
@ -22,6 +37,20 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
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)
}
if let abbr = timeZone {
dateFormatter.timeZone = TimeZone(abbreviation: abbr)
}
}
func setupTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
@objc func updateTime() {
title = dateFormatter.string(from: Date())
}

View File

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

View File

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

View File

@ -3,12 +3,33 @@ import AVFoundation
import Cocoa
import 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,
@ -37,6 +58,7 @@ class VolumeViewController: NSCustomTouchBarItem {
}
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

View File

@ -22,9 +22,20 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
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
@ -44,6 +55,43 @@ 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)")!)

View File

@ -20,12 +20,37 @@ class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate
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() {