mirror of
https://github.com/Toxblh/MTMR.git
synced 2026-01-10 17:08:39 +00:00
Merge ff6d07f2d6 into 52758f947d
This commit is contained in:
commit
fe499b2b44
@ -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;
|
||||
|
||||
@ -5,11 +5,43 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
private let interval: TimeInterval
|
||||
private var forceHideConstraint: NSLayoutConstraint!
|
||||
private let alternativeImages: [String: SourceProtocol]
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case source
|
||||
case alternativeImages
|
||||
case refreshInterval
|
||||
}
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "appleScriptTitledButton"
|
||||
}
|
||||
|
||||
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, alternativeImages: [String: SourceProtocol]) {
|
||||
self.interval = interval
|
||||
self.alternativeImages = alternativeImages
|
||||
super.init(identifier: identifier, title: "⏳")
|
||||
|
||||
initScripts(source: source)
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let source = try container.decode(Source.self, forKey: .source)
|
||||
self.interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||
self.alternativeImages = try container.decodeIfPresent([String: Source].self, forKey: .alternativeImages) ?? [:]
|
||||
|
||||
print("AppleScriptTouchBarItem.init(from decoder)")
|
||||
try super.init(from: decoder)
|
||||
self.title = "⏳"
|
||||
|
||||
initScripts(source: source)
|
||||
}
|
||||
|
||||
func initScripts(source: SourceProtocol) {
|
||||
forceHideConstraint = view.widthAnchor.constraint(equalToConstant: 0)
|
||||
title = "scheduled"
|
||||
DispatchQueue.appleScriptQueue.async {
|
||||
@ -38,10 +70,6 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func refreshAndSchedule() {
|
||||
#if DEBUG
|
||||
print("refresh happened (interval \(interval)), self \(identifier.rawValue))")
|
||||
@ -62,7 +90,7 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
func updateIcon(iconLabel: String) {
|
||||
if alternativeImages[iconLabel] != nil {
|
||||
DispatchQueue.main.async {
|
||||
self.image = self.alternativeImages[iconLabel]!.image
|
||||
self.setImage(self.alternativeImages[iconLabel]!.image)
|
||||
}
|
||||
} else {
|
||||
print("Cannot find icon with label \"\(iconLabel)\"")
|
||||
|
||||
@ -15,6 +15,7 @@ class BasicView: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
||||
var threefingers: NSPanGestureRecognizer!
|
||||
var fourfingers: NSPanGestureRecognizer!
|
||||
var swipeItems: [SwipeItem] = []
|
||||
var items: [NSTouchBarItem] = []
|
||||
var prevPositions: [Int: CGFloat] = [2:0, 3:0, 4:0]
|
||||
|
||||
// legacy gesture positions
|
||||
@ -25,6 +26,7 @@ class BasicView: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
||||
init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem], swipeItems: [SwipeItem]) {
|
||||
super.init(identifier: identifier)
|
||||
self.swipeItems = swipeItems
|
||||
self.items = items
|
||||
let views = items.compactMap { $0.view }
|
||||
let stackView = NSStackView(views: views)
|
||||
stackView.spacing = 1
|
||||
|
||||
@ -8,24 +8,130 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
||||
var tapClosure: (() -> Void)?
|
||||
var longTapClosure: (() -> Void)? {
|
||||
didSet {
|
||||
longClick.isEnabled = longTapClosure != nil
|
||||
enum Align: String, Decodable {
|
||||
case left
|
||||
case center
|
||||
case right
|
||||
}
|
||||
|
||||
// CustomTouchBarItem is a base class for all widgets
|
||||
// This class provides some basic parameter parsing (width, align)
|
||||
// To implement a new class:
|
||||
// 1. Derive your class from CustomTouchBarItem (for static) or CustomButtonTouchBarItem (for buttons)
|
||||
// 2. Override class var typeIdentifier with your object identificator
|
||||
// 3. Override init(from decoder: Decoder) and read all custom json params you need
|
||||
// 4. Don't forget to call super.init(identifier: CustomTouchBarItem.createIdentifier(type)) in the init() function
|
||||
// 5. Add your new class to BarItemDefinition.types in ItemParsing.swift
|
||||
//
|
||||
// Good example is PomodoroBarItem
|
||||
//
|
||||
// If you want to inherid from some other NS class (NSSlider or NSPopoverTouchBarItem or other) then
|
||||
// look into GroupBarItem and BrightnessViewController
|
||||
|
||||
class CustomTouchBarItem: NSCustomTouchBarItem, Decodable {
|
||||
var align: Align
|
||||
private var width: NSLayoutConstraint?
|
||||
|
||||
class var typeIdentifier: String {
|
||||
return "NOTDEFINED"
|
||||
}
|
||||
|
||||
func setWidth(value: CGFloat) {
|
||||
guard value > 0 else {
|
||||
return
|
||||
}
|
||||
|
||||
if let width = self.width {
|
||||
width.isActive = false
|
||||
}
|
||||
self.width = view.widthAnchor.constraint(equalToConstant: value)
|
||||
self.width!.isActive = true
|
||||
}
|
||||
|
||||
func getWidth() -> CGFloat {
|
||||
return width?.constant ?? 0.0
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case width
|
||||
case align
|
||||
// TODO move bordered and background from custom button class
|
||||
//case bordered
|
||||
//case background
|
||||
//case title
|
||||
}
|
||||
|
||||
override init(identifier: NSTouchBarItem.Identifier) {
|
||||
self.align = .center
|
||||
|
||||
// setting width here wouldn't make any affect
|
||||
super.init(identifier: identifier)
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let type = try container.decode(String.self, forKey: .type)
|
||||
self.align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center
|
||||
|
||||
super.init(identifier: CustomTouchBarItem.createIdentifier(type))
|
||||
|
||||
if let width = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
|
||||
self.setWidth(value: width)
|
||||
}
|
||||
}
|
||||
|
||||
static func identifierBase(_ type: String) -> String {
|
||||
return "com.toxblh.mtmr." + type
|
||||
}
|
||||
|
||||
static func createIdentifier(_ type: String) -> NSTouchBarItem.Identifier {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "HH-mm-ss"
|
||||
let time = dateFormatter.string(from: Date())
|
||||
let identifierString = CustomTouchBarItem.identifierBase(type).appending(time + "--" + UUID().uuidString)
|
||||
return NSTouchBarItem.Identifier(identifierString)
|
||||
}
|
||||
}
|
||||
|
||||
class CustomButtonTouchBarItem: CustomTouchBarItem, NSGestureRecognizerDelegate {
|
||||
private var tapAction: EventAction?
|
||||
private var longTapAction: EventAction?
|
||||
var finishViewConfiguration: ()->() = {}
|
||||
override class var typeIdentifier: String {
|
||||
return "staticButton"
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case title
|
||||
case bordered
|
||||
case background
|
||||
case image
|
||||
case action
|
||||
case longAction
|
||||
}
|
||||
|
||||
private var button: NSButton!
|
||||
private var singleClick: HapticClickGestureRecognizer!
|
||||
private var longClick: LongPressGestureRecognizer!
|
||||
private var attributedTitle: NSAttributedString
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, title: String) {
|
||||
attributedTitle = title.defaultTouchbarAttributedString
|
||||
|
||||
super.init(identifier: identifier)
|
||||
|
||||
initButton(title: title, imageSource: nil)
|
||||
}
|
||||
|
||||
func initButton(title: String, imageSource: Source?) {
|
||||
button = CustomHeightButton(title: title, target: nil, action: nil)
|
||||
self.setImage(imageSource?.image)
|
||||
|
||||
longClick = LongPressGestureRecognizer(target: self, action: #selector(handleGestureLong))
|
||||
longClick.isEnabled = false
|
||||
@ -33,6 +139,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
longClick.delegate = self
|
||||
|
||||
singleClick = HapticClickGestureRecognizer(target: self, action: #selector(handleGestureSingle))
|
||||
singleClick.isEnabled = false
|
||||
singleClick.allowedTouchTypes = .direct
|
||||
singleClick.delegate = self
|
||||
|
||||
@ -40,6 +147,45 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
button.attributedTitle = attributedTitle
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
attributedTitle = "".defaultTouchbarAttributedString
|
||||
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let title = try container.decodeIfPresent(String.self, forKey: .title) ?? ""
|
||||
|
||||
try super.init(from: decoder)
|
||||
|
||||
if let borderedFlag = try container.decodeIfPresent(Bool.self, forKey: .bordered) {
|
||||
self.isBordered = borderedFlag
|
||||
}
|
||||
|
||||
if let bgColor = try container.decodeIfPresent(String.self, forKey: .background)?.hexColor {
|
||||
self.backgroundColor = bgColor
|
||||
}
|
||||
|
||||
|
||||
let imageSource = try container.decodeIfPresent(Source.self, forKey: .image)
|
||||
initButton(title: title, imageSource: imageSource)
|
||||
|
||||
self.setTapAction(try? SingleTapEventAction(from: decoder))
|
||||
self.setLongTapAction(try? LongTapEventAction(from: decoder))
|
||||
}
|
||||
|
||||
// From for static buttons
|
||||
convenience init(title: String) {
|
||||
self.init(identifier: CustomTouchBarItem.createIdentifier(CustomButtonTouchBarItem.typeIdentifier), title: title)
|
||||
}
|
||||
|
||||
func setTapAction(_ action: EventAction?) {
|
||||
self.tapAction = action
|
||||
self.singleClick?.isEnabled = action != nil
|
||||
}
|
||||
|
||||
func setLongTapAction(_ action: EventAction?) {
|
||||
self.longTapAction = action
|
||||
self.longClick?.isEnabled = action != nil
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
@ -58,27 +204,35 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
|
||||
var title: String {
|
||||
get {
|
||||
return attributedTitle.string
|
||||
return getAttributedTitle().string
|
||||
}
|
||||
set {
|
||||
attributedTitle = newValue.defaultTouchbarAttributedString
|
||||
setAttributedTitle(newValue.defaultTouchbarAttributedString)
|
||||
}
|
||||
}
|
||||
|
||||
var attributedTitle: NSAttributedString {
|
||||
didSet {
|
||||
button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
|
||||
button?.attributedTitle = attributedTitle
|
||||
}
|
||||
|
||||
func getAttributedTitle() -> NSAttributedString {
|
||||
return attributedTitle
|
||||
}
|
||||
|
||||
func setAttributedTitle(_ attributedTitle: NSAttributedString) {
|
||||
self.attributedTitle = attributedTitle
|
||||
button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
|
||||
button?.attributedTitle = attributedTitle
|
||||
}
|
||||
|
||||
private var image: NSImage?
|
||||
|
||||
func getImage() -> NSImage? {
|
||||
return image
|
||||
}
|
||||
|
||||
func setImage(_ image: NSImage?) {
|
||||
self.image = image
|
||||
button.image = image
|
||||
}
|
||||
|
||||
var image: NSImage? {
|
||||
didSet {
|
||||
button.image = image
|
||||
}
|
||||
}
|
||||
|
||||
private func reinstallButton() {
|
||||
func reinstallButton() {
|
||||
let title = button.attributedTitle
|
||||
let image = button.image
|
||||
let cell = CustomButtonCell(parentItem: self)
|
||||
@ -116,7 +270,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
@objc func handleGestureSingle(gr: NSClickGestureRecognizer) {
|
||||
switch gr.state {
|
||||
case .ended:
|
||||
tapClosure?()
|
||||
self.tapAction?.closure(self)
|
||||
break
|
||||
default:
|
||||
break
|
||||
@ -126,7 +280,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
@objc func handleGestureLong(gr: NSPressGestureRecognizer) {
|
||||
switch gr.state {
|
||||
case .possible: // tiny hack because we're calling action manually
|
||||
(self.longTapClosure ?? self.tapClosure)?()
|
||||
(self.longTapAction?.closure ?? self.tapAction?.closure)?(self)
|
||||
break
|
||||
default:
|
||||
break
|
||||
@ -156,7 +310,7 @@ class CustomButtonCell: NSButtonCell {
|
||||
if flag {
|
||||
setAttributedTitle(attributedTitle, withColor: .lightGray)
|
||||
} else if let parentItem = self.parentItem {
|
||||
attributedTitle = parentItem.attributedTitle
|
||||
attributedTitle = parentItem.getAttributedTitle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
184
MTMR/EventActions.swift
Normal file
184
MTMR/EventActions.swift
Normal file
@ -0,0 +1,184 @@
|
||||
//
|
||||
// EventActions.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class EventAction {
|
||||
var closure: ((_ caller: CustomButtonTouchBarItem) -> Void)
|
||||
|
||||
func setHidKeyClosure(keycode: Int32) -> EventAction {
|
||||
closure = { (_ caller: CustomButtonTouchBarItem) in
|
||||
HIDPostAuxKey(keycode)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func setKeyPressClosure(keycode: Int) -> EventAction {
|
||||
closure = { (_ caller: CustomButtonTouchBarItem) in
|
||||
GenericKeyPress(keyCode: CGKeyCode(keycode)).send()
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func setAppleScriptClosure(appleScript: NSAppleScript) -> EventAction {
|
||||
closure = { (_ caller: CustomButtonTouchBarItem) in
|
||||
DispatchQueue.appleScriptQueue.async {
|
||||
var error: NSDictionary?
|
||||
appleScript.executeAndReturnError(&error)
|
||||
if let error = error {
|
||||
print("error \(error) when handling apple script ")
|
||||
}
|
||||
}
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func setShellScriptClosure(executable: String, parameters: [String]) -> EventAction {
|
||||
closure = { (_ caller: CustomButtonTouchBarItem) in
|
||||
let task = Process()
|
||||
task.launchPath = executable
|
||||
task.arguments = parameters
|
||||
task.launch()
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func setOpenUrlClosure(url: String) -> EventAction {
|
||||
closure = { (_ caller: CustomButtonTouchBarItem) in
|
||||
if let url = URL(string: url), NSWorkspace.shared.open(url) {
|
||||
#if DEBUG
|
||||
print("URL was successfully opened")
|
||||
#endif
|
||||
} else {
|
||||
print("error", url)
|
||||
}
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
init() {
|
||||
self.closure = { (_ caller: CustomButtonTouchBarItem) in }
|
||||
}
|
||||
|
||||
init(_ closure: @escaping (_ caller: CustomButtonTouchBarItem) -> Void) {
|
||||
self.closure = closure
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class LongTapEventAction: EventAction, Decodable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case longAction
|
||||
case longKeycode
|
||||
case longActionAppleScript
|
||||
case longExecutablePath
|
||||
case longShellArguments
|
||||
case longUrl
|
||||
}
|
||||
|
||||
private enum LongActionTypeRaw: String, Decodable {
|
||||
case hidKey
|
||||
case keyPress
|
||||
case appleScript
|
||||
case shellScript
|
||||
case openUrl
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
super.init()
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction)
|
||||
|
||||
switch type {
|
||||
case .some(.hidKey):
|
||||
let keycode = try container.decode(Int32.self, forKey: .longKeycode)
|
||||
|
||||
_ = setHidKeyClosure(keycode: keycode)
|
||||
case .some(.keyPress):
|
||||
let keycode = try container.decode(Int.self, forKey: .longKeycode)
|
||||
|
||||
_ = setKeyPressClosure(keycode: keycode)
|
||||
case .some(.appleScript):
|
||||
let source = try container.decode(Source.self, forKey: .longActionAppleScript)
|
||||
|
||||
guard let appleScript = source.appleScript else {
|
||||
print("cannot create apple script")
|
||||
return
|
||||
}
|
||||
|
||||
_ = setAppleScriptClosure(appleScript: appleScript)
|
||||
case .some(.shellScript):
|
||||
let executable = try container.decode(String.self, forKey: .longExecutablePath)
|
||||
let parameters = try container.decodeIfPresent([String].self, forKey: .longShellArguments) ?? []
|
||||
|
||||
_ = setShellScriptClosure(executable: executable, parameters: parameters)
|
||||
case .some(.openUrl):
|
||||
let url = try container.decode(String.self, forKey: .longUrl)
|
||||
|
||||
_ = setOpenUrlClosure(url: url)
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SingleTapEventAction: EventAction, Decodable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case action
|
||||
case keycode
|
||||
case actionAppleScript
|
||||
case executablePath
|
||||
case shellArguments
|
||||
case url
|
||||
}
|
||||
|
||||
private enum ActionTypeRaw: String, Decodable {
|
||||
case hidKey
|
||||
case keyPress
|
||||
case appleScript
|
||||
case shellScript
|
||||
case openUrl
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
super.init()
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
|
||||
|
||||
switch type {
|
||||
case .some(.hidKey):
|
||||
let keycode = try container.decode(Int32.self, forKey: .keycode)
|
||||
|
||||
_ = setHidKeyClosure(keycode: keycode)
|
||||
case .some(.keyPress):
|
||||
let keycode = try container.decode(Int.self, forKey: .keycode)
|
||||
|
||||
_ = setKeyPressClosure(keycode: keycode)
|
||||
case .some(.appleScript):
|
||||
let source = try container.decode(Source.self, forKey: .actionAppleScript)
|
||||
|
||||
guard let appleScript = source.appleScript else {
|
||||
print("cannot create apple script")
|
||||
return
|
||||
}
|
||||
|
||||
_ = setAppleScriptClosure(appleScript: appleScript)
|
||||
case .some(.shellScript):
|
||||
let executable = try container.decode(String.self, forKey: .executablePath)
|
||||
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
|
||||
|
||||
_ = setShellScriptClosure(executable: executable, parameters: parameters)
|
||||
case .some(.openUrl):
|
||||
let url = try container.decode(String.self, forKey: .url)
|
||||
|
||||
_ = setOpenUrlClosure(url: url)
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.25</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>402</string>
|
||||
<string>624</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@ -3,550 +3,90 @@ import Foundation
|
||||
|
||||
extension Data {
|
||||
func barItemDefinitions() -> [BarItemDefinition]? {
|
||||
return try? JSONDecoder().decode([BarItemDefinition].self, from: utf8string!.stripComments().data(using: .utf8)!)
|
||||
return try? JSONDecoder().decode([BarItemDefinition].self, from: utf8string!.stripComments().data(using: .utf8)!)
|
||||
}
|
||||
}
|
||||
|
||||
struct BarItemDefinition: Decodable {
|
||||
let type: ItemType
|
||||
let action: ActionType
|
||||
let longAction: LongActionType
|
||||
let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]
|
||||
let obj: CustomTouchBarItem
|
||||
|
||||
enum ParsingErrors: Error {
|
||||
case noMatchingType(description: String)
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case objtype = "type"
|
||||
}
|
||||
|
||||
init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]) {
|
||||
self.type = type
|
||||
self.action = action
|
||||
self.longAction = longAction
|
||||
self.additionalParameters = additionalParameters
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try container.decode(String.self, forKey: .type)
|
||||
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type)
|
||||
var additionalParameters = try GeneralParameters(from: decoder).parameters
|
||||
|
||||
if let result = try? parametersDecoder(decoder),
|
||||
case let (itemType, action, longAction, parameters) = result {
|
||||
parameters.forEach { additionalParameters[$0] = $1 }
|
||||
self.init(type: itemType, action: action, longAction: longAction, additionalParameters: additionalParameters)
|
||||
} else {
|
||||
self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, additionalParameters: additionalParameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typealias ParametersDecoder = (Decoder) throws -> (
|
||||
item: ItemType,
|
||||
action: ActionType,
|
||||
longAction: LongActionType,
|
||||
parameters: [GeneralParameters.CodingKeys: GeneralParameter]
|
||||
)
|
||||
|
||||
class SupportedTypesHolder {
|
||||
private var supportedTypes: [String: ParametersDecoder] = [
|
||||
"escape": { _ in (
|
||||
item: .staticButton(title: "esc"),
|
||||
action: .keyPress(keycode: 53),
|
||||
longAction: .none,
|
||||
parameters: [.align: .align(.left)]
|
||||
) },
|
||||
|
||||
"delete": { _ in (
|
||||
item: .staticButton(title: "del"),
|
||||
action: .keyPress(keycode: 117),
|
||||
longAction: .none,
|
||||
parameters: [:]
|
||||
) },
|
||||
|
||||
"brightnessUp": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
|
||||
return (
|
||||
item: .staticButton(title: ""),
|
||||
action: .keyPress(keycode: 144),
|
||||
longAction: .none,
|
||||
parameters: [.image: imageParameter]
|
||||
)
|
||||
},
|
||||
|
||||
"brightnessDown": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
|
||||
return (
|
||||
item: .staticButton(title: ""),
|
||||
action: .keyPress(keycode: 145),
|
||||
longAction: .none,
|
||||
parameters: [.image: imageParameter]
|
||||
)
|
||||
},
|
||||
|
||||
"illuminationUp": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_up"))
|
||||
return (
|
||||
item: .staticButton(title: ""),
|
||||
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_UP),
|
||||
longAction: .none,
|
||||
parameters: [.image: imageParameter]
|
||||
)
|
||||
},
|
||||
|
||||
"illuminationDown": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_down"))
|
||||
return (
|
||||
item: .staticButton(title: ""),
|
||||
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_DOWN),
|
||||
longAction: .none,
|
||||
parameters: [.image: imageParameter]
|
||||
)
|
||||
},
|
||||
|
||||
"volumeDown": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeDownTemplateName)!)
|
||||
return (
|
||||
item: .staticButton(title: ""),
|
||||
action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN),
|
||||
longAction: .none,
|
||||
parameters: [.image: imageParameter]
|
||||
)
|
||||
},
|
||||
|
||||
"volumeUp": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeUpTemplateName)!)
|
||||
return (
|
||||
item: .staticButton(title: ""),
|
||||
action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP),
|
||||
longAction: .none,
|
||||
parameters: [.image: imageParameter]
|
||||
)
|
||||
},
|
||||
|
||||
"mute": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarAudioOutputMuteTemplateName)!)
|
||||
return (
|
||||
item: .staticButton(title: ""),
|
||||
action: .hidKey(keycode: NX_KEYTYPE_MUTE),
|
||||
longAction: .none,
|
||||
parameters: [.image: imageParameter]
|
||||
)
|
||||
},
|
||||
|
||||
"previous": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarRewindTemplateName)!)
|
||||
return (
|
||||
item: .staticButton(title: ""),
|
||||
action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS),
|
||||
longAction: .none,
|
||||
parameters: [.image: imageParameter]
|
||||
)
|
||||
},
|
||||
|
||||
"play": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarPlayPauseTemplateName)!)
|
||||
return (
|
||||
item: .staticButton(title: ""),
|
||||
action: .hidKey(keycode: NX_KEYTYPE_PLAY),
|
||||
longAction: .none,
|
||||
parameters: [.image: imageParameter]
|
||||
)
|
||||
},
|
||||
|
||||
"next": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarFastForwardTemplateName)!)
|
||||
return (
|
||||
item: .staticButton(title: ""),
|
||||
action: .hidKey(keycode: NX_KEYTYPE_NEXT),
|
||||
longAction: .none,
|
||||
parameters: [.image: imageParameter]
|
||||
)
|
||||
},
|
||||
|
||||
"sleep": { _ in (
|
||||
item: .staticButton(title: "☕️"),
|
||||
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]),
|
||||
longAction: .none,
|
||||
parameters: [:]
|
||||
) },
|
||||
|
||||
"displaySleep": { _ in (
|
||||
item: .staticButton(title: "☕️"),
|
||||
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]),
|
||||
longAction: .none,
|
||||
parameters: [:]
|
||||
) },
|
||||
|
||||
|
||||
static let types: [CustomTouchBarItem.Type] = [
|
||||
|
||||
// custom buttons
|
||||
CustomButtonTouchBarItem.self,
|
||||
AppleScriptTouchBarItem.self,
|
||||
ShellScriptTouchBarItem.self,
|
||||
|
||||
|
||||
// basic widget buttons
|
||||
EscapeBarItem.self,
|
||||
DeleteBarItem.self,
|
||||
BrightnessUpBarItem.self,
|
||||
BrightnessDownBarItem.self,
|
||||
IlluminationUpBarItem.self,
|
||||
IlluminationDownBarItem.self,
|
||||
VolumeUpBarItem.self,
|
||||
VolumeDownBarItem.self,
|
||||
MuteBarItem.self,
|
||||
PreviousBarItem.self,
|
||||
PlayBarItem.self,
|
||||
NextBarItem.self,
|
||||
SleepBarItem.self,
|
||||
DisplaySleepBarItem.self,
|
||||
|
||||
|
||||
// custom widgets
|
||||
TimeTouchBarItem.self,
|
||||
BatteryBarItem.self,
|
||||
AppScrubberTouchBarItem.self,
|
||||
VolumeViewController.self,
|
||||
BrightnessViewController.self,
|
||||
WeatherBarItem.self,
|
||||
YandexWeatherBarItem.self,
|
||||
CurrencyBarItem.self,
|
||||
InputSourceBarItem.self,
|
||||
MusicBarItem.self,
|
||||
NightShiftBarItem.self,
|
||||
DnDBarItem.self,
|
||||
PomodoroBarItem.self,
|
||||
NetworkBarItem.self,
|
||||
DarkModeBarItem.self,
|
||||
|
||||
|
||||
// custom-custom objects!
|
||||
SwipeItem.self,
|
||||
GroupBarItem.self,
|
||||
ExitTouchbarBarItem.self,
|
||||
CloseBarItem.self,
|
||||
]
|
||||
|
||||
static let sharedInstance = SupportedTypesHolder()
|
||||
|
||||
func lookup(by type: String) -> ParametersDecoder {
|
||||
return supportedTypes[type] ?? { decoder in (
|
||||
item: try ItemType(from: decoder),
|
||||
action: try ActionType(from: decoder),
|
||||
longAction: try LongActionType(from: decoder),
|
||||
parameters: [:]
|
||||
) }
|
||||
}
|
||||
|
||||
func register(typename: String, decoder: @escaping ParametersDecoder) {
|
||||
supportedTypes[typename] = decoder
|
||||
}
|
||||
|
||||
func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) {
|
||||
register(typename: typename) { _ in
|
||||
(
|
||||
item: item,
|
||||
action,
|
||||
longAction,
|
||||
parameters: [:]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ItemType: Decodable {
|
||||
case staticButton(title: String)
|
||||
case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double, alternativeImages: [String: SourceProtocol])
|
||||
case shellScriptTitledButton(source: SourceProtocol, refreshInterval: Double)
|
||||
case timeButton(formatTemplate: String, timeZone: String?, locale: String?)
|
||||
case battery
|
||||
case dock(autoResize: Bool, filter: String?)
|
||||
case volume
|
||||
case brightness(refreshInterval: Double)
|
||||
case weather(interval: Double, units: String, api_key: String, icon_type: String)
|
||||
case yandexWeather(interval: Double)
|
||||
case currency(interval: Double, from: String, to: String, full: Bool)
|
||||
case inputsource
|
||||
case music(interval: Double, disableMarquee: Bool)
|
||||
case group(items: [BarItemDefinition])
|
||||
case nightShift
|
||||
case dnd
|
||||
case pomodoro(workTime: Double, restTime: Double)
|
||||
case network(flip: Bool)
|
||||
case darkMode
|
||||
case swipe(direction: String, fingers: Int, minOffset: Float, sourceApple: SourceProtocol?, sourceBash: SourceProtocol?)
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case title
|
||||
case source
|
||||
case refreshInterval
|
||||
case from
|
||||
case to
|
||||
case full
|
||||
case timeZone
|
||||
case units
|
||||
case api_key
|
||||
case icon_type
|
||||
case formatTemplate
|
||||
case locale
|
||||
case image
|
||||
case url
|
||||
case longUrl
|
||||
case items
|
||||
case workTime
|
||||
case restTime
|
||||
case flip
|
||||
case autoResize
|
||||
case filter
|
||||
case disableMarquee
|
||||
case alternativeImages
|
||||
case sourceApple
|
||||
case sourceBash
|
||||
case direction
|
||||
case fingers
|
||||
case minOffset
|
||||
}
|
||||
|
||||
enum ItemTypeRaw: String, Decodable {
|
||||
case staticButton
|
||||
case appleScriptTitledButton
|
||||
case shellScriptTitledButton
|
||||
case timeButton
|
||||
case battery
|
||||
case dock
|
||||
case volume
|
||||
case brightness
|
||||
case weather
|
||||
case yandexWeather
|
||||
case currency
|
||||
case inputsource
|
||||
case music
|
||||
case group
|
||||
case nightShift
|
||||
case dnd
|
||||
case pomodoro
|
||||
case network
|
||||
case darkMode
|
||||
case swipe
|
||||
init(obj: CustomTouchBarItem) {
|
||||
self.obj = obj
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try container.decode(ItemTypeRaw.self, forKey: .type)
|
||||
switch type {
|
||||
case .appleScriptTitledButton:
|
||||
let source = try container.decode(Source.self, forKey: .source)
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||
let alternativeImages = try container.decodeIfPresent([String: Source].self, forKey: .alternativeImages) ?? [:]
|
||||
self = .appleScriptTitledButton(source: source, refreshInterval: interval, alternativeImages: alternativeImages)
|
||||
|
||||
case .shellScriptTitledButton:
|
||||
let source = try container.decode(Source.self, forKey: .source)
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||
self = .shellScriptTitledButton(source: source, refreshInterval: interval)
|
||||
|
||||
case .staticButton:
|
||||
let title = try container.decode(String.self, forKey: .title)
|
||||
self = .staticButton(title: title)
|
||||
|
||||
case .timeButton:
|
||||
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
|
||||
let timeZone = try container.decodeIfPresent(String.self, forKey: .timeZone) ?? nil
|
||||
let locale = try container.decodeIfPresent(String.self, forKey: .locale) ?? nil
|
||||
self = .timeButton(formatTemplate: template, timeZone: timeZone, locale: locale)
|
||||
|
||||
case .battery:
|
||||
self = .battery
|
||||
|
||||
case .dock:
|
||||
let autoResize = try container.decodeIfPresent(Bool.self, forKey: .autoResize) ?? false
|
||||
let filterRegexString = try container.decodeIfPresent(String.self, forKey: .filter)
|
||||
self = .dock(autoResize: autoResize, filter: filterRegexString)
|
||||
|
||||
case .volume:
|
||||
self = .volume
|
||||
|
||||
case .brightness:
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
|
||||
self = .brightness(refreshInterval: interval)
|
||||
|
||||
case .weather:
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||
let units = try container.decodeIfPresent(String.self, forKey: .units) ?? "metric"
|
||||
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
|
||||
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
|
||||
self = .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type)
|
||||
|
||||
case .yandexWeather:
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||
self = .yandexWeather(interval: interval)
|
||||
|
||||
case .currency:
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
|
||||
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
|
||||
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
|
||||
let full = try container.decodeIfPresent(Bool.self, forKey: .full) ?? false
|
||||
self = .currency(interval: interval, from: from, to: to, full: full)
|
||||
|
||||
case .inputsource:
|
||||
self = .inputsource
|
||||
|
||||
case .music:
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 5.0
|
||||
let disableMarquee = try container.decodeIfPresent(Bool.self, forKey: .disableMarquee) ?? false
|
||||
self = .music(interval: interval, disableMarquee: disableMarquee)
|
||||
|
||||
case .group:
|
||||
let items = try container.decode([BarItemDefinition].self, forKey: .items)
|
||||
self = .group(items: items)
|
||||
|
||||
case .nightShift:
|
||||
self = .nightShift
|
||||
|
||||
case .dnd:
|
||||
self = .dnd
|
||||
|
||||
case .pomodoro:
|
||||
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime) ?? 1500.0
|
||||
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime) ?? 600.0
|
||||
self = .pomodoro(workTime: workTime, restTime: restTime)
|
||||
|
||||
case .network:
|
||||
let flip = try container.decodeIfPresent(Bool.self, forKey: .flip) ?? false
|
||||
self = .network(flip: flip)
|
||||
|
||||
case .darkMode:
|
||||
self = .darkMode
|
||||
|
||||
case .swipe:
|
||||
let sourceApple = try container.decodeIfPresent(Source.self, forKey: .sourceApple)
|
||||
let sourceBash = try container.decodeIfPresent(Source.self, forKey: .sourceBash)
|
||||
let direction = try container.decode(String.self, forKey: .direction)
|
||||
let fingers = try container.decode(Int.self, forKey: .fingers)
|
||||
let minOffset = try container.decodeIfPresent(Float.self, forKey: .minOffset) ?? 0.0
|
||||
self = .swipe(direction: direction, fingers: fingers, minOffset: minOffset, sourceApple: sourceApple, sourceBash: sourceBash)
|
||||
let objType = try container.decode(String.self, forKey: .objtype)
|
||||
|
||||
|
||||
for obj in BarItemDefinition.types {
|
||||
if obj.typeIdentifier == objType {
|
||||
self.obj = try obj.init(from: decoder)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ActionType: Decodable {
|
||||
case none
|
||||
case hidKey(keycode: Int32)
|
||||
case keyPress(keycode: Int)
|
||||
case appleScript(source: SourceProtocol)
|
||||
case shellScript(executable: String, parameters: [String])
|
||||
case custom(closure: () -> Void)
|
||||
case openUrl(url: String)
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case action
|
||||
case keycode
|
||||
case actionAppleScript
|
||||
case executablePath
|
||||
case shellArguments
|
||||
case url
|
||||
}
|
||||
|
||||
private enum ActionTypeRaw: String, Decodable {
|
||||
case hidKey
|
||||
case keyPress
|
||||
case appleScript
|
||||
case shellScript
|
||||
case openUrl
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
|
||||
|
||||
switch type {
|
||||
case .some(.hidKey):
|
||||
let keycode = try container.decode(Int32.self, forKey: .keycode)
|
||||
self = .hidKey(keycode: keycode)
|
||||
|
||||
case .some(.keyPress):
|
||||
let keycode = try container.decode(Int.self, forKey: .keycode)
|
||||
self = .keyPress(keycode: keycode)
|
||||
|
||||
case .some(.appleScript):
|
||||
let source = try container.decode(Source.self, forKey: .actionAppleScript)
|
||||
self = .appleScript(source: source)
|
||||
|
||||
case .some(.shellScript):
|
||||
let executable = try container.decode(String.self, forKey: .executablePath)
|
||||
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
|
||||
self = .shellScript(executable: executable, parameters: parameters)
|
||||
|
||||
case .some(.openUrl):
|
||||
let url = try container.decode(String.self, forKey: .url)
|
||||
self = .openUrl(url: url)
|
||||
|
||||
case .none:
|
||||
self = .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum LongActionType: Decodable {
|
||||
case none
|
||||
case hidKey(keycode: Int32)
|
||||
case keyPress(keycode: Int)
|
||||
case appleScript(source: SourceProtocol)
|
||||
case shellScript(executable: String, parameters: [String])
|
||||
case custom(closure: () -> Void)
|
||||
case openUrl(url: String)
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case longAction
|
||||
case longKeycode
|
||||
case longActionAppleScript
|
||||
case longExecutablePath
|
||||
case longShellArguments
|
||||
case longUrl
|
||||
}
|
||||
|
||||
private enum LongActionTypeRaw: String, Decodable {
|
||||
case hidKey
|
||||
case keyPress
|
||||
case appleScript
|
||||
case shellScript
|
||||
case openUrl
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let longType = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction)
|
||||
|
||||
switch longType {
|
||||
case .some(.hidKey):
|
||||
let keycode = try container.decode(Int32.self, forKey: .longKeycode)
|
||||
self = .hidKey(keycode: keycode)
|
||||
|
||||
case .some(.keyPress):
|
||||
let keycode = try container.decode(Int.self, forKey: .longKeycode)
|
||||
self = .keyPress(keycode: keycode)
|
||||
|
||||
case .some(.appleScript):
|
||||
let source = try container.decode(Source.self, forKey: .longActionAppleScript)
|
||||
self = .appleScript(source: source)
|
||||
|
||||
case .some(.shellScript):
|
||||
let executable = try container.decode(String.self, forKey: .longExecutablePath)
|
||||
let parameters = try container.decodeIfPresent([String].self, forKey: .longShellArguments) ?? []
|
||||
self = .shellScript(executable: executable, parameters: parameters)
|
||||
|
||||
case .some(.openUrl):
|
||||
let longUrl = try container.decode(String.self, forKey: .longUrl)
|
||||
self = .openUrl(url: longUrl)
|
||||
|
||||
case .none:
|
||||
self = .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum GeneralParameter {
|
||||
case width(_: CGFloat)
|
||||
case image(source: SourceProtocol)
|
||||
case align(_: Align)
|
||||
case bordered(_: Bool)
|
||||
case background(_: NSColor)
|
||||
case title(_: String)
|
||||
}
|
||||
|
||||
struct GeneralParameters: Decodable {
|
||||
let parameters: [GeneralParameters.CodingKeys: GeneralParameter]
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case width
|
||||
case image
|
||||
case align
|
||||
case bordered
|
||||
case background
|
||||
case title
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
var result: [GeneralParameters.CodingKeys: GeneralParameter] = [:]
|
||||
|
||||
if let value = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
|
||||
result[.width] = .width(value)
|
||||
}
|
||||
|
||||
if let imageSource = try container.decodeIfPresent(Source.self, forKey: .image) {
|
||||
result[.image] = .image(source: imageSource)
|
||||
}
|
||||
|
||||
let align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center
|
||||
result[.align] = .align(align)
|
||||
|
||||
if let borderedFlag = try container.decodeIfPresent(Bool.self, forKey: .bordered) {
|
||||
result[.bordered] = .bordered(borderedFlag)
|
||||
}
|
||||
|
||||
if let backgroundColor = try container.decodeIfPresent(String.self, forKey: .background)?.hexColor {
|
||||
result[.background] = .background(backgroundColor)
|
||||
}
|
||||
|
||||
if let title = try container.decodeIfPresent(String.self, forKey: .title) {
|
||||
result[.title] = .title(title)
|
||||
}
|
||||
|
||||
parameters = result
|
||||
|
||||
|
||||
print("Cannot find preset mapping for \(objType)")
|
||||
throw ParsingErrors.noMatchingType(description: "Cannot find preset mapping for \(objType)")
|
||||
}
|
||||
}
|
||||
|
||||
@ -646,12 +186,6 @@ extension Data {
|
||||
}
|
||||
}
|
||||
|
||||
enum Align: String, Decodable {
|
||||
case left
|
||||
case center
|
||||
case right
|
||||
}
|
||||
|
||||
extension URL {
|
||||
var appleScript: NSAppleScript? {
|
||||
guard FileManager.default.fileExists(atPath: path) else { return nil }
|
||||
|
||||
@ -12,11 +12,40 @@ class ShellScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
private let source: String
|
||||
private var forceHideConstraint: NSLayoutConstraint!
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case source
|
||||
case refreshInterval
|
||||
}
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "shellScriptTitledButton"
|
||||
}
|
||||
|
||||
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval) {
|
||||
self.interval = interval
|
||||
self.source = source.string ?? "echo No \"source\""
|
||||
super.init(identifier: identifier, title: "⏳")
|
||||
|
||||
initScripts()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let source = try container.decode(Source.self, forKey: .source)
|
||||
self.source = source.string ?? "echo No \"source\""
|
||||
self.interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||
|
||||
try super.init(from: decoder)
|
||||
self.title = "⏳"
|
||||
|
||||
initScripts()
|
||||
}
|
||||
|
||||
func initScripts() {
|
||||
forceHideConstraint = view.widthAnchor.constraint(equalToConstant: 0)
|
||||
|
||||
DispatchQueue.shellScriptQueue.async {
|
||||
@ -24,10 +53,6 @@ class ShellScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func refreshAndSchedule() {
|
||||
// Execute script and get result
|
||||
let scriptResult = execute(source)
|
||||
@ -45,7 +70,7 @@ class ShellScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
if (newBackgoundColor != self?.backgroundColor) { // performance optimization because of reinstallButton
|
||||
self?.backgroundColor = newBackgoundColor
|
||||
}
|
||||
self?.attributedTitle = title
|
||||
self?.setAttributedTitle(title)
|
||||
self?.forceHideConstraint.isActive = scriptResult == ""
|
||||
}
|
||||
|
||||
|
||||
@ -9,12 +9,25 @@
|
||||
import Foundation
|
||||
import Foundation
|
||||
|
||||
class SwipeItem: NSCustomTouchBarItem {
|
||||
class SwipeItem: CustomTouchBarItem {
|
||||
private var scriptApple: NSAppleScript?
|
||||
private var scriptBash: String?
|
||||
private var direction: String
|
||||
private var fingers: Int
|
||||
private var minOffset: Float
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case sourceApple
|
||||
case sourceBash
|
||||
case direction
|
||||
case fingers
|
||||
case minOffset
|
||||
}
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "swipe"
|
||||
}
|
||||
|
||||
init?(identifier: NSTouchBarItem.Identifier, direction: String, fingers: Int, minOffset: Float, sourceApple: SourceProtocol?, sourceBash: SourceProtocol?) {
|
||||
self.direction = direction
|
||||
self.fingers = fingers
|
||||
@ -28,6 +41,19 @@ class SwipeItem: NSCustomTouchBarItem {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.scriptApple = try container.decodeIfPresent(Source.self, forKey: .sourceApple)?.appleScript
|
||||
self.scriptBash = try container.decodeIfPresent(Source.self, forKey: .sourceBash)?.string
|
||||
self.direction = try container.decode(String.self, forKey: .direction)
|
||||
self.fingers = try container.decode(Int.self, forKey: .fingers)
|
||||
self.minOffset = try container.decodeIfPresent(Float.self, forKey: .minOffset) ?? 0.0
|
||||
|
||||
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
func processEvent(offset: CGFloat, fingers: Int) {
|
||||
if direction == "right" && Float(offset) > self.minOffset && self.fingers == fingers {
|
||||
self.execute()
|
||||
|
||||
@ -16,52 +16,6 @@ struct ExactItem {
|
||||
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
|
||||
let standardConfigPath = appSupportDirectory.appending("/items.json")
|
||||
|
||||
extension ItemType {
|
||||
var identifierBase: String {
|
||||
switch self {
|
||||
case .staticButton(title: _):
|
||||
return "com.toxblh.mtmr.staticButton."
|
||||
case .appleScriptTitledButton(source: _):
|
||||
return "com.toxblh.mtmr.appleScriptButton."
|
||||
case .shellScriptTitledButton(source: _):
|
||||
return "com.toxblh.mtmr.shellScriptButton."
|
||||
case .timeButton(formatTemplate: _, timeZone: _, locale: _):
|
||||
return "com.toxblh.mtmr.timeButton."
|
||||
case .battery:
|
||||
return "com.toxblh.mtmr.battery."
|
||||
case .dock(autoResize: _, filter: _):
|
||||
return "com.toxblh.mtmr.dock"
|
||||
case .volume:
|
||||
return "com.toxblh.mtmr.volume"
|
||||
case .brightness(refreshInterval: _):
|
||||
return "com.toxblh.mtmr.brightness"
|
||||
case .weather(interval: _, units: _, api_key: _, icon_type: _):
|
||||
return "com.toxblh.mtmr.weather"
|
||||
case .yandexWeather(interval: _):
|
||||
return "com.toxblh.mtmr.yandexWeather"
|
||||
case .currency(interval: _, from: _, to: _, full: _):
|
||||
return "com.toxblh.mtmr.currency"
|
||||
case .inputsource:
|
||||
return "com.toxblh.mtmr.inputsource."
|
||||
case .music(interval: _):
|
||||
return "com.toxblh.mtmr.music."
|
||||
case .group(items: _):
|
||||
return "com.toxblh.mtmr.groupBar."
|
||||
case .nightShift:
|
||||
return "com.toxblh.mtmr.nightShift."
|
||||
case .dnd:
|
||||
return "com.toxblh.mtmr.dnd."
|
||||
case .pomodoro(interval: _):
|
||||
return PomodoroBarItem.identifier
|
||||
case .network(flip: _):
|
||||
return NetworkBarItem.identifier
|
||||
case .darkMode:
|
||||
return DarkModeBarItem.identifier
|
||||
case .swipe(direction: _, fingers: _, minOffset: _, sourceApple: _, sourceBash: _):
|
||||
return "com.toxblh.mtmr.swipe."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NSTouchBarItem.Identifier {
|
||||
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
|
||||
@ -73,12 +27,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
var touchBar: NSTouchBar!
|
||||
|
||||
fileprivate var lastPresetPath = ""
|
||||
var jsonItems: [BarItemDefinition] = []
|
||||
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
||||
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
|
||||
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var centerIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var items: [CustomTouchBarItem] = []
|
||||
var basicViewIdentifier = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollView.".appending(UUID().uuidString))
|
||||
var basicView: BasicView?
|
||||
var swipeItems: [SwipeItem] = []
|
||||
@ -90,14 +39,6 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in self?.dismissTouchBar() }), longAction: .none)
|
||||
|
||||
SupportedTypesHolder.sharedInstance.register(typename: "close") { _ in
|
||||
(item: .staticButton(title: ""), action: .custom(closure: { [weak self] in
|
||||
guard let `self` = self else { return }
|
||||
self.reloadPreset(path: self.lastPresetPath)
|
||||
}), longAction: .none, parameters: [.width: .width(30), .image: .image(source: (NSImage(named: NSImage.stopProgressFreestandingTemplateName))!)])
|
||||
}
|
||||
|
||||
blacklistAppIdentifiers = AppSettings.blacklistedAppIds
|
||||
|
||||
@ -108,21 +49,23 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
reloadStandardConfig()
|
||||
}
|
||||
|
||||
func createAndUpdatePreset(newJsonItems: [BarItemDefinition]) {
|
||||
func createAndUpdatePreset(newItems: [BarItemDefinition]) {
|
||||
if let oldBar = self.touchBar {
|
||||
minimizeSystemModal(oldBar)
|
||||
}
|
||||
touchBar = NSTouchBar()
|
||||
jsonItems = newJsonItems
|
||||
itemDefinitions = [:]
|
||||
items = [:]
|
||||
(items, swipeItems) = getItems(newItems: newItems)
|
||||
|
||||
loadItemDefinitions(jsonItems: jsonItems)
|
||||
createItems()
|
||||
|
||||
let centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||
items[identifier]
|
||||
let leftItems = items.compactMap({ (item) -> CustomTouchBarItem? in
|
||||
item.align == .left ? item : nil
|
||||
})
|
||||
let centerItems = items.compactMap({ (item) -> CustomTouchBarItem? in
|
||||
item.align == .center ? item : nil
|
||||
})
|
||||
let rightItems = items.compactMap({ (item) -> CustomTouchBarItem? in
|
||||
item.align == .right ? item : nil
|
||||
})
|
||||
|
||||
|
||||
let centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
||||
let scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
|
||||
@ -130,15 +73,17 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
touchBar.delegate = self
|
||||
touchBar.defaultItemIdentifiers = [basicViewIdentifier]
|
||||
|
||||
let leftItems = leftIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||
items[identifier]
|
||||
})
|
||||
let rightItems = rightIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||
items[identifier]
|
||||
})
|
||||
|
||||
basicView = BasicView(identifier: basicViewIdentifier, items:leftItems + [scrollArea] + rightItems, swipeItems: swipeItems)
|
||||
basicView = BasicView(identifier: basicViewIdentifier, items: leftItems + [scrollArea] + rightItems, swipeItems: swipeItems)
|
||||
basicView?.legacyGesturesEnabled = AppSettings.multitouchGestures
|
||||
|
||||
// it seems that we need to set width only after we added them to the view
|
||||
// so lets reset width here
|
||||
for item in items {
|
||||
item.setWidth(value: item.getWidth())
|
||||
if item is CustomButtonTouchBarItem {
|
||||
(item as! CustomButtonTouchBarItem).reinstallButton()
|
||||
}
|
||||
}
|
||||
|
||||
updateActiveApp()
|
||||
}
|
||||
@ -166,41 +111,26 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
reloadPreset(path: presetPath)
|
||||
}
|
||||
|
||||
func reloadPreset(path: String) {
|
||||
lastPresetPath = path
|
||||
let items = path.fileData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
|
||||
createAndUpdatePreset(newJsonItems: items)
|
||||
}
|
||||
|
||||
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "HH-mm-ss"
|
||||
let time = dateFormatter.string(from: Date())
|
||||
for item in jsonItems {
|
||||
let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString)
|
||||
let identifier = NSTouchBarItem.Identifier(identifierString)
|
||||
itemDefinitions[identifier] = item
|
||||
if item.align == .left {
|
||||
leftIdentifiers.append(identifier)
|
||||
}
|
||||
if item.align == .right {
|
||||
rightIdentifiers.append(identifier)
|
||||
}
|
||||
if item.align == .center {
|
||||
centerIdentifiers.append(identifier)
|
||||
}
|
||||
func reloadPreset(path: String?) {
|
||||
if path != nil {
|
||||
lastPresetPath = path!
|
||||
}
|
||||
|
||||
let items = lastPresetPath.fileData?.barItemDefinitions() ?? [BarItemDefinition(obj: CustomButtonTouchBarItem(title: "bad preset"))]
|
||||
createAndUpdatePreset(newItems: items)
|
||||
}
|
||||
|
||||
func createItems() {
|
||||
for (identifier, definition) in itemDefinitions {
|
||||
let item = createItem(forIdentifier: identifier, definition: definition)
|
||||
if item is SwipeItem {
|
||||
swipeItems.append(item as! SwipeItem)
|
||||
func getItems(newItems: [BarItemDefinition]) -> ([CustomTouchBarItem], [SwipeItem]) {
|
||||
var items: [CustomTouchBarItem] = []
|
||||
var swipeItems: [SwipeItem] = []
|
||||
for item in newItems {
|
||||
if item.obj is SwipeItem {
|
||||
swipeItems.append(item.obj as! SwipeItem)
|
||||
} else {
|
||||
items[identifier] = item
|
||||
items.append(item.obj)
|
||||
}
|
||||
}
|
||||
return (items, swipeItems)
|
||||
}
|
||||
|
||||
@objc func setupControlStripPresence() {
|
||||
@ -224,7 +154,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func dismissTouchBar() {
|
||||
@objc func dismissTouchBar() {
|
||||
minimizeSystemModal(touchBar)
|
||||
updateControlStripPresence()
|
||||
}
|
||||
@ -241,198 +171,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? {
|
||||
var barItem: NSTouchBarItem!
|
||||
switch item.type {
|
||||
case let .staticButton(title: title):
|
||||
barItem = CustomButtonTouchBarItem(identifier: identifier, title: title)
|
||||
case let .appleScriptTitledButton(source: source, refreshInterval: interval, alternativeImages: alternativeImages):
|
||||
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, alternativeImages: alternativeImages)
|
||||
case let .shellScriptTitledButton(source: source, refreshInterval: interval):
|
||||
barItem = ShellScriptTouchBarItem(identifier: identifier, source: source, interval: interval)
|
||||
case let .timeButton(formatTemplate: template, timeZone: timeZone, locale: locale):
|
||||
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, timeZone: timeZone, locale: locale)
|
||||
case .battery:
|
||||
barItem = BatteryBarItem(identifier: identifier)
|
||||
case let .dock(autoResize: autoResize, filter: regexString):
|
||||
if let regexString = regexString {
|
||||
guard let regex = try? NSRegularExpression(pattern: regexString, options: []) else {
|
||||
barItem = CustomButtonTouchBarItem(identifier: identifier, title: "Bad regex")
|
||||
break
|
||||
}
|
||||
barItem = AppScrubberTouchBarItem(identifier: identifier, autoResize: autoResize, filter: regex)
|
||||
} else {
|
||||
barItem = AppScrubberTouchBarItem(identifier: identifier, autoResize: autoResize)
|
||||
}
|
||||
case .volume:
|
||||
if case let .image(source)? = item.additionalParameters[.image] {
|
||||
barItem = VolumeViewController(identifier: identifier, image: source.image)
|
||||
} else {
|
||||
barItem = VolumeViewController(identifier: identifier)
|
||||
}
|
||||
case let .brightness(refreshInterval: interval):
|
||||
if case let .image(source)? = item.additionalParameters[.image] {
|
||||
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval, image: source.image)
|
||||
} else {
|
||||
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
|
||||
}
|
||||
case let .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type):
|
||||
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type)
|
||||
case let .yandexWeather(interval: interval):
|
||||
barItem = YandexWeatherBarItem(identifier: identifier, interval: interval)
|
||||
case let .currency(interval: interval, from: from, to: to, full: full):
|
||||
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, full: full)
|
||||
case .inputsource:
|
||||
barItem = InputSourceBarItem(identifier: identifier)
|
||||
case let .music(interval: interval, disableMarquee: disableMarquee):
|
||||
barItem = MusicBarItem(identifier: identifier, interval: interval, disableMarquee: disableMarquee)
|
||||
case let .group(items: items):
|
||||
barItem = GroupBarItem(identifier: identifier, items: items)
|
||||
case .nightShift:
|
||||
barItem = NightShiftBarItem(identifier: identifier)
|
||||
case .dnd:
|
||||
barItem = DnDBarItem(identifier: identifier)
|
||||
case let .pomodoro(workTime: workTime, restTime: restTime):
|
||||
barItem = PomodoroBarItem(identifier: identifier, workTime: workTime, restTime: restTime)
|
||||
case let .network(flip: flip):
|
||||
barItem = NetworkBarItem(identifier: identifier, flip: flip)
|
||||
case .darkMode:
|
||||
barItem = DarkModeBarItem(identifier: identifier)
|
||||
case let .swipe(direction: direction, fingers: fingers, minOffset: minOffset, sourceApple: sourceApple, sourceBash: sourceBash):
|
||||
barItem = SwipeItem(identifier: identifier, direction: direction, fingers: fingers, minOffset: minOffset, sourceApple: sourceApple, sourceBash: sourceBash)
|
||||
}
|
||||
|
||||
if let action = self.action(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
|
||||
item.tapClosure = action
|
||||
}
|
||||
if let longAction = self.longAction(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
|
||||
item.longTapClosure = longAction
|
||||
}
|
||||
if case let .bordered(bordered)? = item.additionalParameters[.bordered], let item = barItem as? CustomButtonTouchBarItem {
|
||||
item.isBordered = bordered
|
||||
}
|
||||
if case let .background(color)? = item.additionalParameters[.background], let item = barItem as? CustomButtonTouchBarItem {
|
||||
item.backgroundColor = color
|
||||
}
|
||||
if case let .width(value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
|
||||
widthBarItem.setWidth(value: value)
|
||||
}
|
||||
if case let .image(source)? = item.additionalParameters[.image], let item = barItem as? CustomButtonTouchBarItem {
|
||||
item.image = source.image
|
||||
}
|
||||
if case let .title(value)? = item.additionalParameters[.title] {
|
||||
if let item = barItem as? GroupBarItem {
|
||||
item.collapsedRepresentationLabel = value
|
||||
} else if let item = barItem as? CustomButtonTouchBarItem {
|
||||
item.title = value
|
||||
}
|
||||
}
|
||||
return barItem
|
||||
}
|
||||
|
||||
func action(forItem item: BarItemDefinition) -> (() -> Void)? {
|
||||
switch item.action {
|
||||
case let .hidKey(keycode: keycode):
|
||||
return { HIDPostAuxKey(keycode) }
|
||||
case let .keyPress(keycode: keycode):
|
||||
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
|
||||
case let .appleScript(source: source):
|
||||
guard let appleScript = source.appleScript else {
|
||||
print("cannot create apple script for item \(item)")
|
||||
return {}
|
||||
}
|
||||
return {
|
||||
DispatchQueue.appleScriptQueue.async {
|
||||
var error: NSDictionary?
|
||||
appleScript.executeAndReturnError(&error)
|
||||
if let error = error {
|
||||
print("error \(error) when handling \(item) ")
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .shellScript(executable: executable, parameters: parameters):
|
||||
return {
|
||||
let task = Process()
|
||||
task.launchPath = executable
|
||||
task.arguments = parameters
|
||||
task.launch()
|
||||
}
|
||||
case let .openUrl(url: url):
|
||||
return {
|
||||
if let url = URL(string: url), NSWorkspace.shared.open(url) {
|
||||
#if DEBUG
|
||||
print("URL was successfully opened")
|
||||
#endif
|
||||
} else {
|
||||
print("error", url)
|
||||
}
|
||||
}
|
||||
case let .custom(closure: closure):
|
||||
return closure
|
||||
case .none:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func longAction(forItem item: BarItemDefinition) -> (() -> Void)? {
|
||||
switch item.longAction {
|
||||
case let .hidKey(keycode: keycode):
|
||||
return { HIDPostAuxKey(keycode) }
|
||||
case let .keyPress(keycode: keycode):
|
||||
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
|
||||
case let .appleScript(source: source):
|
||||
guard let appleScript = source.appleScript else {
|
||||
print("cannot create apple script for item \(item)")
|
||||
return {}
|
||||
}
|
||||
return {
|
||||
var error: NSDictionary?
|
||||
appleScript.executeAndReturnError(&error)
|
||||
if let error = error {
|
||||
print("error \(error) when handling \(item) ")
|
||||
}
|
||||
}
|
||||
case let .shellScript(executable: executable, parameters: parameters):
|
||||
return {
|
||||
let task = Process()
|
||||
task.launchPath = executable
|
||||
task.arguments = parameters
|
||||
task.launch()
|
||||
}
|
||||
case let .openUrl(url: url):
|
||||
return {
|
||||
if let url = URL(string: url), NSWorkspace.shared.open(url) {
|
||||
#if DEBUG
|
||||
print("URL was successfully opened")
|
||||
#endif
|
||||
} else {
|
||||
print("error", url)
|
||||
}
|
||||
}
|
||||
case let .custom(closure: closure):
|
||||
return closure
|
||||
case .none:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protocol CanSetWidth {
|
||||
func setWidth(value: CGFloat)
|
||||
}
|
||||
|
||||
extension NSCustomTouchBarItem: CanSetWidth {
|
||||
func setWidth(value: CGFloat) {
|
||||
view.widthAnchor.constraint(equalToConstant: value).isActive = true
|
||||
}
|
||||
}
|
||||
|
||||
extension BarItemDefinition {
|
||||
var align: Align {
|
||||
if case let .align(result)? = additionalParameters[.align] {
|
||||
return result
|
||||
}
|
||||
return .center
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
class AppScrubberTouchBarItem: NSCustomTouchBarItem {
|
||||
class AppScrubberTouchBarItem: CustomTouchBarItem {
|
||||
private var scrollView = NSScrollView()
|
||||
private var autoResize: Bool = false
|
||||
private var widthConstraint: NSLayoutConstraint?
|
||||
@ -19,6 +19,20 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
|
||||
private var frontmostApplicationIdentifier: String? {
|
||||
return NSWorkspace.shared.frontmostApplication?.bundleIdentifier
|
||||
}
|
||||
|
||||
enum ParsingErrors: Error {
|
||||
case IncorrectRegex(description: String)
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case autoResize
|
||||
case filter
|
||||
}
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "dock"
|
||||
}
|
||||
|
||||
|
||||
private var applications: [DockItem] = []
|
||||
private var items: [DockBarItem] = []
|
||||
@ -29,6 +43,35 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
|
||||
self.autoResize = autoResize
|
||||
view = scrollView
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.autoResize = try container.decodeIfPresent(Bool.self, forKey: .autoResize) ?? false
|
||||
let filterRegexString = try container.decodeIfPresent(String.self, forKey: .filter)
|
||||
|
||||
if let filterRegexString = filterRegexString {
|
||||
let regex = try? NSRegularExpression(pattern: filterRegexString, options: [])
|
||||
if regex == nil {
|
||||
throw ParsingErrors.IncorrectRegex(description: "incorrect regex")
|
||||
}
|
||||
self.filter = regex
|
||||
} else {
|
||||
self.filter = nil
|
||||
}
|
||||
|
||||
try super.init(from: decoder)
|
||||
view = scrollView
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didLaunchApplicationNotification, object: nil)
|
||||
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didTerminateApplicationNotification, object: nil)
|
||||
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(softReloadItems), name: NSWorkspace.didActivateApplicationNotification, object: nil)
|
||||
@ -36,10 +79,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
|
||||
persistentAppIdentifiers = AppSettings.dockPersistentAppIds
|
||||
hardReloadItems()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
@objc func hardReloadItems() {
|
||||
applications = launchedApplications()
|
||||
@ -82,12 +122,17 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem {
|
||||
public func createAppButton(for app: DockItem) -> DockBarItem {
|
||||
let item = DockBarItem(app)
|
||||
item.isBordered = false
|
||||
item.tapClosure = { [weak self] in
|
||||
self?.switchToApp(app: app)
|
||||
}
|
||||
item.longTapClosure = { [weak self] in
|
||||
self?.handleHalfLongPress(item: app)
|
||||
}
|
||||
|
||||
item.setTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.switchToApp(app: app)
|
||||
} )
|
||||
)
|
||||
item.setLongTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.handleHalfLongPress(item: app)
|
||||
} )
|
||||
)
|
||||
item.killAppClosure = {[weak self] in
|
||||
self?.handleLongPress(item: app)
|
||||
}
|
||||
@ -212,8 +257,8 @@ class DockBarItem: CustomButtonTouchBarItem {
|
||||
super.init(identifier: .init(app.bundleIdentifier), title: "")
|
||||
dotView.wantsLayer = true
|
||||
|
||||
image = app.icon
|
||||
image?.size = NSSize(width: iconWidth, height: iconWidth)
|
||||
self.setImage(app.icon)
|
||||
self.getImage()?.size = NSSize(width: iconWidth, height: iconWidth)
|
||||
|
||||
killGestureRecognizer = LongPressGestureRecognizer(target: self, action: #selector(firePanGestureRecognizer))
|
||||
killGestureRecognizer.allowedTouchTypes = .direct
|
||||
@ -243,4 +288,8 @@ class DockBarItem: CustomButtonTouchBarItem {
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) {
|
||||
fatalError("init(from decoder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,21 +12,35 @@ import IOKit.ps
|
||||
class BatteryBarItem: CustomButtonTouchBarItem {
|
||||
private let batteryInfo = BatteryInfo()
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "battery"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier, title: " ")
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
batteryInfo.start { [weak self] in
|
||||
self?.refresh()
|
||||
}
|
||||
refresh()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func refresh() {
|
||||
attributedTitle = batteryInfo.formattedInfo()
|
||||
setAttributedTitle(batteryInfo.formattedInfo())
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
27
MTMR/Widgets/BrightnessDownBarItem.swift
Normal file
27
MTMR/Widgets/BrightnessDownBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// BrightnessDownBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class BrightnessDownBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "brightnessDown"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setImage(#imageLiteral(resourceName: "brightnessDown"))
|
||||
self.setTapAction(EventAction().setKeyPressClosure(keycode: 145))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/BrightnessUpBarItem.swift
Normal file
27
MTMR/Widgets/BrightnessUpBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// BrightnessUpBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class BrightnessUpBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "brightnessUp"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setImage(#imageLiteral(resourceName: "brightnessUp"))
|
||||
self.setTapAction(EventAction().setKeyPressClosure(keycode: 144))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,12 +3,39 @@ import AVFoundation
|
||||
import Cocoa
|
||||
import CoreAudio
|
||||
|
||||
class BrightnessViewController: NSCustomTouchBarItem {
|
||||
class BrightnessViewController: CustomTouchBarItem {
|
||||
private(set) var sliderItem: CustomSlider!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "brightness"
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case image
|
||||
case refreshInterval
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, refreshInterval: Double, image: NSImage? = nil) {
|
||||
super.init(identifier: identifier)
|
||||
self.setup(image: nil, interval: refreshInterval)
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let image = try container.decodeIfPresent(Source.self, forKey: .image)?.image
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
|
||||
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setup(image: image, interval: interval)
|
||||
}
|
||||
|
||||
|
||||
func setup(image: NSImage?, interval: Double) {
|
||||
if image == nil {
|
||||
sliderItem = CustomSlider()
|
||||
} else {
|
||||
@ -22,14 +49,10 @@ class BrightnessViewController: NSCustomTouchBarItem {
|
||||
|
||||
view = sliderItem
|
||||
|
||||
let timer = Timer.scheduledTimer(timeInterval: refreshInterval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true)
|
||||
let timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true)
|
||||
RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
sliderItem.unbind(NSBindingName.value)
|
||||
}
|
||||
|
||||
48
MTMR/Widgets/CloseBarItem.swift
Normal file
48
MTMR/Widgets/CloseBarItem.swift
Normal file
@ -0,0 +1,48 @@
|
||||
//
|
||||
// CloseBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/30/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class CloseBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "close"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier, title: "")
|
||||
|
||||
if self.title == "" {
|
||||
self.title = "close"
|
||||
}
|
||||
|
||||
self.setTapAction(EventAction.init({_ in
|
||||
|
||||
TouchBarController.shared.reloadPreset(path: nil)
|
||||
|
||||
} ))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
if self.title == "" {
|
||||
self.title = "close"
|
||||
}
|
||||
|
||||
self.setTapAction(EventAction.init({_ in
|
||||
|
||||
TouchBarController.shared.reloadPreset(path: nil)
|
||||
|
||||
} ))
|
||||
}
|
||||
|
||||
}
|
||||
@ -58,6 +58,18 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
"LTC": 2,
|
||||
"ETH": 2,
|
||||
]
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "currency"
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case refreshInterval
|
||||
case from
|
||||
case to
|
||||
case full
|
||||
}
|
||||
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, full: Bool) {
|
||||
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||
@ -87,7 +99,51 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
|
||||
|
||||
super.init(identifier: identifier, title: "⏳")
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
|
||||
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
|
||||
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
|
||||
let full = try container.decodeIfPresent(Bool.self, forKey: .full) ?? false
|
||||
|
||||
activity = NSBackgroundActivityScheduler(identifier: CustomTouchBarItem.createIdentifier("Currency.updatecheck").rawValue)
|
||||
activity.interval = interval
|
||||
self.from = from
|
||||
self.to = to
|
||||
self.full = full
|
||||
|
||||
if let prefix = currencies[from] {
|
||||
self.prefix = prefix
|
||||
} else {
|
||||
prefix = from
|
||||
}
|
||||
|
||||
if let postfix = currencies[to] {
|
||||
self.postfix = postfix
|
||||
} else {
|
||||
postfix = to
|
||||
}
|
||||
|
||||
|
||||
if let decimal = decimals[to] {
|
||||
self.decimal = decimal
|
||||
} else {
|
||||
decimal = 2
|
||||
}
|
||||
|
||||
try super.init(from: decoder)
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
activity.repeats = true
|
||||
activity.qualityOfService = .utility
|
||||
activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in
|
||||
@ -97,10 +153,6 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
updateCurrency()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func updateCurrency() {
|
||||
let urlRequest = URLRequest(url: URL(string: "https://api.coinbase.com/v2/exchange-rates?currency=\(from)")!)
|
||||
|
||||
@ -153,10 +205,10 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
title = String(format: "%@%.2f", prefix, value)
|
||||
}
|
||||
|
||||
let regularFont = attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
|
||||
let regularFont = getAttributedTitle().attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
|
||||
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont, .baselineOffset: 1])
|
||||
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
|
||||
attributedTitle = newTitle
|
||||
setAttributedTitle(newTitle)
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
@ -1,26 +1,43 @@
|
||||
import Foundation
|
||||
|
||||
class DarkModeBarItem: CustomButtonTouchBarItem, Widget {
|
||||
static var name: String = "darkmode"
|
||||
static var identifier: String = "com.toxblh.mtmr.darkmode"
|
||||
|
||||
class DarkModeBarItem: CustomButtonTouchBarItem {
|
||||
private var timer: Timer!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "darkMode"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier, title: "")
|
||||
isBordered = false
|
||||
setWidth(value: 24)
|
||||
|
||||
tapClosure = { [weak self] in self?.DarkModeToggle() }
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||
|
||||
refresh()
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
if getWidth() == 0.0 {
|
||||
setWidth(value: 24)
|
||||
}
|
||||
|
||||
self.setTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.DarkModeToggle()
|
||||
} )
|
||||
)
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||
|
||||
refresh()
|
||||
}
|
||||
|
||||
func DarkModeToggle() {
|
||||
DarkMode.isEnabled = !DarkMode.isEnabled
|
||||
@ -28,7 +45,7 @@ class DarkModeBarItem: CustomButtonTouchBarItem, Widget {
|
||||
}
|
||||
|
||||
@objc func refresh() {
|
||||
image = DarkMode.isEnabled ? #imageLiteral(resourceName: "dark-mode-on") : #imageLiteral(resourceName: "dark-mode-off")
|
||||
self.setImage(DarkMode.isEnabled ? #imageLiteral(resourceName: "dark-mode-on") : #imageLiteral(resourceName: "dark-mode-off"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
MTMR/Widgets/DeleteBarItem.swift
Normal file
27
MTMR/Widgets/DeleteBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// DeleteBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class DeleteBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "delete"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
print("DeleteBarItem.init")
|
||||
self.title = "del"
|
||||
self.setTapAction(EventAction().setKeyPressClosure(keycode: 117))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
27
MTMR/Widgets/DisplaySleepBarItem.swift
Normal file
27
MTMR/Widgets/DisplaySleepBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// DisplaySleep.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class DisplaySleepBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "displaySleep"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.title = "☕️"
|
||||
self.setTapAction(EventAction().setShellScriptClosure(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,21 +11,39 @@ import Foundation
|
||||
class DnDBarItem: CustomButtonTouchBarItem {
|
||||
private var timer: Timer!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "dnd"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier, title: "")
|
||||
isBordered = false
|
||||
setWidth(value: 32)
|
||||
|
||||
tapClosure = { [weak self] in self?.DnDToggle() }
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||
|
||||
refresh()
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
if getWidth() == 0.0 {
|
||||
setWidth(value: 32)
|
||||
}
|
||||
|
||||
self.setTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.DnDToggle()
|
||||
} )
|
||||
)
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||
|
||||
refresh()
|
||||
}
|
||||
|
||||
func DnDToggle() {
|
||||
DoNotDisturb.isEnabled = !DoNotDisturb.isEnabled
|
||||
@ -33,7 +51,7 @@ class DnDBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
|
||||
@objc func refresh() {
|
||||
image = DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off")
|
||||
self.setImage(DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
MTMR/Widgets/EscapeBarItem.swift
Normal file
27
MTMR/Widgets/EscapeBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// EscapeBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class EscapeBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "escape"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.title = "escape"
|
||||
self.setTapAction(EventAction().setKeyPressClosure(keycode: 53))
|
||||
self.align = .left
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
50
MTMR/Widgets/ExitTouchbarBarItem.swift
Normal file
50
MTMR/Widgets/ExitTouchbarBarItem.swift
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// ExitTouchbarBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/30/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ExitTouchbarBarItem: CustomButtonTouchBarItem {
|
||||
private var timer: Timer!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "exitTouchbar"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier, title: "")
|
||||
|
||||
if self.title == "" {
|
||||
self.title = "exit"
|
||||
}
|
||||
|
||||
self.setTapAction(EventAction.init({_ in
|
||||
|
||||
TouchBarController.shared.dismissTouchBar()
|
||||
|
||||
} ))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
if self.title == "" {
|
||||
self.title = "exit"
|
||||
}
|
||||
|
||||
self.setTapAction(EventAction.init({_ in
|
||||
|
||||
TouchBarController.shared.dismissTouchBar()
|
||||
|
||||
} ))
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,50 +7,96 @@
|
||||
//
|
||||
import Cocoa
|
||||
|
||||
class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
|
||||
var jsonItems: [BarItemDefinition]
|
||||
|
||||
class GroupBarItem: CustomTouchBarItem, NSTouchBarDelegate {
|
||||
private(set) var popoverItem: NSPopoverTouchBarItem!
|
||||
var jsonItems: [BarItemDefinition] = []
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "group"
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case items
|
||||
case title
|
||||
}
|
||||
|
||||
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
||||
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
|
||||
var items: [CustomTouchBarItem] = []
|
||||
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var centerIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var centerItems: [NSTouchBarItem] = []
|
||||
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
|
||||
var scrollArea: NSCustomTouchBarItem?
|
||||
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
||||
var swipeItems: [SwipeItem] = []
|
||||
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.GroupScrollArea.".appending(UUID().uuidString))
|
||||
var basicView: BasicView?
|
||||
var basicViewIdentifier = NSTouchBarItem.Identifier("com.toxblh.mtmr.GroupScrollView.".appending(UUID().uuidString))
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, items: [BarItemDefinition]) {
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, items: [BarItemDefinition], title: String) {
|
||||
jsonItems = items
|
||||
popoverItem = NSPopoverTouchBarItem(identifier: identifier)
|
||||
super.init(identifier: identifier)
|
||||
popoverTouchBar.delegate = self
|
||||
|
||||
self.setup(title: title)
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
popoverItem = NSPopoverTouchBarItem(identifier: identifier)
|
||||
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.jsonItems = try container.decode([BarItemDefinition].self, forKey: .items)
|
||||
let title = try container.decodeIfPresent(String.self, forKey: .title) ?? " "
|
||||
|
||||
self.setup(title: title)
|
||||
}
|
||||
|
||||
|
||||
func setup(title: String) {
|
||||
let button = NSButton(title: title, target: self,
|
||||
action: #selector(GroupBarItem.showPopover(_:)))
|
||||
|
||||
// Use the built-in gesture recognizer for tap and hold to open our popover's NSTouchBar.
|
||||
let gestureRecognizer = popoverItem.makeStandardActivatePopoverGestureRecognizer()
|
||||
button.addGestureRecognizer(gestureRecognizer)
|
||||
|
||||
popoverItem.collapsedRepresentation = button
|
||||
|
||||
view = button
|
||||
|
||||
deinit {}
|
||||
if getWidth() == 0.0 {
|
||||
setWidth(value: 60)
|
||||
}
|
||||
}
|
||||
|
||||
@objc override func showPopover(_: Any?) {
|
||||
itemDefinitions = [:]
|
||||
items = [:]
|
||||
leftIdentifiers = []
|
||||
centerItems = []
|
||||
rightIdentifiers = []
|
||||
|
||||
loadItemDefinitions(jsonItems: jsonItems)
|
||||
createItems()
|
||||
|
||||
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
|
||||
items[identifier]
|
||||
@objc func showPopover(_: Any?) {
|
||||
items = getItems(newItems: jsonItems)
|
||||
|
||||
let leftItems = items.compactMap({ (item) -> CustomTouchBarItem? in
|
||||
item.align == .left || item.align == .center ? item : nil
|
||||
})
|
||||
let centerItems = items.compactMap({ (item) -> CustomTouchBarItem? in
|
||||
item.align == .center && false ? item : nil
|
||||
})
|
||||
let rightItems = items.compactMap({ (item) -> CustomTouchBarItem? in
|
||||
item.align == .right ? item : nil
|
||||
})
|
||||
|
||||
centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
||||
scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
|
||||
|
||||
let centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
||||
let scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
|
||||
|
||||
TouchBarController.shared.touchBar.delegate = self
|
||||
TouchBarController.shared.touchBar.defaultItemIdentifiers = []
|
||||
TouchBarController.shared.touchBar.defaultItemIdentifiers = leftIdentifiers + [centerScrollArea] + rightIdentifiers
|
||||
TouchBarController.shared.touchBar.defaultItemIdentifiers = [basicViewIdentifier]
|
||||
|
||||
basicView = BasicView(identifier: basicViewIdentifier, items: leftItems + [scrollArea] + rightItems, swipeItems: swipeItems)
|
||||
basicView?.legacyGesturesEnabled = AppSettings.multitouchGestures
|
||||
|
||||
|
||||
if AppSettings.showControlStripState {
|
||||
presentSystemModal(TouchBarController.shared.touchBar, systemTrayItemIdentifier: .controlStripItem)
|
||||
@ -60,41 +106,23 @@ class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
|
||||
}
|
||||
|
||||
func touchBar(_: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
|
||||
if identifier == centerScrollArea {
|
||||
return scrollArea
|
||||
if identifier == basicViewIdentifier {
|
||||
return basicView
|
||||
}
|
||||
|
||||
guard let item = self.items[identifier],
|
||||
let definition = self.itemDefinitions[identifier],
|
||||
definition.align != .center else {
|
||||
return nil
|
||||
}
|
||||
return item
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "HH-mm-ss"
|
||||
let time = dateFormatter.string(from: Date())
|
||||
for item in jsonItems {
|
||||
let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString)
|
||||
let identifier = NSTouchBarItem.Identifier(identifierString)
|
||||
itemDefinitions[identifier] = item
|
||||
if item.align == .left {
|
||||
leftIdentifiers.append(identifier)
|
||||
}
|
||||
if item.align == .right {
|
||||
rightIdentifiers.append(identifier)
|
||||
}
|
||||
if item.align == .center {
|
||||
centerIdentifiers.append(identifier)
|
||||
|
||||
func getItems(newItems: [BarItemDefinition]) -> [CustomTouchBarItem] {
|
||||
var items: [CustomTouchBarItem] = []
|
||||
for item in newItems {
|
||||
if item.obj is SwipeItem {
|
||||
swipeItems.append(item.obj as! SwipeItem)
|
||||
} else {
|
||||
items.append(item.obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createItems() {
|
||||
for (identifier, definition) in itemDefinitions {
|
||||
items[identifier] = TouchBarController.shared.createItem(forIdentifier: identifier, definition: definition)
|
||||
}
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
MTMR/Widgets/IlluminationDownBarItem.swift
Normal file
27
MTMR/Widgets/IlluminationDownBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// illuminationDownBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class IlluminationDownBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "illuminationDown"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setImage(#imageLiteral(resourceName: "ill_down"))
|
||||
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_ILLUMINATION_DOWN))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/IlluminationUpBarItem.swift
Normal file
27
MTMR/Widgets/IlluminationUpBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// illuminationUpBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class IlluminationUpBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "illuminationUp"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setImage(#imageLiteral(resourceName: "ill_up"))
|
||||
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_ILLUMINATION_UP))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,22 +11,39 @@ import Cocoa
|
||||
class InputSourceBarItem: CustomButtonTouchBarItem {
|
||||
fileprivate var notificationCenter: CFNotificationCenter
|
||||
let buttonSize = NSSize(width: 21, height: 21)
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "inputsource"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
notificationCenter = CFNotificationCenterGetDistributedCenter()
|
||||
super.init(identifier: identifier, title: "⏳")
|
||||
|
||||
observeIputSourceChangedNotification()
|
||||
textInputSourceDidChange()
|
||||
tapClosure = { [weak self] in
|
||||
self?.switchInputSource()
|
||||
}
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
notificationCenter = CFNotificationCenterGetDistributedCenter()
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
observeIputSourceChangedNotification()
|
||||
textInputSourceDidChange()
|
||||
self.setTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.switchInputSource()
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
deinit {
|
||||
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()))
|
||||
}
|
||||
@ -45,7 +62,7 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
|
||||
|
||||
if let iconImage = iconImage {
|
||||
iconImage.size = buttonSize
|
||||
image = iconImage
|
||||
self.setImage(iconImage)
|
||||
title = ""
|
||||
} else {
|
||||
title = currentSource.name
|
||||
|
||||
@ -33,17 +33,36 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
||||
private var songTitle: String?
|
||||
private var timer: Timer?
|
||||
private let iconSize = NSSize(width: 21, height: 21)
|
||||
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case refreshInterval
|
||||
case disableMarquee
|
||||
}
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "music"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, disableMarquee: Bool) {
|
||||
self.interval = interval
|
||||
self.disableMarquee = disableMarquee
|
||||
|
||||
super.init(identifier: identifier, title: "⏳")
|
||||
isBordered = false
|
||||
|
||||
tapClosure = { [weak self] in self?.playPause() }
|
||||
longTapClosure = { [weak self] in self?.nextTrack() }
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
self.setTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.playPause()
|
||||
} )
|
||||
)
|
||||
self.setLongTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.nextTrack()
|
||||
} )
|
||||
)
|
||||
|
||||
refreshAndSchedule()
|
||||
}
|
||||
@ -60,11 +79,23 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 5.0
|
||||
self.disableMarquee = try container.decodeIfPresent(Bool.self, forKey: .disableMarquee) ?? false
|
||||
|
||||
try super.init(from: decoder)
|
||||
self.setup()
|
||||
}
|
||||
|
||||
@objc func playPause() {
|
||||
for ident in playerBundleIdentifiers {
|
||||
print("checking \(ident)")
|
||||
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
|
||||
print("musicPlayer \(musicPlayer)")
|
||||
if musicPlayer.isRunning {
|
||||
print("musicPlayer.isRunning \(musicPlayer.isRunning)")
|
||||
if ident == .Spotify {
|
||||
let mp = (musicPlayer as SpotifyApplication)
|
||||
mp.playpause!()
|
||||
@ -194,6 +225,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
||||
for ident in playerBundleIdentifiers {
|
||||
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
|
||||
if musicPlayer.isRunning {
|
||||
print("musicPlayer \(musicPlayer)")
|
||||
var tempTitle = ""
|
||||
if ident == .Spotify {
|
||||
tempTitle = (musicPlayer as SpotifyApplication).title
|
||||
@ -268,7 +300,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
||||
let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: ident.rawValue) {
|
||||
let image = NSWorkspace.shared.icon(forFile: appPath)
|
||||
image.size = self.iconSize
|
||||
self.image = image
|
||||
self.setImage(image)
|
||||
iconUpdated = true
|
||||
}
|
||||
break
|
||||
@ -278,7 +310,7 @@ class MusicBarItem: CustomButtonTouchBarItem {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if !iconUpdated {
|
||||
self.image = nil
|
||||
self.setImage(nil)
|
||||
}
|
||||
|
||||
if !titleUpdated {
|
||||
|
||||
27
MTMR/Widgets/MuteBarItem.swift
Normal file
27
MTMR/Widgets/MuteBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// MuteBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class MuteBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "mute"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setImage(NSImage(named: NSImage.touchBarAudioOutputMuteTemplateName)!)
|
||||
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_MUTE))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,12 +8,17 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
class NetworkBarItem: CustomButtonTouchBarItem, Widget {
|
||||
static var name: String = "network"
|
||||
static var identifier: String = "com.toxblh.mtmr.network"
|
||||
|
||||
class NetworkBarItem: CustomButtonTouchBarItem {
|
||||
private let flip: Bool
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case flip
|
||||
}
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "network"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, flip: Bool = false) {
|
||||
self.flip = flip
|
||||
super.init(identifier: identifier, title: " ")
|
||||
@ -23,6 +28,14 @@ class NetworkBarItem: CustomButtonTouchBarItem, Widget {
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.flip = try container.decodeIfPresent(Bool.self, forKey: .flip) ?? false
|
||||
|
||||
try super.init(from: decoder)
|
||||
startMonitoringProcess()
|
||||
}
|
||||
|
||||
func startMonitoringProcess() {
|
||||
var pipe: Pipe
|
||||
@ -144,6 +157,6 @@ class NetworkBarItem: CustomButtonTouchBarItem, Widget {
|
||||
}
|
||||
|
||||
|
||||
self.attributedTitle = newTitle
|
||||
self.setAttributedTitle(newTitle)
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/NextBarItem.swift
Normal file
27
MTMR/Widgets/NextBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// NextBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class NextBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "next"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setImage(NSImage(named: NSImage.touchBarFastForwardTemplateName)!)
|
||||
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_NEXT))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,10 @@ import Foundation
|
||||
class NightShiftBarItem: CustomButtonTouchBarItem {
|
||||
private let nsclient = CBBlueLightClient()
|
||||
private var timer: Timer!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "nightShift"
|
||||
}
|
||||
|
||||
private var blueLightStatus: Status {
|
||||
var status: Status = Status()
|
||||
@ -28,19 +32,33 @@ class NightShiftBarItem: CustomButtonTouchBarItem {
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier, title: "")
|
||||
isBordered = false
|
||||
setWidth(value: 28)
|
||||
|
||||
tapClosure = { [weak self] in self?.nightShiftAction() }
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||
|
||||
refresh()
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
if getWidth() == 0.0 {
|
||||
setWidth(value: 28)
|
||||
}
|
||||
|
||||
self.setTapAction(
|
||||
EventAction( { [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.nightShiftAction()
|
||||
} )
|
||||
)
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
|
||||
|
||||
refresh()
|
||||
}
|
||||
|
||||
func nightShiftAction() {
|
||||
setNightShift(state: !isNightShiftEnabled)
|
||||
@ -48,6 +66,6 @@ class NightShiftBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
|
||||
@objc func refresh() {
|
||||
image = isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff")
|
||||
self.setImage(isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff"))
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/PlayBarItem.swift
Normal file
27
MTMR/Widgets/PlayBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// PlayBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class PlayBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "play"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setImage(NSImage(named: NSImage.touchBarPlayPauseTemplateName)!)
|
||||
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_PLAY))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,25 +8,14 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
class PomodoroBarItem: CustomButtonTouchBarItem, Widget {
|
||||
static let identifier = "com.toxblh.mtmr.pomodoro."
|
||||
static let name = "pomodoro"
|
||||
static let decoder: ParametersDecoder = { decoder in
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case workTime
|
||||
case restTime
|
||||
}
|
||||
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime)
|
||||
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime)
|
||||
|
||||
return (
|
||||
item: .pomodoro(workTime: workTime ?? 1500.00, restTime: restTime ?? 300),
|
||||
action: .none,
|
||||
longAction: .none,
|
||||
parameters: [:]
|
||||
)
|
||||
class PomodoroBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "pomodoro"
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case workTime
|
||||
case restTime
|
||||
}
|
||||
|
||||
private enum TimeTypes {
|
||||
@ -45,18 +34,44 @@ class PomodoroBarItem: CustomButtonTouchBarItem, Widget {
|
||||
private var timeLeftString: String {
|
||||
return String(format: "%.2i:%.2i", timeLeft / 60, timeLeft % 60)
|
||||
}
|
||||
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, workTime: TimeInterval, restTime: TimeInterval) {
|
||||
self.workTime = workTime
|
||||
self.restTime = restTime
|
||||
super.init(identifier: identifier, title: defaultTitle)
|
||||
tapClosure = { [weak self] in self?.startStopWork() }
|
||||
longTapClosure = { [weak self] in self?.startStopRest() }
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.workTime = try container.decodeIfPresent(Double.self, forKey: .workTime) ?? 1500.0
|
||||
self.restTime = try container.decodeIfPresent(Double.self, forKey: .restTime) ?? 600.0
|
||||
|
||||
try super.init(from: decoder)
|
||||
self.title = defaultTitle
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
self.setTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.startStopWork()
|
||||
} )
|
||||
)
|
||||
self.setLongTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.startStopRest()
|
||||
} )
|
||||
)
|
||||
}
|
||||
|
||||
deinit {
|
||||
timer?.cancel()
|
||||
|
||||
27
MTMR/Widgets/PreviousBarItem.swift
Normal file
27
MTMR/Widgets/PreviousBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// PreviousBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class PreviousBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "previous"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setImage(NSImage(named: NSImage.touchBarRewindTemplateName)!)
|
||||
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_PREVIOUS))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/SleepBarItem.swift
Normal file
27
MTMR/Widgets/SleepBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// SleepBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class SleepBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "sleep"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.title = "☕️"
|
||||
self.setTapAction(EventAction().setShellScriptClosure(executable: "/usr/bin/pmset", parameters: ["sleepnow"]))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,40 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
|
||||
private let dateFormatter = DateFormatter()
|
||||
private var timer: Timer!
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case formatTemplate
|
||||
case timeZone
|
||||
case locale
|
||||
}
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "timeButton"
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, formatTemplate: String, timeZone: String? = nil, locale: String? = nil) {
|
||||
super.init(identifier: identifier, title: " ")
|
||||
self.setupFormatter(formatTemplate: formatTemplate, timeZone: timeZone, locale: locale)
|
||||
self.setupTimer()
|
||||
updateTime()
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
|
||||
let timeZone = try container.decodeIfPresent(String.self, forKey: .timeZone) ?? nil
|
||||
let locale = try container.decodeIfPresent(String.self, forKey: .locale) ?? nil
|
||||
|
||||
try super.init(from: decoder)
|
||||
self.setupFormatter(formatTemplate: template, timeZone: timeZone, locale: locale)
|
||||
self.setupTimer()
|
||||
updateTime()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setupFormatter(formatTemplate: String, timeZone: String? = nil, locale: String? = nil) {
|
||||
dateFormatter.dateFormat = formatTemplate
|
||||
if let locale = locale {
|
||||
dateFormatter.locale = Locale(identifier: locale)
|
||||
@ -12,14 +45,10 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
|
||||
if let abbr = timeZone {
|
||||
dateFormatter.timeZone = TimeZone(abbreviation: abbr)
|
||||
}
|
||||
super.init(identifier: identifier, title: " ")
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
|
||||
isBordered = false
|
||||
updateTime()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
|
||||
func setupTimer() {
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
|
||||
}
|
||||
|
||||
@objc func updateTime() {
|
||||
|
||||
27
MTMR/Widgets/VolumeDownBarItem.swift
Normal file
27
MTMR/Widgets/VolumeDownBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// volumeDownBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class VolumeDownBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "volumeDown"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setImage(NSImage(named: NSImage.touchBarVolumeDownTemplateName)!)
|
||||
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_SOUND_DOWN))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
27
MTMR/Widgets/VolumeUpBarItem.swift
Normal file
27
MTMR/Widgets/VolumeUpBarItem.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// VolumeUpBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by Fedor Zaitsev on 4/27/20.
|
||||
// Copyright © 2020 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class VolumeUpBarItem: CustomButtonTouchBarItem {
|
||||
override class var typeIdentifier: String {
|
||||
return "volumeUp"
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setImage(NSImage(named: NSImage.touchBarVolumeUpTemplateName)!)
|
||||
self.setTapAction(EventAction().setHidKeyClosure(keycode: NX_KEYTYPE_SOUND_UP))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,12 +3,33 @@ import AVFoundation
|
||||
import Cocoa
|
||||
import CoreAudio
|
||||
|
||||
class VolumeViewController: NSCustomTouchBarItem {
|
||||
class VolumeViewController: CustomTouchBarItem {
|
||||
private(set) var sliderItem: CustomSlider!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "volume"
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case image
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, image: NSImage? = nil) {
|
||||
super.init(identifier: identifier)
|
||||
|
||||
self.setup(image: image)
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let image = try container.decodeIfPresent(Source.self, forKey: .image)?.image
|
||||
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setup(image: image)
|
||||
}
|
||||
|
||||
func setup(image: NSImage?) {
|
||||
var forPropertyAddress = AudioObjectPropertyAddress(
|
||||
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
|
||||
mScope: kAudioDevicePropertyScopeOutput,
|
||||
@ -36,6 +57,7 @@ class VolumeViewController: NSCustomTouchBarItem {
|
||||
self.sliderItem.floatValue = self.getInputGain() * 100
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
|
||||
@ -21,10 +21,21 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||
private var iconsSource: Dictionary<String, String>
|
||||
|
||||
private var manager: CLLocationManager!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "weather"
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case refreshInterval
|
||||
case units
|
||||
case api_key
|
||||
case icon_type
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text") {
|
||||
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||
activity.interval = interval
|
||||
self.activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||
self.activity.interval = interval
|
||||
self.units = units
|
||||
self.api_key = api_key
|
||||
|
||||
@ -43,7 +54,44 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||
}
|
||||
|
||||
super.init(identifier: identifier, title: "⏳")
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
|
||||
|
||||
|
||||
self.activity = NSBackgroundActivityScheduler(identifier: CustomTouchBarItem.createIdentifier("Weather.updatecheck").rawValue)
|
||||
self.activity.interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||
self.units = try container.decodeIfPresent(String.self, forKey: .units) ?? "metric"
|
||||
self.api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
|
||||
|
||||
if self.units == "metric" {
|
||||
units_str = "°C"
|
||||
}
|
||||
|
||||
if self.units == "imperial" {
|
||||
units_str = "°F"
|
||||
}
|
||||
|
||||
if icon_type == "images" {
|
||||
iconsSource = iconsImages
|
||||
} else {
|
||||
iconsSource = iconsText
|
||||
}
|
||||
|
||||
try super.init(from: decoder)
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setup() {
|
||||
let status = CLLocationManager.authorizationStatus()
|
||||
if status == .restricted || status == .denied {
|
||||
print("User permission not given")
|
||||
@ -69,10 +117,6 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||
manager.startUpdatingLocation()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func updateWeather() {
|
||||
if location != nil {
|
||||
let urlRequest = URLRequest(url: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)&units=\(units)&appid=\(api_key)")!)
|
||||
|
||||
@ -19,13 +19,38 @@ class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate
|
||||
private var location: CLLocation!
|
||||
private var prevLocation: CLLocation!
|
||||
private var manager: CLLocationManager!
|
||||
|
||||
override class var typeIdentifier: String {
|
||||
return "weather"
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case refreshInterval
|
||||
}
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
|
||||
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||
activity.interval = interval
|
||||
|
||||
super.init(identifier: identifier, title: "⏳")
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.activity = NSBackgroundActivityScheduler(identifier: CustomTouchBarItem.createIdentifier("YandexWeather.updatecheck").rawValue)
|
||||
self.activity.interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||
|
||||
try super.init(from: decoder)
|
||||
self.setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
let status = CLLocationManager.authorizationStatus()
|
||||
if status == .restricted || status == .denied {
|
||||
print("User permission not given")
|
||||
@ -50,11 +75,12 @@ class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate
|
||||
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
|
||||
manager.startUpdatingLocation()
|
||||
|
||||
tapClosure = tapClosure ?? defaultTapAction
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
|
||||
self.setTapAction(
|
||||
EventAction({ [weak self] (_ caller: CustomButtonTouchBarItem) in
|
||||
self?.defaultTapAction()
|
||||
} )
|
||||
)
|
||||
}
|
||||
|
||||
@objc func updateWeather() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user