From e44ff00f3b16d674c9f162be3de4823ec740aebc Mon Sep 17 00:00:00 2001 From: Serg Date: Sat, 12 Oct 2019 12:54:48 +0700 Subject: [PATCH 1/2] support for global settings. haptic feedback as the first one resolves #185 --- MTMR/CustomButtonTouchBarItem.swift | 6 ++--- MTMR/HapticFeedback.swift | 2 +- MTMR/ItemsParsing.swift | 20 ++++++++++++++-- MTMR/TouchBarController.swift | 14 +++++++++-- MTMR/Widgets/AppScrubberTouchBarItem.swift | 8 +++---- MTMRTests/ParseConfigTests.swift | 28 ++++++++++++++++++++++ 6 files changed, 66 insertions(+), 12 deletions(-) diff --git a/MTMR/CustomButtonTouchBarItem.swift b/MTMR/CustomButtonTouchBarItem.swift index 238d0cd..5ae98de 100644 --- a/MTMR/CustomButtonTouchBarItem.swift +++ b/MTMR/CustomButtonTouchBarItem.swift @@ -176,12 +176,12 @@ class CustomButtonCell: NSButtonCell { class HapticClickGestureRecognizer: NSClickGestureRecognizer { override func touchesBegan(with event: NSEvent) { - HapticFeedback.shared.tap(strong: 2) + HapticFeedback.shared?.tap(strong: 2) super.touchesBegan(with: event) } override func touchesEnded(with event: NSEvent) { - HapticFeedback.shared.tap(strong: 1) + HapticFeedback.shared?.tap(strong: 1) super.touchesEnded(with: event) } } @@ -226,7 +226,7 @@ class LongPressGestureRecognizer: NSPressGestureRecognizer { @objc private func onTimer() { if let target = self.target, let action = self.action { target.performSelector(onMainThread: action, with: self, waitUntilDone: false) - HapticFeedback.shared.tap(strong: 6) + HapticFeedback.shared?.tap(strong: 6) } } diff --git a/MTMR/HapticFeedback.swift b/MTMR/HapticFeedback.swift index 7bd0b20..38a84d0 100644 --- a/MTMR/HapticFeedback.swift +++ b/MTMR/HapticFeedback.swift @@ -9,7 +9,7 @@ import IOKit class HapticFeedback { - static let shared = HapticFeedback() + static var shared: HapticFeedback? // Here we have list of possible IDs for Haptic Generator Device. They are not constant // To find deviceID, you will need IORegistryExplorer app from Additional Tools for Xcode dmg diff --git a/MTMR/ItemsParsing.swift b/MTMR/ItemsParsing.swift index 8ed9452..d62ae98 100644 --- a/MTMR/ItemsParsing.swift +++ b/MTMR/ItemsParsing.swift @@ -2,11 +2,27 @@ import AppKit import Foundation extension Data { - func barItemDefinitions() -> [BarItemDefinition]? { - return try? JSONDecoder().decode([BarItemDefinition].self, from: utf8string!.stripComments().data(using: .utf8)!) + func mtmrPreset() -> Preset? { + let data = self.utf8string!.stripComments().data(using: .utf8)! + guard let preset = try? JSONDecoder().decode(Preset.self, from: data) else { + if let oldFormat = try? JSONDecoder().decode([BarItemDefinition].self, from: data) { + return Preset(settings: nil, barItems: oldFormat) + } + return nil + } + return preset } } +struct Preset: Decodable { + let settings: GlobalSettings? + let barItems: [BarItemDefinition] +} + +struct GlobalSettings: Decodable { + let hapticFeedback: Bool? +} + struct BarItemDefinition: Decodable { let type: ItemType let action: ActionType diff --git a/MTMR/TouchBarController.swift b/MTMR/TouchBarController.swift index b0c5578..118f0fd 100644 --- a/MTMR/TouchBarController.swift +++ b/MTMR/TouchBarController.swift @@ -172,8 +172,18 @@ class TouchBarController: NSObject, NSTouchBarDelegate { 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) + let preset = path.fileData?.mtmrPreset() ?? fallbackPreset() + applySettings(preset.settings ?? GlobalSettings(hapticFeedback: true)) + createAndUpdatePreset(newJsonItems: preset.barItems) + } + + func fallbackPreset() -> Preset { + let items = [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])] + return Preset(settings: nil, barItems: items) + } + + func applySettings(_ settings: GlobalSettings) { + HapticFeedback.shared = settings.hapticFeedback ?? true ? HapticFeedback() : nil } func loadItemDefinitions(jsonItems: [BarItemDefinition]) { diff --git a/MTMR/Widgets/AppScrubberTouchBarItem.swift b/MTMR/Widgets/AppScrubberTouchBarItem.swift index 5ddfb50..51043da 100644 --- a/MTMR/Widgets/AppScrubberTouchBarItem.swift +++ b/MTMR/Widgets/AppScrubberTouchBarItem.swift @@ -146,12 +146,12 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub ticks += 1 if ticks == minTicks { - HapticFeedback.shared.tap(strong: 2) + HapticFeedback.shared?.tap(strong: 2) } if ticks > maxTicks { stopTimer() - HapticFeedback.shared.tap(strong: 6) + HapticFeedback.shared?.tap(strong: 6) } } @@ -182,7 +182,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub NSWorkspace.shared.openFile(bundleIdentifier!.replacingOccurrences(of: "file://", with: "")) } else { NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIdentifier!, options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil) - HapticFeedback.shared.tap(strong: 6) + HapticFeedback.shared?.tap(strong: 6) } updateRunningApplication() @@ -201,7 +201,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub } } } else { - HapticFeedback.shared.tap(strong: 6) + HapticFeedback.shared?.tap(strong: 6) if let index = self.persistentAppIdentifiers.index(of: bundleIdentifier!) { persistentAppIdentifiers.remove(at: index) } else { diff --git a/MTMRTests/ParseConfigTests.swift b/MTMRTests/ParseConfigTests.swift index a3321cc..5e674f4 100644 --- a/MTMRTests/ParseConfigTests.swift +++ b/MTMRTests/ParseConfigTests.swift @@ -64,4 +64,32 @@ class ParseConfig: XCTestCase { return } } + + func testParsesOldFormat() { + let fixture = """ + [ { "type": "escape" } ] + """.data(using: .utf8)! + let result = fixture.mtmrPreset() + XCTAssertEqual(result?.barItems.count, 1) + guard case .staticButton("esc")? = result?.barItems.first?.type else { + XCTFail() + return + } + } + + func testParsesHapticFeedbackSettings() { + let fixture = """ + { + "settings": { "hapticFeedback": false }, + "barItems": [ { "type": "escape" } ] + } + """.data(using: .utf8)! + let result = fixture.mtmrPreset() + XCTAssertEqual(result?.barItems.count, 1) + guard case .staticButton("esc")? = result?.barItems.first?.type else { + XCTFail() + return + } + XCTAssertEqual(result?.settings?.hapticFeedback, .some(false)) + } } From 41a9544c3a047fd8337079448c0d3feb8ce22663 Mon Sep 17 00:00:00 2001 From: Toxblh Date: Sat, 12 Oct 2019 13:04:28 +0100 Subject: [PATCH 2/2] More consistent settings --- MTMR/AppDelegate.swift | 15 +++++++++++++++ MTMR/Info.plist | 2 +- MTMR/ItemsParsing.swift | 20 ++------------------ MTMR/TouchBarController.swift | 23 +++++++++++------------ MTMRTests/ParseConfigTests.swift | 28 ---------------------------- 5 files changed, 29 insertions(+), 59 deletions(-) diff --git a/MTMR/AppDelegate.swift b/MTMR/AppDelegate.swift index d6adfe5..08932de 100644 --- a/MTMR/AppDelegate.swift +++ b/MTMR/AppDelegate.swift @@ -25,6 +25,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true] as NSDictionary) TouchBarController.shared.setupControlStripPresence() + HapticFeedbackUpdate() if let button = statusItem.button { button.image = #imageLiteral(resourceName: "StatusImage") @@ -40,6 +41,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationWillTerminate(_: Notification) {} + func HapticFeedbackUpdate() { + HapticFeedback.shared = TouchBarController.shared.hapticFeedbackState ? HapticFeedback() : nil + } + @objc func updateIsBlockedApp() { var blacklistAppIdentifiers: [String] = [] if let blackListed = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.blackListedApps") { @@ -90,6 +95,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } + @objc func toggleHapticFeedback(_: Any?) { + TouchBarController.shared.hapticFeedbackState = !TouchBarController.shared.hapticFeedbackState + HapticFeedbackUpdate() + createMenu() + } + @objc func openPreset(_: Any?) { let dialog = NSOpenPanel() @@ -124,6 +135,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { let hideControlStrip = NSMenuItem(title: "Hide Control Strip", action: #selector(toggleControlStrip(_:)), keyEquivalent: "T") hideControlStrip.state = TouchBarController.shared.showControlStripState ? .off : .on + let hapticFeedback = NSMenuItem(title: "Haptic Feedback", action: #selector(toggleHapticFeedback(_:)), keyEquivalent: "H") + hapticFeedback.state = TouchBarController.shared.hapticFeedbackState ? .on : .off + let settingSeparator = NSMenuItem(title: "Settings", action: nil, keyEquivalent: "") settingSeparator.isEnabled = false @@ -133,6 +147,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { menu.addItem(NSMenuItem.separator()) menu.addItem(settingSeparator) + menu.addItem(hapticFeedback) menu.addItem(hideControlStrip) menu.addItem(toggleBlackList) menu.addItem(startAtLogin) diff --git a/MTMR/Info.plist b/MTMR/Info.plist index 6a68148..2fef6e1 100644 --- a/MTMR/Info.plist +++ b/MTMR/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 0.23.1 CFBundleVersion - 278 + 294 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/MTMR/ItemsParsing.swift b/MTMR/ItemsParsing.swift index d62ae98..ac888da 100644 --- a/MTMR/ItemsParsing.swift +++ b/MTMR/ItemsParsing.swift @@ -2,27 +2,11 @@ import AppKit import Foundation extension Data { - func mtmrPreset() -> Preset? { - let data = self.utf8string!.stripComments().data(using: .utf8)! - guard let preset = try? JSONDecoder().decode(Preset.self, from: data) else { - if let oldFormat = try? JSONDecoder().decode([BarItemDefinition].self, from: data) { - return Preset(settings: nil, barItems: oldFormat) - } - return nil - } - return preset + func barItemDefinitions() -> [BarItemDefinition]? { + return try? JSONDecoder().decode([BarItemDefinition].self, from: utf8string!.stripComments().data(using: .utf8)!) } } -struct Preset: Decodable { - let settings: GlobalSettings? - let barItems: [BarItemDefinition] -} - -struct GlobalSettings: Decodable { - let hapticFeedback: Bool? -} - struct BarItemDefinition: Decodable { let type: ItemType let action: ActionType diff --git a/MTMR/TouchBarController.swift b/MTMR/TouchBarController.swift index 118f0fd..7e38e39 100644 --- a/MTMR/TouchBarController.swift +++ b/MTMR/TouchBarController.swift @@ -89,6 +89,15 @@ class TouchBarController: NSObject, NSTouchBarDelegate { UserDefaults.standard.set(newValue, forKey: "com.toxblh.mtmr.settings.showControlStrip") } } + + var hapticFeedbackState: Bool { + get { + return UserDefaults.standard.bool(forKey: "com.toxblh.mtmr.settings.hapticFeedback") + } + set { + UserDefaults.standard.set(newValue, forKey: "com.toxblh.mtmr.settings.hapticFeedback") + } + } var blacklistAppIdentifiers: [String] = [] var frontmostApplicationIdentifier: String? { @@ -172,18 +181,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate { func reloadPreset(path: String) { lastPresetPath = path - let preset = path.fileData?.mtmrPreset() ?? fallbackPreset() - applySettings(preset.settings ?? GlobalSettings(hapticFeedback: true)) - createAndUpdatePreset(newJsonItems: preset.barItems) - } - - func fallbackPreset() -> Preset { - let items = [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])] - return Preset(settings: nil, barItems: items) - } - - func applySettings(_ settings: GlobalSettings) { - HapticFeedback.shared = settings.hapticFeedback ?? true ? HapticFeedback() : nil + let items = path.fileData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])] + createAndUpdatePreset(newJsonItems: items) } func loadItemDefinitions(jsonItems: [BarItemDefinition]) { diff --git a/MTMRTests/ParseConfigTests.swift b/MTMRTests/ParseConfigTests.swift index 5e674f4..a3321cc 100644 --- a/MTMRTests/ParseConfigTests.swift +++ b/MTMRTests/ParseConfigTests.swift @@ -64,32 +64,4 @@ class ParseConfig: XCTestCase { return } } - - func testParsesOldFormat() { - let fixture = """ - [ { "type": "escape" } ] - """.data(using: .utf8)! - let result = fixture.mtmrPreset() - XCTAssertEqual(result?.barItems.count, 1) - guard case .staticButton("esc")? = result?.barItems.first?.type else { - XCTFail() - return - } - } - - func testParsesHapticFeedbackSettings() { - let fixture = """ - { - "settings": { "hapticFeedback": false }, - "barItems": [ { "type": "escape" } ] - } - """.data(using: .utf8)! - let result = fixture.mtmrPreset() - XCTAssertEqual(result?.barItems.count, 1) - guard case .staticButton("esc")? = result?.barItems.first?.type else { - XCTFail() - return - } - XCTAssertEqual(result?.settings?.hapticFeedback, .some(false)) - } }