8
8
9
9
import Cocoa
10
10
11
+ struct ItemAction {
12
+ typealias TriggerClosure = ( ( ) -> Void ) ?
13
+
14
+ let trigger : Action . Trigger
15
+ let closure : TriggerClosure
16
+
17
+ init ( trigger: Action . Trigger , _ closure: TriggerClosure ) {
18
+ self . trigger = trigger
19
+ self . closure = closure
20
+ }
21
+ }
22
+
11
23
class CustomButtonTouchBarItem : NSCustomTouchBarItem , NSGestureRecognizerDelegate {
12
- var tapClosure : ( ( ) -> Void ) ?
13
- var longTapClosure : ( ( ) -> Void ) ? {
24
+
25
+ var actions : [ ItemAction ] = [ ] {
14
26
didSet {
15
- longClick. isEnabled = longTapClosure != nil
27
+ multiClick. isDoubleClickEnabled = actions. filter ( { $0. trigger == . doubleTap } ) . count > 0
28
+ multiClick. isTripleClickEnabled = actions. filter ( { $0. trigger == . tripleTap } ) . count > 0
29
+ longClick. isEnabled = actions. filter ( { $0. trigger == . longTap } ) . count > 0
16
30
}
17
31
}
18
32
var finishViewConfiguration : ( ) -> ( ) = { }
19
33
20
34
private var button : NSButton !
21
- private var singleClick : HapticClickGestureRecognizer !
22
35
private var longClick : LongPressGestureRecognizer !
36
+ private var multiClick : MultiClickGestureRecognizer !
23
37
24
38
init ( identifier: NSTouchBarItem . Identifier , title: String ) {
25
39
attributedTitle = title. defaultTouchbarAttributedString
@@ -31,10 +45,17 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
31
45
longClick. isEnabled = false
32
46
longClick. allowedTouchTypes = . direct
33
47
longClick. delegate = self
34
-
35
- singleClick = HapticClickGestureRecognizer ( target: self , action: #selector( handleGestureSingle) )
36
- singleClick. allowedTouchTypes = . direct
37
- singleClick. delegate = self
48
+
49
+ multiClick = MultiClickGestureRecognizer (
50
+ target: self ,
51
+ action: #selector( handleGestureSingleTap) ,
52
+ doubleAction: #selector( handleGestureDoubleTap) ,
53
+ tripleAction: #selector( handleGestureTripleTap)
54
+ )
55
+ multiClick. allowedTouchTypes = . direct
56
+ multiClick. delegate = self
57
+ multiClick. isDoubleClickEnabled = false
58
+ multiClick. isTripleClickEnabled = false
38
59
39
60
reinstallButton ( )
40
61
button. attributedTitle = attributedTitle
@@ -100,33 +121,43 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat
100
121
view = button
101
122
102
123
view. addGestureRecognizer ( longClick)
103
- view. addGestureRecognizer ( singleClick)
124
+ // view.addGestureRecognizer(singleClick)
125
+ view. addGestureRecognizer ( multiClick)
104
126
finishViewConfiguration ( )
105
127
}
106
128
107
129
func gestureRecognizer( _ gestureRecognizer: NSGestureRecognizer , shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer ) -> Bool {
108
- if gestureRecognizer == singleClick && otherGestureRecognizer == longClick
109
- || gestureRecognizer == longClick && otherGestureRecognizer == singleClick // need it
130
+ if gestureRecognizer == multiClick && otherGestureRecognizer == longClick
131
+ || gestureRecognizer == longClick && otherGestureRecognizer == multiClick // need it
110
132
{
111
133
return false
112
134
}
113
135
return true
114
136
}
115
-
116
- @objc func handleGestureSingle( gr: NSClickGestureRecognizer ) {
117
- switch gr. state {
118
- case . ended:
119
- tapClosure ? ( )
120
- break
121
- default :
122
- break
137
+
138
+ func callActions( for trigger: Action . Trigger ) {
139
+ let itemActions = self . actions. filter { $0. trigger == trigger }
140
+ for itemAction in itemActions {
141
+ itemAction. closure ? ( )
123
142
}
124
143
}
144
+
145
+ @objc func handleGestureSingleTap( ) {
146
+ callActions ( for: . singleTap)
147
+ }
148
+
149
+ @objc func handleGestureDoubleTap( ) {
150
+ callActions ( for: . doubleTap)
151
+ }
152
+
153
+ @objc func handleGestureTripleTap( ) {
154
+ callActions ( for: . tripleTap)
155
+ }
125
156
126
157
@objc func handleGestureLong( gr: NSPressGestureRecognizer ) {
127
158
switch gr. state {
128
159
case . possible: // tiny hack because we're calling action manually
129
- ( self . longTapClosure ?? self . tapClosure ) ? ( )
160
+ callActions ( for : . longTap )
130
161
break
131
162
default :
132
163
break
@@ -176,15 +207,78 @@ class CustomButtonCell: NSButtonCell {
176
207
}
177
208
}
178
209
179
- class HapticClickGestureRecognizer : NSClickGestureRecognizer {
210
+ // Thanks to https://stackoverflow.com/a/49843893
211
+ final class MultiClickGestureRecognizer : NSClickGestureRecognizer {
212
+
213
+ private let _action : Selector
214
+ private let _doubleAction : Selector
215
+ private let _tripleAction : Selector
216
+ private var _clickCount : Int = 0
217
+
218
+ public var isDoubleClickEnabled = true
219
+ public var isTripleClickEnabled = true
220
+
221
+ override var action : Selector ? {
222
+ get {
223
+ return nil /// prevent base class from performing any actions
224
+ } set {
225
+ if newValue != nil { // if they are trying to assign an actual action
226
+ fatalError ( " Only use init(target:action:doubleAction) for assigning actions " )
227
+ }
228
+ }
229
+ }
230
+
231
+ required init ( target: AnyObject , action: Selector , doubleAction: Selector , tripleAction: Selector ) {
232
+ _action = action
233
+ _doubleAction = doubleAction
234
+ _tripleAction = tripleAction
235
+ super. init ( target: target, action: nil )
236
+ }
237
+
238
+ required init ? ( coder: NSCoder ) {
239
+ fatalError ( " init(target:action:doubleAction:tripleAction) is only support atm " )
240
+ }
241
+
180
242
override func touchesBegan( with event: NSEvent ) {
181
243
HapticFeedback . shared? . tap ( strong: 2 )
182
244
super. touchesBegan ( with: event)
183
245
}
184
-
246
+
185
247
override func touchesEnded( with event: NSEvent ) {
186
248
HapticFeedback . shared? . tap ( strong: 1 )
187
249
super. touchesEnded ( with: event)
250
+ _clickCount += 1
251
+
252
+ var delayThreshold : TimeInterval // fine tune this as needed
253
+
254
+ guard isDoubleClickEnabled || isTripleClickEnabled else {
255
+ _ = target? . perform ( _action)
256
+ return
257
+ }
258
+
259
+ if ( isTripleClickEnabled) {
260
+ delayThreshold = 0.4
261
+ perform ( #selector( _resetAndPerformActionIfNecessary) , with: nil , afterDelay: delayThreshold)
262
+ if _clickCount == 3 {
263
+ _ = target? . perform ( _tripleAction)
264
+ }
265
+ } else {
266
+ delayThreshold = 0.3
267
+ perform ( #selector( _resetAndPerformActionIfNecessary) , with: nil , afterDelay: delayThreshold)
268
+ if _clickCount == 2 {
269
+ _ = target? . perform ( _doubleAction)
270
+ }
271
+ }
272
+ }
273
+
274
+ @objc private func _resetAndPerformActionIfNecessary( ) {
275
+ if _clickCount == 1 {
276
+ _ = target? . perform ( _action)
277
+ }
278
+ if isTripleClickEnabled && _clickCount == 2 {
279
+ _ = target? . perform ( _doubleAction)
280
+ }
281
+ _clickCount = 0
188
282
}
189
283
}
190
284
0 commit comments