minivmac4ios/Mini vMac/TrackPad.m

164 lines
4.8 KiB
Mathematica
Raw Normal View History

2016-05-14 11:12:21 +00:00
//
// TrackPad.m
// Mini vMac for iOS
//
// Created by Jesús A. Álvarez on 18/04/2016.
2018-04-28 10:12:43 +00:00
// Copyright © 2016-2018 namedfork. All rights reserved.
2016-05-14 11:12:21 +00:00
//
#import "TrackPad.h"
#import "AppDelegate.h"
2016-11-18 22:41:04 +00:00
@import AudioToolbox;
2016-05-14 11:12:21 +00:00
#define TRACKPAD_ACCEL_N 1
#define TRACKPAD_ACCEL_T 0.2
#define TRACKPAD_ACCEL_D 20
@implementation TrackPad
{
NSTimeInterval touchTimeThreshold;
NSTimeInterval previousClickTime, previousTouchTime;
CGFloat touchDistanceThreshold;
CGPoint previousTouchLoc;
BOOL shouldClick;
BOOL isDragging;
2016-11-18 22:41:04 +00:00
BOOL supportsForceTouch, didForceClick;
2016-05-14 11:12:21 +00:00
NSMutableSet *currentTouches;
}
- (instancetype)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
touchTimeThreshold = 0.25;
touchDistanceThreshold = 16;
currentTouches = [NSMutableSet setWithCapacity:4];
self.multipleTouchEnabled = YES;
2016-05-14 11:12:21 +00:00
}
return self;
}
2016-11-18 22:41:04 +00:00
- (void)willMoveToSuperview:(UIView *)newSuperview {
[super willMoveToSuperview:newSuperview];
@try {
supportsForceTouch = (newSuperview.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable);
} @catch (NSException *exception) {
supportsForceTouch = NO;
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[currentTouches unionSet:touches];
if (currentTouches.count == 1) {
[self firstTouchBegan:touches.anyObject withEvent:event];
} else {
[self startDragging];
}
}
- (void)firstTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLoc = [touch locationInView:self];
2016-11-18 22:41:04 +00:00
shouldClick = YES;
if (!isDragging && (event.timestamp - previousTouchTime < touchTimeThreshold) &&
2016-05-14 11:12:21 +00:00
fabs(previousTouchLoc.x - touchLoc.x) < touchDistanceThreshold &&
fabs(previousTouchLoc.y - touchLoc.y) < touchDistanceThreshold) {
[self startDragging];
2016-05-14 11:12:21 +00:00
}
2016-11-18 22:41:04 +00:00
previousTouchTime = event.timestamp;
previousTouchLoc = touchLoc;
2016-05-14 11:12:21 +00:00
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = touches.anyObject;
CGPoint touchLoc = [touch locationInView:self];
previousTouchLoc = [touch previousLocationInView:self];
2016-05-14 11:12:21 +00:00
// acceleration
CGPoint locDiff = CGPointMake(touchLoc.x - previousTouchLoc.x, touchLoc.y - previousTouchLoc.y);
2016-05-14 11:12:21 +00:00
NSTimeInterval timeDiff = 100 * (event.timestamp - previousTouchTime);
NSTimeInterval accel = TRACKPAD_ACCEL_N / (TRACKPAD_ACCEL_T + ((timeDiff * timeDiff)/TRACKPAD_ACCEL_D));
locDiff.x *= accel;
locDiff.y *= accel;
2016-11-18 22:41:04 +00:00
if (!CGPointEqualToPoint(touchLoc, previousTouchLoc)) {
shouldClick = NO;
[[AppDelegate sharedEmulator] moveMouseX:locDiff.x Y:locDiff.y];
}
2016-05-14 11:12:21 +00:00
previousTouchTime = event.timestamp;
previousTouchLoc = touchLoc;
2016-11-18 22:41:04 +00:00
if (supportsForceTouch) {
[self handleForceClick:touch];
}
2016-05-14 11:12:21 +00:00
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[currentTouches minusSet:touches];
if (currentTouches.count > 0) {
return;
2016-11-18 22:41:04 +00:00
} else if (didForceClick) {
AudioServicesPlaySystemSound(1519);
didForceClick = NO;
[self cancelScheduledClick];
[self mouseUp];
return;
}
2016-05-14 11:12:21 +00:00
CGPoint touchLoc = [touches.anyObject locationInView:self];
if (shouldClick && (event.timestamp - previousTouchTime < touchTimeThreshold)) {
2016-11-18 22:41:04 +00:00
[self cancelScheduledClick];
2016-05-14 11:12:21 +00:00
[self performSelector:@selector(mouseClick) withObject:nil afterDelay:touchTimeThreshold];
}
shouldClick = NO;
if (isDragging) {
[self stopDragging];
2016-05-14 11:12:21 +00:00
}
previousTouchLoc = touchLoc;
previousTouchTime = event.timestamp;
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[currentTouches minusSet:touches];
2016-11-18 22:41:04 +00:00
isDragging = NO;
shouldClick = NO;
didForceClick = NO;
[self mouseUp];
}
- (void)startDragging {
isDragging = YES;
shouldClick = NO;
[[AppDelegate sharedEmulator] setMouseButton:YES];
}
- (void)stopDragging {
isDragging = NO;
shouldClick = NO;
[[AppDelegate sharedEmulator] setMouseButton:NO];
}
2016-11-18 22:41:04 +00:00
- (void)handleForceClick:(UITouch *)touch {
if (touch.force > 3.0 && !didForceClick) {
AudioServicesPlaySystemSound(1519);
didForceClick = YES;
[self startDragging];
}
}
2016-05-14 11:12:21 +00:00
- (void)mouseClick {
if (isDragging) {
2016-05-14 11:12:21 +00:00
return;
}
2016-05-28 11:01:13 +00:00
[[AppDelegate sharedEmulator] setMouseButton:YES];
2016-05-14 11:12:21 +00:00
[self performSelector:@selector(mouseUp) withObject:nil afterDelay:2.0/60.0];
}
2016-11-18 22:41:04 +00:00
- (void)cancelScheduledClick {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(mouseClick) object:nil];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(mouseUp) object:nil];
}
2016-05-14 11:12:21 +00:00
- (void)mouseUp {
2016-05-28 11:01:13 +00:00
[[AppDelegate sharedEmulator] setMouseButton:NO];
2016-05-14 11:12:21 +00:00
}
@end