mirror of
https://github.com/Toxblh/MTMR.git
synced 2026-10-31 02:18:35 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b73f8865a9 | ||
|
|
852df9d6b6 | ||
|
|
57e8af90dd | ||
|
|
0e48566c74 | ||
|
|
55177a7f90 | ||
|
|
d46827832e | ||
|
|
9e10b2d3d1 | ||
|
|
33efbbebaa | ||
|
|
d048ec851e | ||
|
|
42ebb91e6b | ||
|
|
1da4e2795f | ||
|
|
21bc525a04 | ||
|
|
9a34d5bca0 | ||
|
|
4c9820a390 | ||
|
|
634beeae8b | ||
|
|
3ec2422181 | ||
|
|
a3c7fa7464 | ||
|
|
20ed827750 | ||
|
|
85aa2c4f2b | ||
|
|
b761551384 | ||
|
|
3854b455a6 | ||
|
|
9c803e85cc | ||
|
|
651258b01f | ||
|
|
02baef80db | ||
|
|
cd75be6c26 | ||
|
|
7646c6e89d | ||
|
|
9c970aff63 | ||
|
|
49d7333d5e | ||
|
|
cd69bc341c | ||
|
|
453895f395 | ||
|
|
12796d6387 | ||
|
|
f65e72faed | ||
|
|
df535b2893 | ||
|
|
574ca0fb84 | ||
|
|
4a03ba79ec | ||
|
|
907b79965d | ||
|
|
fbec803d47 | ||
|
|
3f9574245a | ||
|
|
2bf6b90b04 | ||
|
|
9d596c6adc | ||
|
|
175e9edb8c | ||
|
|
8e58bb179a | ||
|
|
d37d9607d3 | ||
|
|
6b553c4d0d | ||
|
|
788927edb3 | ||
|
|
9013779f71 | ||
|
|
a85507c544 | ||
|
|
59aa113995 | ||
|
|
9c5a19925b | ||
|
|
2aa602d77d | ||
|
|
a1b3f13902 | ||
|
|
60384561d2 | ||
|
|
77938340d6 | ||
|
|
93d2f496d0 | ||
|
|
0808f0617a | ||
|
|
966a924183 | ||
|
|
a648402d04 | ||
|
|
207af6468e | ||
|
|
6609a7c806 | ||
|
|
479190d12b | ||
|
|
5ed1790601 | ||
|
|
b95f5af95f | ||
|
|
c76027ca9d | ||
|
|
55dc551516 | ||
|
|
cf5d8bf546 | ||
|
|
8031108fc9 | ||
|
|
d1a577868b | ||
|
|
5f02bb2962 | ||
|
|
cc1332dd02 | ||
|
|
a0dbfca828 | ||
|
|
76bdf12745 | ||
|
|
e1268d0d65 | ||
|
|
9c1e5f5032 | ||
|
|
88cde9256a | ||
|
|
d974f92e3d | ||
|
|
4b8ef1cd96 | ||
|
|
e65c57dbaa | ||
|
|
babc8451e0 | ||
|
|
7698ea1c8e | ||
|
|
aa95d1a1bd | ||
|
|
3fe6c22068 | ||
|
|
5ab19ee27b | ||
|
|
ae4b0aa870 | ||
|
|
1e2043ebda | ||
|
|
409b8d8b46 | ||
|
|
8bccbf3834 |
@@ -0,0 +1,6 @@
|
||||
language: swift
|
||||
xcode_project: MTMR.xcodeproj
|
||||
xcode_scheme: UnitTests
|
||||
osx_image: xcode9.3
|
||||
install: gem install xcpretty
|
||||
script: "xcodebuild test -project MTMR.xcodeproj -scheme 'UnitTests' | xcpretty -c && exit ${PIPESTATUS[0]}"
|
||||
@@ -7,6 +7,9 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
36300E83209F040900B31C71 /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; };
|
||||
36300E8F20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */; };
|
||||
36300E9120A2D2B200B31C71 /* BackgroundColorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36300E9020A2D2B200B31C71 /* BackgroundColorTests.swift */; };
|
||||
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368EDDE620812A1D00E10953 /* ScrollViewItem.swift */; };
|
||||
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */; };
|
||||
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */; };
|
||||
@@ -46,17 +49,10 @@
|
||||
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
B082B262205C7D8000BC04DC /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = B082B247205C7D8000BC04DC /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = B082B24E205C7D8000BC04DC;
|
||||
remoteInfo = MTMR;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
36300E85209FD16700B31C71 /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = SOURCE_ROOT; };
|
||||
36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptDefinitionTests.swift; sourceTree = "<group>"; };
|
||||
36300E9020A2D2B200B31C71 /* BackgroundColorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundColorTests.swift; sourceTree = "<group>"; };
|
||||
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewItem.swift; sourceTree = "<group>"; };
|
||||
36BDC22F207CDA8600FCFEBE /* TECHNICAL_DEBT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TECHNICAL_DEBT.md; sourceTree = "<group>"; };
|
||||
36C2ECD2207B3B1D003CDA33 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
@@ -184,8 +180,11 @@
|
||||
B082B264205C7D8000BC04DC /* MTMRTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36300E85209FD16700B31C71 /* .travis.yml */,
|
||||
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */,
|
||||
B082B267205C7D8000BC04DC /* Info.plist */,
|
||||
36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */,
|
||||
36300E9020A2D2B200B31C71 /* BackgroundColorTests.swift */,
|
||||
);
|
||||
path = MTMRTests;
|
||||
sourceTree = "<group>";
|
||||
@@ -266,7 +265,6 @@
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
B082B263205C7D8000BC04DC /* PBXTargetDependency */,
|
||||
);
|
||||
name = MTMRTests;
|
||||
productName = MTMRTests;
|
||||
@@ -378,21 +376,16 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
36300E9120A2D2B200B31C71 /* BackgroundColorTests.swift in Sources */,
|
||||
36300E8F20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift in Sources */,
|
||||
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */,
|
||||
36300E83209F040900B31C71 /* SupportHelpers.swift in Sources */,
|
||||
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
B082B263205C7D8000BC04DC /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = B082B24E205C7D8000BC04DC /* MTMR */;
|
||||
targetProxy = B082B262205C7D8000BC04DC /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
B082B258205C7D8000BC04DC /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0920"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
|
||||
BuildableName = "MTMR.app"
|
||||
BlueprintName = "MTMR"
|
||||
ReferencedContainer = "container:MTMR.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B082B260205C7D8000BC04DC"
|
||||
BuildableName = "MTMRTests.xctest"
|
||||
BlueprintName = "MTMRTests"
|
||||
ReferencedContainer = "container:MTMR.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
|
||||
BuildableName = "MTMR.app"
|
||||
BlueprintName = "MTMR"
|
||||
ReferencedContainer = "container:MTMR.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
|
||||
BuildableName = "MTMR.app"
|
||||
BlueprintName = "MTMR"
|
||||
ReferencedContainer = "container:MTMR.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
|
||||
BuildableName = "MTMR.app"
|
||||
BlueprintName = "MTMR"
|
||||
ReferencedContainer = "container:MTMR.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0920"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B082B260205C7D8000BC04DC"
|
||||
BuildableName = "MTMRTests.xctest"
|
||||
BlueprintName = "MTMRTests"
|
||||
ReferencedContainer = "container:MTMR.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -41,6 +41,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
TouchBarController.shared.createAndUpdatePreset()
|
||||
}
|
||||
|
||||
@objc func toggleControlStrip(_ sender: Any?) {
|
||||
TouchBarController.shared.controlStripState = !TouchBarController.shared.controlStripState
|
||||
createMenu()
|
||||
TouchBarController.shared.resetControlStrip()
|
||||
}
|
||||
|
||||
@objc func openPreset(_ sender: Any?) {
|
||||
let dialog = NSOpenPanel();
|
||||
|
||||
@@ -71,6 +77,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
menu.addItem(withTitle: "Preferences", action: #selector(openPrefereces(_:)), keyEquivalent: ",")
|
||||
menu.addItem(withTitle: "Reload Preset", action: #selector(updatePreset(_:)), keyEquivalent: "r")
|
||||
menu.addItem(withTitle: "Open Preset", action: #selector(openPreset(_:)), keyEquivalent: "O")
|
||||
menu.addItem(withTitle: TouchBarController.shared.controlStripState ? "Hide Control Strip" : "Show Control Strip" , action: #selector(toggleControlStrip(_:)), keyEquivalent: "T")
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
|
||||
statusItem.menu = menu
|
||||
|
||||
@@ -10,7 +10,7 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
super.init(identifier: identifier, title: "⏳", onTap: onTap, onLongTap: onLongTap)
|
||||
self.forceHideConstraint = self.view.widthAnchor.constraint(equalToConstant: 0)
|
||||
guard let script = source.appleScript else {
|
||||
button.title = "no script"
|
||||
self.title = "no script"
|
||||
return
|
||||
}
|
||||
self.script = script
|
||||
@@ -22,7 +22,7 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
print(error?.description ?? "unknown error")
|
||||
#endif
|
||||
DispatchQueue.main.async {
|
||||
self.button.title = "error"
|
||||
self.title = "error"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -36,11 +36,11 @@ class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
|
||||
|
||||
func refreshAndSchedule() {
|
||||
#if DEBUG
|
||||
print("refresh happened")
|
||||
print("refresh happened (interval \(self.interval)), self \(self.identifier.rawValue))")
|
||||
#endif
|
||||
let scriptResult = self.execute()
|
||||
DispatchQueue.main.async {
|
||||
self.button.title = scriptResult
|
||||
self.title = scriptResult
|
||||
self.forceHideConstraint.isActive = scriptResult == ""
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + self.interval) { [weak self] in
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
@interface MediaKeys : NSObject
|
||||
|
||||
+ (void)decreaseVolume;
|
||||
+ (void)increaseVolume;
|
||||
+ (void)muteVolume;
|
||||
+ (void)HIDPostAuxKey:(UInt8)keyCode;
|
||||
|
||||
@end
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import "TouchBarSupport.h"
|
||||
#import <IOKit/hidsystem/ev_keymap.h>
|
||||
|
||||
@implementation MediaKeys
|
||||
|
||||
@@ -30,7 +31,7 @@ static io_connect_t get_event_driver(void)
|
||||
}
|
||||
|
||||
|
||||
static void HIDPostAuxKey( const UInt8 auxKeyCode )
|
||||
static void HIDReleaseAuxKey( const UInt8 auxKeyCode )
|
||||
{
|
||||
NXEventData event;
|
||||
kern_return_t kr;
|
||||
@@ -51,16 +52,8 @@ static void HIDPostAuxKey( const UInt8 auxKeyCode )
|
||||
kr = IOHIDPostEvent( get_event_driver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE );
|
||||
}
|
||||
|
||||
+ (void)decreaseVolume {
|
||||
HIDPostAuxKey(NX_KEYTYPE_SOUND_DOWN);
|
||||
}
|
||||
|
||||
+ (void)increaseVolume {
|
||||
HIDPostAuxKey(NX_KEYTYPE_SOUND_UP);
|
||||
}
|
||||
|
||||
+ (void)muteVolume {
|
||||
HIDPostAuxKey(NX_KEYTYPE_MUTE);
|
||||
+ (void)HIDPostAuxKey: (UInt8)keyCode {
|
||||
HIDReleaseAuxKey(keyCode);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
import Cocoa
|
||||
|
||||
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
|
||||
private let tapClosure: () -> ()?
|
||||
private let longTapClosure: () -> ()?
|
||||
private(set) var button: NSButton!
|
||||
private let tapClosure: (() -> ())?
|
||||
private let longTapClosure: (() -> ())?
|
||||
private(set) var button: NSButton! //todo hide completely
|
||||
|
||||
private var singleClick: NSClickGestureRecognizer!
|
||||
private var longClick: NSPressGestureRecognizer!
|
||||
@@ -19,12 +19,10 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
init(identifier: NSTouchBarItem.Identifier, title: String, onTap callback: @escaping () -> (), onLongTap callbackLong: @escaping () -> (), bezelColor: NSColor? = .clear) {
|
||||
self.tapClosure = callback
|
||||
self.longTapClosure = callbackLong
|
||||
self.attributedTitle = title.defaultTouchbarAttributedString
|
||||
|
||||
super.init(identifier: identifier)
|
||||
button = NSButton(title: title, target: self, action: nil)
|
||||
button.bezelColor = bezelColor
|
||||
button.title = title
|
||||
self.view = button
|
||||
button = CustomHeightButton(title: title, target: nil, action: nil)
|
||||
|
||||
longClick = NSPressGestureRecognizer(target: self, action: #selector(handleGestureLong))
|
||||
longClick.allowedTouchTypes = .direct
|
||||
@@ -34,14 +32,61 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
singleClick.allowedTouchTypes = .direct
|
||||
singleClick.delegate = self
|
||||
|
||||
self.view.addGestureRecognizer(longClick)
|
||||
self.view.addGestureRecognizer(singleClick)
|
||||
reinstallButton()
|
||||
button.attributedTitle = attributedTitle
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
var isBordered: Bool = true {
|
||||
didSet {
|
||||
reinstallButton()
|
||||
}
|
||||
}
|
||||
|
||||
var backgroundColor: NSColor? {
|
||||
didSet {
|
||||
reinstallButton()
|
||||
}
|
||||
}
|
||||
|
||||
var title: String {
|
||||
get {
|
||||
return self.attributedTitle.string
|
||||
}
|
||||
set {
|
||||
self.attributedTitle = newValue.defaultTouchbarAttributedString
|
||||
}
|
||||
}
|
||||
|
||||
var attributedTitle: NSAttributedString {
|
||||
didSet {
|
||||
self.button?.attributedTitle = attributedTitle
|
||||
}
|
||||
}
|
||||
|
||||
private func reinstallButton() {
|
||||
let title = button.attributedTitle
|
||||
let image = button.image
|
||||
let cell = CustomButtonCell(parentItem: self)
|
||||
button.cell = cell
|
||||
if let color = backgroundColor {
|
||||
cell.isBordered = true
|
||||
button.bezelColor = color
|
||||
cell.backgroundColor = color
|
||||
} else {
|
||||
button.isBordered = isBordered
|
||||
button.bezelStyle = isBordered ? .rounded : .inline
|
||||
}
|
||||
button.attributedTitle = title
|
||||
button.image = image
|
||||
self.view = button
|
||||
|
||||
self.view.addGestureRecognizer(longClick)
|
||||
self.view.addGestureRecognizer(singleClick)
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer == singleClick && otherGestureRecognizer == longClick {
|
||||
@@ -55,7 +100,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
switch gr.state {
|
||||
case .ended:
|
||||
hf.tap(strong: 2)
|
||||
self.tapClosure()
|
||||
self.tapClosure?()
|
||||
break
|
||||
default:
|
||||
break
|
||||
@@ -66,12 +111,12 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
let hf: HapticFeedback = HapticFeedback()
|
||||
switch gr.state {
|
||||
case .began:
|
||||
if self.longTapClosure != nil {
|
||||
if let closure = self.longTapClosure {
|
||||
hf.tap(strong: 2)
|
||||
self.tapClosure()
|
||||
} else {
|
||||
closure()
|
||||
} else if let closure = self.tapClosure {
|
||||
hf.tap(strong: 6)
|
||||
self.longTapClosure()
|
||||
closure()
|
||||
print("long click")
|
||||
}
|
||||
break
|
||||
@@ -82,3 +127,52 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
|
||||
}
|
||||
}
|
||||
|
||||
class CustomHeightButton : NSButton {
|
||||
|
||||
override var intrinsicContentSize: NSSize {
|
||||
var size = super.intrinsicContentSize
|
||||
size.height = 30
|
||||
return size
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CustomButtonCell: NSButtonCell {
|
||||
weak var parentItem: CustomButtonTouchBarItem?
|
||||
|
||||
init(parentItem: CustomButtonTouchBarItem) {
|
||||
super.init(textCell: "")
|
||||
self.parentItem = parentItem
|
||||
}
|
||||
|
||||
override func highlight(_ flag: Bool, withFrame cellFrame: NSRect, in controlView: NSView) {
|
||||
super.highlight(flag, withFrame: cellFrame, in: controlView)
|
||||
if !self.isBordered {
|
||||
if flag {
|
||||
self.setAttributedTitle(self.attributedTitle, withColor: .lightGray)
|
||||
} else if let parentItem = self.parentItem {
|
||||
self.attributedTitle = parentItem.attributedTitle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required init(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setAttributedTitle(_ title: NSAttributedString, withColor color: NSColor) {
|
||||
let attrTitle = NSMutableAttributedString(attributedString: title)
|
||||
attrTitle.addAttributes([.foregroundColor: color], range: NSRange(location: 0, length: attrTitle.length))
|
||||
self.attributedTitle = attrTitle
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension String {
|
||||
var defaultTouchbarAttributedString: NSAttributedString {
|
||||
let attrTitle = NSMutableAttributedString(string: self, attributes: [.foregroundColor: NSColor.white, .font: NSFont.systemFont(ofSize: 15, weight: .regular), .baselineOffset: 1])
|
||||
attrTitle.setAlignment(.center, range: NSRange(location: 0, length: self.count))
|
||||
return attrTitle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,19 +49,19 @@ class CustomSliderCell: NSSliderCell {
|
||||
override func drawBar(inside aRect: NSRect, flipped: Bool) {
|
||||
_barRect = aRect
|
||||
|
||||
var rect = aRect
|
||||
rect.size.height = CGFloat(4)
|
||||
let barRadius = CGFloat(2)
|
||||
let value = CGFloat((self.doubleValue - self.minValue) / (self.maxValue - self.minValue))
|
||||
let finalWidth = CGFloat(value * (self.controlView!.frame.size.width - 12))
|
||||
|
||||
var bgRect = aRect
|
||||
bgRect.size.height = CGFloat(4)
|
||||
|
||||
var leftRect = rect
|
||||
leftRect.size.width = finalWidth
|
||||
let bg = NSBezierPath(roundedRect: rect, xRadius: barRadius, yRadius: barRadius)
|
||||
let bg = NSBezierPath(roundedRect: bgRect, xRadius: barRadius, yRadius: barRadius)
|
||||
NSColor.lightGray.setFill()
|
||||
bg.fill()
|
||||
|
||||
let active = NSBezierPath(roundedRect: leftRect, xRadius: barRadius, yRadius: barRadius)
|
||||
var activeRect = bgRect
|
||||
|
||||
activeRect.size.width = CGFloat((Double(bgRect.size.width) / (self.maxValue - self.minValue)) * self.doubleValue)
|
||||
let active = NSBezierPath(roundedRect: activeRect, xRadius: barRadius, yRadius: barRadius)
|
||||
NSColor.darkGray.setFill()
|
||||
active.fill()
|
||||
}
|
||||
|
||||
+6
-2
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.13</string>
|
||||
<string>0.14</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
@@ -26,8 +26,12 @@
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2018 Anton Palgunov. All rights reserved.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>Weather widget need your location for correct work</string>
|
||||
<key>NSLocationUsageDescription</key>
|
||||
<string>...</string>
|
||||
<string>Weather widget need your location for correct work</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>Weather widget need your location for correct work</string>
|
||||
<key>NSMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
+16
-61
@@ -47,11 +47,11 @@ class SupportedTypesHolder {
|
||||
"delete": { _ in return (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: 113), longAction: .none, parameters: [.image: imageParameter])
|
||||
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: 107), longAction: .none, parameters: [.image: imageParameter])
|
||||
return (item: .staticButton(title: ""), action: .keyPress(keycode: 145), longAction: .none, parameters: [.image: imageParameter])
|
||||
},
|
||||
"volumeDown": { _ in
|
||||
let imageParameter = GeneralParameter.image(source: NSImage(named: .touchBarVolumeDownTemplate)!)
|
||||
@@ -229,7 +229,7 @@ enum ItemType: Decodable {
|
||||
|
||||
enum ActionType: Decodable {
|
||||
case none
|
||||
case hidKey(keycode: Int)
|
||||
case hidKey(keycode: Int32)
|
||||
case keyPress(keycode: Int)
|
||||
case appleSctipt(source: SourceProtocol)
|
||||
case shellScript(executable: String, parameters: [String])
|
||||
@@ -258,7 +258,7 @@ enum ActionType: Decodable {
|
||||
let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
|
||||
switch type {
|
||||
case .some(.hidKey):
|
||||
let keycode = try container.decode(Int.self, forKey: .keycode)
|
||||
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)
|
||||
@@ -282,7 +282,7 @@ enum ActionType: Decodable {
|
||||
|
||||
enum LongActionType: Decodable {
|
||||
case none
|
||||
case hidKey(keycode: Int)
|
||||
case hidKey(keycode: Int32)
|
||||
case keyPress(keycode: Int)
|
||||
case appleSctipt(source: SourceProtocol)
|
||||
case shellScript(executable: String, parameters: [String])
|
||||
@@ -311,7 +311,7 @@ enum LongActionType: Decodable {
|
||||
let longType = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction)
|
||||
switch longType {
|
||||
case .some(.hidKey):
|
||||
let keycode = try container.decode(Int.self, forKey: .keycode)
|
||||
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)
|
||||
@@ -332,62 +332,13 @@ enum LongActionType: Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
extension ItemType: Equatable {}
|
||||
func ==(lhs: ItemType, rhs: ItemType) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case let (.staticButton(a), .staticButton(b)):
|
||||
return a == b
|
||||
case let (.appleScriptTitledButton(a, b), .appleScriptTitledButton(c, d)):
|
||||
return a == c && b == d
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension ActionType: Equatable {}
|
||||
func ==(lhs: ActionType, rhs: ActionType) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.none, .none):
|
||||
return true
|
||||
case let (.hidKey(a), .hidKey(b)),
|
||||
let (.keyPress(a), .keyPress(b)):
|
||||
return a == b
|
||||
case let (.appleSctipt(a), .appleSctipt(b)):
|
||||
return a == b
|
||||
case let (.shellScript(a, b), .shellScript(c, d)):
|
||||
return a == c && b == d
|
||||
case let (.openUrl(a), .openUrl(b)):
|
||||
return a == b
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension LongActionType: Equatable {}
|
||||
func ==(lhs: LongActionType, rhs: LongActionType) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.none, .none):
|
||||
return true
|
||||
case let (.hidKey(a), .hidKey(b)),
|
||||
let (.keyPress(a), .keyPress(b)):
|
||||
return a == b
|
||||
case let (.appleSctipt(a), .appleSctipt(b)):
|
||||
return a == b
|
||||
case let (.shellScript(a, b), .shellScript(c, d)):
|
||||
return a == c && b == d
|
||||
case let (.openUrl(a), .openUrl(b)):
|
||||
return a == b
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
enum GeneralParameter {
|
||||
case width(_: CGFloat)
|
||||
case image(source: SourceProtocol)
|
||||
case align(_: Align)
|
||||
case bordered(_: Bool)
|
||||
case background(_:NSColor)
|
||||
}
|
||||
|
||||
struct GeneralParameters: Decodable {
|
||||
@@ -397,6 +348,8 @@ struct GeneralParameters: Decodable {
|
||||
case width
|
||||
case image
|
||||
case align
|
||||
case bordered
|
||||
case background
|
||||
}
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
@@ -409,6 +362,12 @@ struct GeneralParameters: Decodable {
|
||||
}
|
||||
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)
|
||||
}
|
||||
parameters = result
|
||||
}
|
||||
}
|
||||
@@ -458,10 +417,6 @@ extension NSImage: SourceProtocol {
|
||||
return self
|
||||
}
|
||||
}
|
||||
extension SourceProtocol where Self: Equatable {}
|
||||
func ==(left: SourceProtocol, right: SourceProtocol) -> Bool {
|
||||
return left.data == right.data
|
||||
}
|
||||
|
||||
extension String {
|
||||
var base64Data: Data? {
|
||||
|
||||
+3
-41
@@ -29,45 +29,7 @@ extension KeyPress {
|
||||
}
|
||||
}
|
||||
|
||||
func doKey(_ key: Int, down: Bool) {
|
||||
let flags = NSEvent.ModifierFlags(rawValue: down ? 0xa00 : 0xb00)
|
||||
let data1 = (key << 16) | ((down ? 0xa : 0xb) << 8)
|
||||
|
||||
let ev = NSEvent.otherEvent(
|
||||
with: NSEvent.EventType.systemDefined,
|
||||
location: NSPoint(x:0.0, y:0.0),
|
||||
modifierFlags: flags,
|
||||
timestamp: TimeInterval(0),
|
||||
windowNumber: 0,
|
||||
context: nil,
|
||||
// context: 0,
|
||||
subtype: 8,
|
||||
data1: data1,
|
||||
data2: -1
|
||||
)
|
||||
let cev = ev!.cgEvent!
|
||||
cev.post(tap: CGEventTapLocation(rawValue: 0)!)
|
||||
func HIDPostAuxKey(_ key: Int32) {
|
||||
let key = UInt8(key)
|
||||
MediaKeys.hidPostAuxKey(key)
|
||||
}
|
||||
|
||||
func HIDPostAuxKey(_ key: Int) {
|
||||
doKey(key, down: true)
|
||||
doKey(key, down: false)
|
||||
}
|
||||
|
||||
|
||||
// hidsystem/ev_keymap.h
|
||||
let NX_KEYTYPE_SOUND_UP = 0
|
||||
let NX_KEYTYPE_SOUND_DOWN = 1
|
||||
let NX_KEYTYPE_MUTE = 7
|
||||
|
||||
let NX_KEYTYPE_BRIGHTNESS_UP = 2
|
||||
let NX_KEYTYPE_BRIGHTNESS_DOWN = 3
|
||||
|
||||
let NX_KEYTYPE_PLAY = 16
|
||||
let NX_KEYTYPE_NEXT = 17
|
||||
let NX_KEYTYPE_PREVIOUS = 18
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ class ScrollViewItem: NSCustomTouchBarItem {
|
||||
stackView.orientation = .horizontal
|
||||
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
|
||||
scrollView.documentView = stackView
|
||||
// scrollView.documentView?.bounds.origin = CGPoint(x: 0.0, y: -2.5)
|
||||
self.view = scrollView
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
extension String {
|
||||
func trim() -> String {
|
||||
@@ -17,6 +18,24 @@ extension String {
|
||||
// ((\s|,)\/\*[\s\S]*?\*\/)|(( |, ")\/\/.*)
|
||||
return self.replacingOccurrences(of: "((\\s|,)\\/\\*[\\s\\S]*?\\*\\/)|(( |, \\\")\\/\\/.*)", with: "", options: .regularExpression)
|
||||
}
|
||||
|
||||
var hexColor: NSColor? {
|
||||
let hex = trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
||||
var int = UInt32()
|
||||
Scanner(string: hex).scanHexInt32(&int)
|
||||
let a, r, g, b: UInt32
|
||||
switch hex.count {
|
||||
case 3: // RGB (12-bit)
|
||||
(r, g, b, a) = ((int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17, 255)
|
||||
case 6: // RGB (24-bit)
|
||||
(r, g, b, a) = (int >> 16, int >> 8 & 0xFF, int & 0xFF, 255)
|
||||
case 8: // ARGB (32-bit)
|
||||
(r, g, b, a) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return NSColor(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
|
||||
}
|
||||
}
|
||||
|
||||
extension NSImage {
|
||||
|
||||
@@ -50,7 +50,7 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
|
||||
static let shared = TouchBarController()
|
||||
|
||||
let touchBar = NSTouchBar()
|
||||
var touchBar: NSTouchBar!
|
||||
|
||||
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
|
||||
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
|
||||
@@ -61,6 +61,15 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
var scrollArea: NSCustomTouchBarItem?
|
||||
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
|
||||
|
||||
var controlStripState: Bool {
|
||||
get {
|
||||
return UserDefaults.standard.bool(forKey: "com.toxblh.mtmr.settings.showControlStrip")
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(!controlStripState, forKey: "com.toxblh.mtmr.settings.showControlStrip")
|
||||
}
|
||||
}
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in self?.dismissTouchBar()}), longAction: .none)
|
||||
@@ -69,6 +78,10 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
}
|
||||
|
||||
func createAndUpdatePreset(jsonItems: [BarItemDefinition]? = nil) {
|
||||
if let oldBar = self.touchBar {
|
||||
NSTouchBar.minimizeSystemModalFunctionBar(oldBar)
|
||||
}
|
||||
self.touchBar = NSTouchBar()
|
||||
var jsonItems = jsonItems
|
||||
self.itemDefinitions = [:]
|
||||
self.items = [:]
|
||||
@@ -110,8 +123,11 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
}
|
||||
|
||||
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(UUID().uuidString)
|
||||
let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString)
|
||||
let identifier = NSTouchBarItem.Identifier(identifierString)
|
||||
itemDefinitions[identifier] = item
|
||||
if item.align == .left {
|
||||
@@ -145,7 +161,16 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
}
|
||||
|
||||
@objc private func presentTouchBar() {
|
||||
NSTouchBar.presentSystemModalFunctionBar(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
|
||||
if self.controlStripState {
|
||||
NSTouchBar.presentSystemModalFunctionBar(touchBar, systemTrayItemIdentifier: .controlStripItem)
|
||||
} else {
|
||||
NSTouchBar.presentSystemModalFunctionBar(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func resetControlStrip() {
|
||||
NSTouchBar.minimizeSystemModalFunctionBar(self.touchBar)
|
||||
presentTouchBar()
|
||||
}
|
||||
|
||||
@objc private func dismissTouchBar() {
|
||||
@@ -201,15 +226,26 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
barItem = InputSourceBarItem(identifier: identifier, onTap: action, onLongTap: longAction)
|
||||
}
|
||||
|
||||
if case .bordered(let bordered)? = item.additionalParameters[.bordered], let item = barItem as? CustomButtonTouchBarItem {
|
||||
item.isBordered = bordered
|
||||
}
|
||||
if case .background(let color)? = item.additionalParameters[.background], let item = barItem as? CustomButtonTouchBarItem {
|
||||
item.backgroundColor = color
|
||||
}
|
||||
if case .width(let value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
|
||||
widthBarItem.setWidth(value: value)
|
||||
}
|
||||
if case .image(let source)? = item.additionalParameters[.image], let item = barItem as? CustomButtonTouchBarItem {
|
||||
let button = item.button!
|
||||
button.image = source.image
|
||||
button.imageScaling = .scaleProportionallyDown
|
||||
button.imagePosition = .imageLeading
|
||||
|
||||
if (item.title == "") {
|
||||
button.imagePosition = .imageOnly
|
||||
}
|
||||
|
||||
button.imageHugsTitle = true
|
||||
button.bezelColor = .clear
|
||||
button.image = source.image
|
||||
}
|
||||
return barItem
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
||||
private var scrubber: NSScrubber!
|
||||
|
||||
private let hf: HapticFeedback = HapticFeedback()
|
||||
|
||||
private var timer: Timer!
|
||||
private var ticks: Int = 0
|
||||
private let minTicks: Int = 5
|
||||
private let maxTicks: Int = 20
|
||||
private var lastSelected: Int = 0
|
||||
|
||||
private var persistentAppIdentifiers: [String] = []
|
||||
private var runningAppsIdentifiers: [String] = []
|
||||
@@ -25,8 +31,6 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
||||
|
||||
private var applications: [DockItem] = []
|
||||
|
||||
private var timeAtPress: NSDate?
|
||||
|
||||
override init(identifier: NSTouchBarItem.Identifier) {
|
||||
super.init(identifier: identifier)
|
||||
|
||||
@@ -35,7 +39,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
||||
scrubber.dataSource = self
|
||||
scrubber.mode = .free // .fixed
|
||||
let layout = NSScrubberFlowLayout();
|
||||
layout.itemSize = NSSize(width: 44, height: 30)
|
||||
layout.itemSize = NSSize(width: 36, height: 32)
|
||||
layout.itemSpacing = 2
|
||||
scrubber.scrubberLayout = layout
|
||||
scrubber.selectionBackgroundStyle = .roundedBackground
|
||||
@@ -94,6 +98,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
||||
|
||||
if let icon = app.icon {
|
||||
item.image = icon
|
||||
item.image.size = NSSize(width: 26, height: 26)
|
||||
}
|
||||
|
||||
item.removeFromSuperview()
|
||||
@@ -105,59 +110,82 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
||||
} else {
|
||||
dotView.layer?.backgroundColor = NSColor.black.cgColor
|
||||
}
|
||||
dotView.layer?.cornerRadius = 2
|
||||
dotView.setFrameOrigin(NSPoint(x: 20, y: 0))
|
||||
dotView.frame.size = NSSize(width: 4, height: 4)
|
||||
dotView.layer?.cornerRadius = 1.5
|
||||
dotView.setFrameOrigin(NSPoint(x: 17, y: 1))
|
||||
dotView.frame.size = NSSize(width: 3, height: 3)
|
||||
item.addSubview(dotView)
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
public func didBeginInteracting(with scrubber: NSScrubber) {
|
||||
timeAtPress = NSDate()
|
||||
stopTimer()
|
||||
self.ticks = 0
|
||||
self.timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(checkTimer), userInfo: nil, repeats: true)
|
||||
}
|
||||
|
||||
@objc private func checkTimer() {
|
||||
self.ticks += 1
|
||||
|
||||
if (self.ticks == minTicks) {
|
||||
hf.tap(strong: 2)
|
||||
}
|
||||
|
||||
if (self.ticks > maxTicks) {
|
||||
stopTimer()
|
||||
hf.tap(strong: 6)
|
||||
}
|
||||
}
|
||||
|
||||
private func stopTimer() {
|
||||
self.timer?.invalidate()
|
||||
self.timer = nil
|
||||
self.lastSelected = 0
|
||||
}
|
||||
|
||||
public func didCancelInteracting(with scrubber: NSScrubber) {
|
||||
timeAtPress = nil
|
||||
stopTimer()
|
||||
}
|
||||
|
||||
|
||||
public func didFinishInteracting(with scrubber: NSScrubber) {
|
||||
let timePressed = NSDate().timeIntervalSince(timeAtPress! as Date)
|
||||
if (timePressed > 0.5 && scrubber.selectedIndex > 0) {
|
||||
self.longPress(with: scrubber)
|
||||
stopTimer()
|
||||
|
||||
if (ticks == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
hf.tap(strong: 6)
|
||||
if (self.ticks >= minTicks && scrubber.selectedIndex > 0) {
|
||||
self.longPress(selected: scrubber.selectedIndex)
|
||||
return
|
||||
}
|
||||
|
||||
let bundleIdentifier = applications[scrubber.selectedIndex].bundleIdentifier
|
||||
if bundleIdentifier!.contains("file://") {
|
||||
NSWorkspace.shared.openFile(bundleIdentifier!.replacingOccurrences(of: "file://", with: ""))
|
||||
} else {
|
||||
NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIdentifier!, options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil)
|
||||
hf.tap(strong: 6)
|
||||
}
|
||||
updateRunningApplication()
|
||||
|
||||
// NB: if you can't open app which on another space, try to check mark
|
||||
// "When switching to an application, switch to a Space with open windows for the application"
|
||||
// in Mission control settings
|
||||
}
|
||||
|
||||
private func longPress(with scrubber: NSScrubber) {
|
||||
let timePressed = NSDate().timeIntervalSince(timeAtPress! as Date)
|
||||
private func longPress(selected: Int) {
|
||||
let bundleIdentifier = applications[selected].bundleIdentifier
|
||||
|
||||
let bundleIdentifier = applications[scrubber.selectedIndex].bundleIdentifier
|
||||
|
||||
if (timePressed > 2.0) {
|
||||
if let processIdentifier = applications[scrubber.selectedIndex].pid {
|
||||
hf.tap(strong: 6)
|
||||
NSRunningApplication(processIdentifier: processIdentifier)?.forceTerminate()
|
||||
if (self.ticks > maxTicks) {
|
||||
if let processIdentifier = applications[selected].pid {
|
||||
if !(NSRunningApplication(processIdentifier: processIdentifier)?.terminate())! {
|
||||
NSRunningApplication(processIdentifier: processIdentifier)?.forceTerminate()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hf.tap(strong: 6)
|
||||
if let index = self.persistentAppIdentifiers.index(of: bundleIdentifier!) {
|
||||
self.persistentAppIdentifiers.remove(at: index)
|
||||
updateRunningApplication()
|
||||
} else {
|
||||
self.persistentAppIdentifiers.append(bundleIdentifier!)
|
||||
}
|
||||
@@ -165,6 +193,8 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
||||
UserDefaults.standard.set(self.persistentAppIdentifiers, forKey: "com.toxblh.mtmr.dock.persistent")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
self.ticks = 0
|
||||
updateRunningApplication()
|
||||
}
|
||||
|
||||
private func launchedApplications() -> [DockItem] {
|
||||
@@ -201,7 +231,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub
|
||||
public func getDockPersistentAppsList() -> [DockItem] {
|
||||
var returnable: [DockItem] = []
|
||||
|
||||
for (index, bundleIdentifier) in persistentAppIdentifiers.enumerated() {
|
||||
for bundleIdentifier in persistentAppIdentifiers {
|
||||
if !self.runningAppsIdentifiers.contains(bundleIdentifier) {
|
||||
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier))
|
||||
returnable.append(dockItem)
|
||||
|
||||
@@ -10,20 +10,28 @@ import IOKit.ps
|
||||
import Foundation
|
||||
|
||||
class BatteryBarItem: CustomButtonTouchBarItem {
|
||||
private var timer: Timer!
|
||||
private let batteryInfo = BatteryInfo()
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
|
||||
super.init(identifier: identifier, title: " ", onTap: onTap, onLongTap: onLongTap)
|
||||
self.view = button
|
||||
|
||||
let batteryInfo = BatteryInfo(button: button)
|
||||
batteryInfo.start()
|
||||
batteryInfo.updateInfo()
|
||||
batteryInfo.start { [weak self] in
|
||||
self?.refresh()
|
||||
}
|
||||
self.refresh()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func refresh() {
|
||||
self.attributedTitle = self.batteryInfo.formattedInfo()
|
||||
}
|
||||
|
||||
deinit {
|
||||
batteryInfo.stop()
|
||||
}
|
||||
}
|
||||
|
||||
class BatteryInfo: NSObject {
|
||||
@@ -34,18 +42,11 @@ class BatteryInfo: NSObject {
|
||||
var isCharging: Bool = false
|
||||
var ACPower: String = ""
|
||||
var timeRemaining: String = ""
|
||||
|
||||
var button: NSButton?
|
||||
var notifyBlock: ()->() = {}
|
||||
var loop:CFRunLoopSource?
|
||||
|
||||
init(button: NSButton) {
|
||||
super.init()
|
||||
|
||||
self.button = button
|
||||
self.start()
|
||||
}
|
||||
|
||||
func start() {
|
||||
func start(notifyBlock: @escaping ()->()) {
|
||||
self.notifyBlock = notifyBlock
|
||||
let opaque = Unmanaged.passRetained(self).toOpaque()
|
||||
let context = UnsafeMutableRawPointer(opaque)
|
||||
loop = IOPSNotificationCreateRunLoopSource({ (context) in
|
||||
@@ -54,16 +55,17 @@ class BatteryInfo: NSObject {
|
||||
}
|
||||
|
||||
let watcher = Unmanaged<BatteryInfo>.fromOpaque(ctx).takeUnretainedValue()
|
||||
watcher.updateInfo()
|
||||
}, context).takeRetainedValue() as CFRunLoopSource
|
||||
watcher.notifyBlock()
|
||||
}, context).takeRetainedValue() as CFRunLoopSource
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
|
||||
}
|
||||
|
||||
func stop() {
|
||||
if !(self.loop != nil) {
|
||||
self.notifyBlock = {}
|
||||
guard let loop = self.loop else {
|
||||
return
|
||||
}
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), self.loop, CFRunLoopMode.defaultMode)
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
|
||||
self.loop = nil
|
||||
}
|
||||
|
||||
@@ -108,28 +110,38 @@ class BatteryInfo: NSObject {
|
||||
|
||||
func getFormattedTime(time: Int) -> String {
|
||||
if (time > 0) {
|
||||
let timeFormatted = NSString(format: " (%d:%02d)", time / 60, time % 60) as String
|
||||
let timeFormatted = NSString(format: " %d:%02d", time / 60, time % 60) as String
|
||||
return timeFormatted
|
||||
} else if (time == 0) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return " (?)"
|
||||
return ""
|
||||
}
|
||||
|
||||
public func updateInfo() {
|
||||
public func formattedInfo() -> NSAttributedString {
|
||||
var title = ""
|
||||
self.getPSInfo()
|
||||
|
||||
if ACPower == "AC Power" {
|
||||
title += "⚡️"
|
||||
if current < 100 {
|
||||
title += "⚡️"
|
||||
}
|
||||
timeRemaining = getFormattedTime(time: timeToFull)
|
||||
} else {
|
||||
timeRemaining = getFormattedTime(time: timeToEmpty)
|
||||
}
|
||||
|
||||
title += String(current) + "%" + timeRemaining
|
||||
button?.title = title
|
||||
title += String(current) + "%"
|
||||
|
||||
var color = NSColor.white
|
||||
if current <= 10 && ACPower != "AC Power" {
|
||||
color = NSColor.red
|
||||
}
|
||||
|
||||
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: NSFont.systemFont(ofSize: 15), .baselineOffset: 1])
|
||||
let newTitleSecond = NSMutableAttributedString(string: timeRemaining as String, attributes: [NSAttributedStringKey.foregroundColor: color, NSAttributedStringKey.font: NSFont.systemFont(ofSize: 8, weight: .regular), NSAttributedStringKey.baselineOffset: 7])
|
||||
newTitle.append(newTitleSecond)
|
||||
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
|
||||
return newTitle
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import Cocoa
|
||||
import CoreLocation
|
||||
|
||||
class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
private let activity = NSBackgroundActivityScheduler(identifier: "com.toxblh.mtmr.currency.updatecheck")
|
||||
private let activity: NSBackgroundActivityScheduler
|
||||
private var prefix: String
|
||||
private var from: String
|
||||
private var to: String
|
||||
@@ -37,6 +37,7 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
]
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
|
||||
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||
activity.interval = interval
|
||||
self.from = from
|
||||
self.to = to
|
||||
@@ -71,7 +72,6 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
if error == nil {
|
||||
do {
|
||||
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
|
||||
// print(json)
|
||||
var value: Float32!
|
||||
|
||||
if let data_array = json["data"] as? [String : AnyObject] {
|
||||
@@ -107,14 +107,11 @@ class CurrencyBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
self.oldValue = value
|
||||
|
||||
button.title = String(format: "%@%.2f", self.prefix, value)
|
||||
let title = String(format: "%@%.2f", self.prefix, value)
|
||||
|
||||
let textRange = NSRange(location: 0, length: button.title.count)
|
||||
let newTitle = NSMutableAttributedString(string: button.title)
|
||||
newTitle.addAttribute(NSAttributedStringKey.foregroundColor, value: color, range: textRange)
|
||||
newTitle.addAttribute(NSAttributedStringKey.font, value: button.font!, range: textRange)
|
||||
newTitle.setAlignment(.center, range: textRange)
|
||||
|
||||
button.attributedTitle = newTitle
|
||||
let regularFont = self.attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
|
||||
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont])
|
||||
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
|
||||
self.attributedTitle = newTitle
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import Cocoa
|
||||
class InputSourceBarItem: CustomButtonTouchBarItem {
|
||||
|
||||
fileprivate var notificationCenter: CFNotificationCenter
|
||||
let buttonSize = NSSize(width: 21, height: 21)
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
|
||||
notificationCenter = CFNotificationCenterGetDistributedCenter();
|
||||
@@ -19,12 +20,25 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
|
||||
observeIputSourceChangedNotification();
|
||||
textInputSourceDidChange()
|
||||
|
||||
self.button.cell?.action = #selector(switchInputSource)
|
||||
self.button.action = #selector(switchInputSource)
|
||||
|
||||
self.button.frame.size = buttonSize
|
||||
self.button.bounds.size = buttonSize
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()));
|
||||
}
|
||||
|
||||
@objc override func handleGestureSingle(gr: NSClickGestureRecognizer) {
|
||||
super.handleGestureSingle(gr: gr)
|
||||
switchInputSource()
|
||||
}
|
||||
|
||||
@objc public func textInputSourceDidChange() {
|
||||
let currentSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
|
||||
@@ -42,9 +56,11 @@ class InputSourceBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
|
||||
if (iconImage != nil) {
|
||||
self.button.image = iconImage
|
||||
self.button.cell?.image = iconImage
|
||||
self.button.cell?.image?.size = buttonSize
|
||||
self.title = ""
|
||||
} else {
|
||||
self.button.title = currentSource.name
|
||||
self.title = currentSource.name
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
|
||||
super.init(identifier: identifier, title: " ", onTap: onTap, onLongTap: onLongTap)
|
||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
|
||||
self.view = button
|
||||
button.bezelColor = .clear
|
||||
updateTime()
|
||||
}
|
||||
|
||||
@@ -18,7 +19,7 @@ class TimeTouchBarItem: CustomButtonTouchBarItem {
|
||||
}
|
||||
|
||||
@objc func updateTime() {
|
||||
button.title = self.dateFormatter.string(from: Date())
|
||||
self.title = self.dateFormatter.string(from: Date())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import Cocoa
|
||||
import CoreLocation
|
||||
|
||||
class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||
private let activity = NSBackgroundActivityScheduler(identifier: "com.toxblh.mtmr.weather.updatecheck")
|
||||
private let activity: NSBackgroundActivityScheduler
|
||||
private var units: String
|
||||
private var api_key: String
|
||||
private var units_str = "°F"
|
||||
@@ -23,6 +23,7 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||
private var manager:CLLocationManager!
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text", onTap: @escaping () -> (), onLongTap: @escaping () -> ()) {
|
||||
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||
activity.interval = interval
|
||||
self.units = units
|
||||
self.api_key = api_key
|
||||
@@ -116,7 +117,7 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||
}
|
||||
|
||||
func setWeather(text: String) {
|
||||
button.title = text
|
||||
self.title = text
|
||||
}
|
||||
|
||||
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
{ "type": "volumeDown", "width": 34, "align": "right" },
|
||||
{ "type": "volume", "width": 60, "align": "right" },
|
||||
{ "type": "volumeUp", "width": 34, "align": "right" },
|
||||
{ "type": "inputsource", "align": "right" },
|
||||
{ "type": "battery", "align": "right" },
|
||||
{ "type": "timeButton", "align": "right" }
|
||||
]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import XCTest
|
||||
|
||||
class AppleScriptDefinitionTests: XCTestCase {
|
||||
|
||||
func testInline() {
|
||||
let buttonNoActionFixture = """
|
||||
[ { "type": "appleScriptTitledButton", "source": { "inline": "tell everything fine" } } ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
|
||||
guard case .appleScriptTitledButton(let source, _)? = result?.first?.type else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(source.string, "tell everything fine")
|
||||
}
|
||||
|
||||
func testPath() {
|
||||
let buttonNoActionFixture = """
|
||||
[ { "type": "appleScriptTitledButton", "source": { "filePath": "/ololo/pew" } } ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
|
||||
guard case .appleScriptTitledButton(let source, _)? = result?.first?.type else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
let sourceStruct = source as? Source
|
||||
XCTAssertEqual(sourceStruct?.filePath, "/ololo/pew")
|
||||
}
|
||||
|
||||
func testRefreshInterval() {
|
||||
let buttonNoActionFixture = """
|
||||
[ { "type": "appleScriptTitledButton", "source": { "inline": "tell everything fine" }, "refreshInterval": 305} ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
|
||||
guard case .appleScriptTitledButton(_, 305)? = result?.first?.type else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import XCTest
|
||||
|
||||
class BackgroundColorTests: XCTestCase {
|
||||
|
||||
func testOpaque() {
|
||||
let buttonNoActionFixture = """
|
||||
[ { "type": "staticButton", "title": "Pew", "background": "#FF0000" } ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
|
||||
guard case .background(let color)? = result?.first?.additionalParameters[.background] else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(color, .red)
|
||||
}
|
||||
|
||||
func testAlpha() {
|
||||
let buttonNoActionFixture = """
|
||||
[ { "type": "staticButton", "title": "Pew", "background": "#FF000080" } ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
|
||||
guard case .background(let color)? = result?.first?.additionalParameters[.background] else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(color.alphaComponent, 0.5, accuracy: 0.01)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import XCTest
|
||||
@testable import MTMR
|
||||
|
||||
class ParseConfig: XCTestCase {
|
||||
|
||||
@@ -8,8 +7,14 @@ class ParseConfig: XCTestCase {
|
||||
[ { "type": "staticButton", "title": "Pew" } ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture)
|
||||
XCTAssertEqual(result?.first?.type, .staticButton(title: "Pew"))
|
||||
XCTAssertEqual(result?.first?.action, .some(.none))
|
||||
guard case .staticButton("Pew")? = result?.first?.type else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
guard case .none? = result?.first?.action else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func testButtonKeyCodeAction() {
|
||||
@@ -17,17 +22,48 @@ class ParseConfig: XCTestCase {
|
||||
[ { "type": "staticButton", "title": "Pew", "action": "hidKey", "keycode": 123} ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture)
|
||||
XCTAssertEqual(result?.first?.type, .staticButton(title: "Pew"))
|
||||
XCTAssertEqual(result?.first?.action, .hidKey(keycode: 123))
|
||||
guard case .staticButton("Pew")? = result?.first?.type else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
guard case .hidKey(keycode: 123)? = result?.first?.action else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func testPredefinedItem() {
|
||||
let buttonKeycodeFixture = """
|
||||
[ { "type": "brightnessUp" } ]
|
||||
[ { "type": "escape" } ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture)
|
||||
XCTAssertEqual(result?.first?.type, .staticButton(title: "🔆"))
|
||||
XCTAssertEqual(result?.first?.action, .keyPress(keycode: 113))
|
||||
guard case .staticButton("esc")? = result?.first?.type else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
guard case .keyPress(keycode: 53)? = result?.first?.action else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func testExtendedWidthForPredefinedItem() {
|
||||
let buttonKeycodeFixture = """
|
||||
[ { "type": "escape", "width": 110}, ]
|
||||
""".data(using: .utf8)!
|
||||
let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture)
|
||||
guard case .staticButton("esc")? = result?.first?.type else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
guard case .keyPress(keycode: 53)? = result?.first?.action else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
guard case .width(110)? = result?.first?.additionalParameters[.width] else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
# My TouchBar. My rules
|
||||
[](https://github.com/Toxblh/MTMR/releases)
|
||||
[](https://github.com/Toxblh/MTMR/blob/master/LICENSE) [](https://github.com/Toxblh/MTMR/releases/latest)  
|
||||
|
||||
<p align="center">
|
||||
<img src="Resources/TouchBar-v0.8.1.png">
|
||||
@@ -34,7 +35,7 @@ My the idea is to create the program like a platform for plugins for customizati
|
||||
- [ ] System for autoupdate (maybe https://sparkle-project.org/)
|
||||
|
||||
Settings:
|
||||
- [ ] Intarface for plugins and export like presets
|
||||
- [ ] Interface for plugins and export like presets
|
||||
- [ ] Startup at login
|
||||
- [ ] Show on/off in Dock
|
||||
- [ ] Show on/off in StatusBar
|
||||
@@ -45,9 +46,8 @@ Maybe:
|
||||
|
||||
|
||||
## Installation
|
||||
1. Download last release [Releases](https://github.com/Toxblh/MTMR/releases)
|
||||
3. Open MTMR
|
||||
4. Open preset `open ~/Library/Application\ Support/MTMR/items.json` and customize it. Restart MTMR to apply changes.
|
||||
- Download last [release](https://github.com/Toxblh/MTMR/releases)
|
||||
- Or via Homebrew `brew cask install mtmr`
|
||||
|
||||
## Preset
|
||||
|
||||
@@ -68,6 +68,7 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
||||
- battery
|
||||
- currency
|
||||
- weather
|
||||
- inputsource
|
||||
|
||||
> Media Keys
|
||||
- previous
|
||||
@@ -167,7 +168,7 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
||||
"url": "https://google.com",
|
||||
```
|
||||
|
||||
## Additional paramaters:
|
||||
## Additional parameters:
|
||||
|
||||
- `width` allow to restrict how much room a particular button will take
|
||||
```json
|
||||
@@ -245,4 +246,6 @@ File for customize your preset for MTMR: `open ~/Library/Application\ Support/MT
|
||||
|
||||
## Credits
|
||||
|
||||
<a href="https://www.buymeacoffee.com/toxblh" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: auto !important;width: auto !important;" ></a>
|
||||
|
||||
Built by [@Toxblh](https://patreon.com/toxblh) and [@ReDetection](http://patreon.com/ReDetection).
|
||||
|
||||
@@ -16,8 +16,11 @@ cd Release
|
||||
rm -r App.xcarchive
|
||||
|
||||
# Prerequisite: npm i -g create-dmg
|
||||
create-dmg "${NAME}.app"
|
||||
NAME_DMG="${NAME}.app"
|
||||
echo $NAME_DMG
|
||||
create-dmg $NAME_DMG
|
||||
|
||||
VERSION=`mdls -raw -name kMDItemVersion ${NAME}.app`
|
||||
|
||||
VERSION=`mdls -raw -name kMDItemVersion MTMR.app`
|
||||
echo $VERSION
|
||||
zip -r "${NAME}v${VERSION}.zip" "${NAME}.app"
|
||||
|
||||
Reference in New Issue
Block a user