From 11530ef1806b5fd396968430f94a4b986eba4237 Mon Sep 17 00:00:00 2001 From: bobrosoft Date: Thu, 1 Aug 2019 12:25:59 +0200 Subject: [PATCH 1/5] CustomButtonTouchBarItem: recognise longPress in more expected manner without long wait and requirement to release the button --- MTMR/CustomButtonTouchBarItem.swift | 69 +++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/MTMR/CustomButtonTouchBarItem.swift b/MTMR/CustomButtonTouchBarItem.swift index a61a508..d218451 100644 --- a/MTMR/CustomButtonTouchBarItem.swift +++ b/MTMR/CustomButtonTouchBarItem.swift @@ -10,11 +10,15 @@ import Cocoa class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate { var tapClosure: (() -> Void)? - var longTapClosure: (() -> Void)? + var longTapClosure: (() -> Void)? { + didSet { + longClick.isEnabled = longTapClosure != nil + } + } private var button: NSButton! private var singleClick: HapticClickGestureRecognizer! - private var longClick: NSPressGestureRecognizer! + private var longClick: LongPressGestureRecognizer! init(identifier: NSTouchBarItem.Identifier, title: String) { attributedTitle = title.defaultTouchbarAttributedString @@ -22,7 +26,8 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat super.init(identifier: identifier) button = CustomHeightButton(title: title, target: nil, action: nil) - longClick = NSPressGestureRecognizer(target: self, action: #selector(handleGestureLong)) + longClick = LongPressGestureRecognizer(target: self, action: #selector(handleGestureLong)) + longClick.isEnabled = false longClick.allowedTouchTypes = .direct longClick.delegate = self @@ -97,7 +102,9 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat } func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool { - if gestureRecognizer == singleClick && otherGestureRecognizer == longClick { + if gestureRecognizer == singleClick && otherGestureRecognizer == longClick + || gestureRecognizer == longClick && otherGestureRecognizer == singleClick // need it + { return false } return true @@ -115,15 +122,8 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat @objc func handleGestureLong(gr: NSPressGestureRecognizer) { switch gr.state { - case .began: - if let closure = self.longTapClosure { - HapticFeedback.shared.tap(strong: 2) - closure() - } else if let closure = self.tapClosure { - HapticFeedback.shared.tap(strong: 6) - closure() - print("long click") - } + case .possible: // tiny hack because we're calling action manually + (self.longTapClosure ?? self.tapClosure)?() break default: break @@ -181,6 +181,49 @@ class HapticClickGestureRecognizer: NSClickGestureRecognizer { } } +class LongPressGestureRecognizer: NSPressGestureRecognizer { + private let recognizeTimeout = 0.4 + private var timer: Timer? + + override func touchesBegan(with event: NSEvent) { + timerInvalidate() + + let touches = event.touches(for: self.view!) + if touches.count == 1 { // to prevent it for built-in two/three-finger gestures + timer = Timer.scheduledTimer(withTimeInterval: recognizeTimeout, repeats: false) { _ in + if let target = self.target, let action = self.action { + target.performSelector(inBackground: action, with: self) + HapticFeedback.shared.tap(strong: 6) + } + } + } + + super.touchesBegan(with: event) + } + + override func touchesMoved(with event: NSEvent) { + timerInvalidate() // to prevent it for built-in two/three-finger gestures + super.touchesMoved(with: event) + } + + override func touchesCancelled(with event: NSEvent) { + timerInvalidate() + super.touchesCancelled(with: event) + } + + override func touchesEnded(with event: NSEvent) { + timerInvalidate() + super.touchesEnded(with: event) + } + + private func timerInvalidate() { + if let timer = timer { + timer.invalidate() + self.timer = nil + } + } +} + extension String { var defaultTouchbarAttributedString: NSAttributedString { let attrTitle = NSMutableAttributedString(string: self, attributes: [.foregroundColor: NSColor.white, .font: NSFont.systemFont(ofSize: 15, weight: .regular), .baselineOffset: 1]) From 229c55c367e3e39f1f286679548d5b33be926f37 Mon Sep 17 00:00:00 2001 From: bobrosoft Date: Fri, 2 Aug 2019 14:50:55 +0200 Subject: [PATCH 2/5] CustomButtonTouchBarItem: try to fix strange app hanging --- MTMR/CustomButtonTouchBarItem.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/MTMR/CustomButtonTouchBarItem.swift b/MTMR/CustomButtonTouchBarItem.swift index d218451..c9d4306 100644 --- a/MTMR/CustomButtonTouchBarItem.swift +++ b/MTMR/CustomButtonTouchBarItem.swift @@ -190,9 +190,9 @@ class LongPressGestureRecognizer: NSPressGestureRecognizer { let touches = event.touches(for: self.view!) if touches.count == 1 { // to prevent it for built-in two/three-finger gestures - timer = Timer.scheduledTimer(withTimeInterval: recognizeTimeout, repeats: false) { _ in - if let target = self.target, let action = self.action { - target.performSelector(inBackground: action, with: self) + timer = Timer.scheduledTimer(withTimeInterval: recognizeTimeout, repeats: false) { [weak self] _ in + if let _self = self, let target = self?.target, let action = self?.action { + target.performSelector(onMainThread: action, with: _self, waitUntilDone: false) HapticFeedback.shared.tap(strong: 6) } } @@ -222,6 +222,10 @@ class LongPressGestureRecognizer: NSPressGestureRecognizer { self.timer = nil } } + + deinit { + timerInvalidate() + } } extension String { From 63e3de7313f9327750b7f178a05a437579c48f59 Mon Sep 17 00:00:00 2001 From: bobrosoft Date: Tue, 6 Aug 2019 15:03:01 +0200 Subject: [PATCH 3/5] CustomButtonTouchBarItem: try to fix strange app hanging (attempt 2) --- MTMR/CustomButtonTouchBarItem.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/MTMR/CustomButtonTouchBarItem.swift b/MTMR/CustomButtonTouchBarItem.swift index c9d4306..428ca05 100644 --- a/MTMR/CustomButtonTouchBarItem.swift +++ b/MTMR/CustomButtonTouchBarItem.swift @@ -190,12 +190,7 @@ class LongPressGestureRecognizer: NSPressGestureRecognizer { let touches = event.touches(for: self.view!) if touches.count == 1 { // to prevent it for built-in two/three-finger gestures - timer = Timer.scheduledTimer(withTimeInterval: recognizeTimeout, repeats: false) { [weak self] _ in - if let _self = self, let target = self?.target, let action = self?.action { - target.performSelector(onMainThread: action, with: _self, waitUntilDone: false) - HapticFeedback.shared.tap(strong: 6) - } - } + timer = Timer.scheduledTimer(timeInterval: 0.25, target: self, selector: #selector(self.onTimer), userInfo: nil, repeats: false) } super.touchesBegan(with: event) @@ -223,6 +218,13 @@ 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) + } + } + deinit { timerInvalidate() } From 2d64c091e3a092667af91cab083c869a61f75be6 Mon Sep 17 00:00:00 2001 From: bobrosoft Date: Tue, 6 Aug 2019 16:37:57 +0200 Subject: [PATCH 4/5] fix: need to properly stop periodic timer/activity on destroy to eliminate possible memory leaks --- MTMR/Widgets/CurrencyBarItem.swift | 4 ++++ MTMR/Widgets/WeatherBarItem.swift | 4 ++++ MTMR/Widgets/YandexWeatherBarItem.swift | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/MTMR/Widgets/CurrencyBarItem.swift b/MTMR/Widgets/CurrencyBarItem.swift index 1d1bbde..dfe3479 100644 --- a/MTMR/Widgets/CurrencyBarItem.swift +++ b/MTMR/Widgets/CurrencyBarItem.swift @@ -127,4 +127,8 @@ class CurrencyBarItem: CustomButtonTouchBarItem { newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count)) attributedTitle = newTitle } + + deinit { + activity.invalidate() + } } diff --git a/MTMR/Widgets/WeatherBarItem.swift b/MTMR/Widgets/WeatherBarItem.swift index 711e00a..61ffeac 100644 --- a/MTMR/Widgets/WeatherBarItem.swift +++ b/MTMR/Widgets/WeatherBarItem.swift @@ -135,4 +135,8 @@ class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate { // print("inside didChangeAuthorization "); updateWeather() } + + deinit { + activity.invalidate() + } } diff --git a/MTMR/Widgets/YandexWeatherBarItem.swift b/MTMR/Widgets/YandexWeatherBarItem.swift index 0d4f9ab..6df2149 100644 --- a/MTMR/Widgets/YandexWeatherBarItem.swift +++ b/MTMR/Widgets/YandexWeatherBarItem.swift @@ -121,6 +121,10 @@ class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate func locationManager(_: CLLocationManager, didChangeAuthorization _: CLAuthorizationStatus) { updateWeather() } + + deinit { + activity.invalidate() + } } extension String { From 168b6298105fef983eff5711fa96e707ce9d9aa5 Mon Sep 17 00:00:00 2001 From: bobrosoft Date: Tue, 6 Aug 2019 16:51:40 +0200 Subject: [PATCH 5/5] CustomButtonTouchBarItem: fix time interval constant --- MTMR/CustomButtonTouchBarItem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MTMR/CustomButtonTouchBarItem.swift b/MTMR/CustomButtonTouchBarItem.swift index 428ca05..c6aa049 100644 --- a/MTMR/CustomButtonTouchBarItem.swift +++ b/MTMR/CustomButtonTouchBarItem.swift @@ -190,7 +190,7 @@ class LongPressGestureRecognizer: NSPressGestureRecognizer { let touches = event.touches(for: self.view!) if touches.count == 1 { // to prevent it for built-in two/three-finger gestures - timer = Timer.scheduledTimer(timeInterval: 0.25, target: self, selector: #selector(self.onTimer), userInfo: nil, repeats: false) + timer = Timer.scheduledTimer(timeInterval: recognizeTimeout, target: self, selector: #selector(self.onTimer), userInfo: nil, repeats: false) } super.touchesBegan(with: event)