88
99#import " TrackPad.h"
1010#import " AppDelegate.h"
11+ @import AudioToolbox;
1112
1213#define TRACKPAD_ACCEL_N 1
1314#define TRACKPAD_ACCEL_T 0.2
@@ -21,6 +22,7 @@ @implementation TrackPad
2122 CGPoint previousTouchLoc;
2223 BOOL shouldClick;
2324 BOOL isDragging;
25+ BOOL supportsForceTouch, didForceClick;
2426 NSMutableSet *currentTouches;
2527}
2628
@@ -34,6 +36,15 @@ - (instancetype)initWithFrame:(CGRect)frame {
3436 return self;
3537}
3638
39+ - (void )willMoveToSuperview : (UIView *)newSuperview {
40+ [super willMoveToSuperview: newSuperview];
41+ @try {
42+ supportsForceTouch = (newSuperview.traitCollection .forceTouchCapability == UIForceTouchCapabilityAvailable);
43+ } @catch (NSException *exception) {
44+ supportsForceTouch = NO ;
45+ }
46+ }
47+
3748- (void )touchesBegan : (NSSet <UITouch *> *)touches withEvent : (UIEvent *)event {
3849 [currentTouches unionSet: touches];
3950 if (currentTouches.count == 1 ) {
@@ -45,11 +56,14 @@ - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
4556
4657- (void )firstTouchBegan : (UITouch *)touch withEvent : (UIEvent *)event {
4758 CGPoint touchLoc = [touch locationInView: self ];
59+ shouldClick = YES ;
4860 if (!isDragging && (event.timestamp - previousTouchTime < touchTimeThreshold) &&
4961 fabs (previousTouchLoc.x - touchLoc.x ) < touchDistanceThreshold &&
5062 fabs (previousTouchLoc.y - touchLoc.y ) < touchDistanceThreshold) {
5163 [self startDragging ];
5264 }
65+ previousTouchTime = event.timestamp ;
66+ previousTouchLoc = touchLoc;
5367}
5468
5569- (void )touchesMoved : (NSSet <UITouch *> *)touches withEvent : (UIEvent *)event {
@@ -62,21 +76,34 @@ - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
6276 NSTimeInterval accel = TRACKPAD_ACCEL_N / (TRACKPAD_ACCEL_T + ((timeDiff * timeDiff)/TRACKPAD_ACCEL_D));
6377 locDiff.x *= accel;
6478 locDiff.y *= accel;
65- shouldClick = NO ;
66- [[AppDelegate sharedEmulator ] moveMouseX: locDiff.x Y: locDiff.y];
79+
80+ if (!CGPointEqualToPoint (touchLoc, previousTouchLoc)) {
81+ shouldClick = NO ;
82+ [[AppDelegate sharedEmulator ] moveMouseX: locDiff.x Y: locDiff.y];
83+ }
84+
6785 previousTouchTime = event.timestamp ;
6886 previousTouchLoc = touchLoc;
87+
88+ if (supportsForceTouch) {
89+ [self handleForceClick: touch];
90+ }
6991}
7092
7193- (void )touchesEnded : (NSSet <UITouch *> *)touches withEvent : (UIEvent *)event {
7294 [currentTouches minusSet: touches];
7395 if (currentTouches.count > 0 ) {
7496 return ;
97+ } else if (didForceClick) {
98+ AudioServicesPlaySystemSound (1519 );
99+ didForceClick = NO ;
100+ [self cancelScheduledClick ];
101+ [self mouseUp ];
102+ return ;
75103 }
76104 CGPoint touchLoc = [touches.anyObject locationInView: self ];
77105 if (shouldClick && (event.timestamp - previousTouchTime < touchTimeThreshold)) {
78- [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector (mouseClick ) object: nil ];
79- [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector (mouseUp ) object: nil ];
106+ [self cancelScheduledClick ];
80107 [self performSelector: @selector (mouseClick ) withObject: nil afterDelay: touchTimeThreshold];
81108 }
82109 shouldClick = NO ;
@@ -90,7 +117,10 @@ - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
90117
91118- (void )touchesCancelled : (NSSet <UITouch *> *)touches withEvent : (UIEvent *)event {
92119 [currentTouches minusSet: touches];
93- [self stopDragging ];
120+ isDragging = NO ;
121+ shouldClick = NO ;
122+ didForceClick = NO ;
123+ [self mouseUp ];
94124}
95125
96126- (void )startDragging {
@@ -105,6 +135,14 @@ - (void)stopDragging {
105135 [[AppDelegate sharedEmulator ] setMouseButton: NO ];
106136}
107137
138+ - (void )handleForceClick : (UITouch *)touch {
139+ if (touch.force > 3.0 && !didForceClick) {
140+ AudioServicesPlaySystemSound (1519 );
141+ didForceClick = YES ;
142+ [self startDragging ];
143+ }
144+ }
145+
108146- (void )mouseClick {
109147 if (isDragging) {
110148 return ;
@@ -113,6 +151,11 @@ - (void)mouseClick {
113151 [self performSelector: @selector (mouseUp ) withObject: nil afterDelay: 2.0 /60.0 ];
114152}
115153
154+ - (void )cancelScheduledClick {
155+ [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector (mouseClick ) object: nil ];
156+ [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector (mouseUp ) object: nil ];
157+ }
158+
116159- (void )mouseUp {
117160 [[AppDelegate sharedEmulator ] setMouseButton: NO ];
118161}
0 commit comments