diff --git a/iOS/GBBackgroundView.h b/iOS/GBBackgroundView.h index dcad561..1c34798 100644 --- a/iOS/GBBackgroundView.h +++ b/iOS/GBBackgroundView.h @@ -5,4 +5,5 @@ @interface GBBackgroundView : UIImageView @property (readonly) GBView *gbView; @property (nonatomic) GBLayout *layout; +@property (nonatomic) bool usesSwipePad; @end diff --git a/iOS/GBBackgroundView.m b/iOS/GBBackgroundView.m index 88b05e4..8d78414 100644 --- a/iOS/GBBackgroundView.m +++ b/iOS/GBBackgroundView.m @@ -29,9 +29,59 @@ static void positionView(UIImageView *view, CGPoint position) }; } +static GB_key_mask_t angleToKeyMask(double angle) +{ + signed quantizedAngle = round(angle / M_PI * 16); + if (quantizedAngle < 0) { + quantizedAngle += 32; + } + switch (quantizedAngle) { + case 32: + case 0: return GB_KEY_RIGHT_MASK; + case 1: return GB_KEY_RIGHT_MASK; + case 2: return GB_KEY_RIGHT_MASK; + case 3: return GB_KEY_RIGHT_MASK | GB_KEY_DOWN_MASK; + case 4: return GB_KEY_RIGHT_MASK | GB_KEY_DOWN_MASK; + case 5: return GB_KEY_DOWN_MASK; + case 6: return GB_KEY_DOWN_MASK; + case 7: return GB_KEY_DOWN_MASK; + + case 8: return GB_KEY_DOWN_MASK; + case 9: return GB_KEY_DOWN_MASK; + case 10: return GB_KEY_DOWN_MASK; + case 11: return GB_KEY_LEFT_MASK | GB_KEY_DOWN_MASK; + case 12: return GB_KEY_LEFT_MASK | GB_KEY_DOWN_MASK; + case 13: return GB_KEY_LEFT_MASK; + case 14: return GB_KEY_LEFT_MASK; + case 15: return GB_KEY_LEFT_MASK; + + case 16: return GB_KEY_LEFT_MASK; + case 17: return GB_KEY_LEFT_MASK; + case 18: return GB_KEY_LEFT_MASK; + case 19: return GB_KEY_LEFT_MASK | GB_KEY_UP_MASK; + case 20: return GB_KEY_LEFT_MASK | GB_KEY_UP_MASK; + case 21: return GB_KEY_UP_MASK; + case 22: return GB_KEY_UP_MASK; + case 23: return GB_KEY_UP_MASK; + + case 24: return GB_KEY_UP_MASK; + case 25: return GB_KEY_UP_MASK; + case 26: return GB_KEY_UP_MASK; + case 27: return GB_KEY_RIGHT_MASK | GB_KEY_UP_MASK; + case 28: return GB_KEY_RIGHT_MASK | GB_KEY_UP_MASK; + case 29: return GB_KEY_RIGHT_MASK; + case 30: return GB_KEY_RIGHT_MASK; + case 31: return GB_KEY_RIGHT_MASK; + } + + return 0; +} + @implementation GBBackgroundView { NSMutableSet *_touches; + UITouch *_swipePadTouch; + CGPoint _swipeOrigin; UIImageView *_dpadView; UIImageView *_dpadShadowView; UIImageView *_aButtonView; @@ -79,10 +129,24 @@ static void positionView(UIImageView *view, CGPoint position) - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + static const double dpadRadius = 75; + CGPoint dpadLocation = _layout.dpadLocation; + double factor = [UIScreen mainScreen].scale; + dpadLocation.x /= factor; + dpadLocation.y /= factor; for (UITouch *touch in touches) { - if (CGRectContainsPoint(self.gbView.frame, [touch locationInView:self])) { + CGPoint point = [touch locationInView:self]; + if (CGRectContainsPoint(self.gbView.frame, point)) { [self.window.rootViewController presentViewController:[GBMenuViewController menu] animated:true completion:nil]; } + + if (_usesSwipePad && !_swipePadTouch) { + if (fabs(point.x - dpadLocation.x) <= dpadRadius && + fabs(point.y - dpadLocation.y) <= dpadRadius) { + _swipePadTouch = touch; + _swipeOrigin = point; + } + } } [_touches unionSet:touches]; [self touchesChanged]; @@ -90,6 +154,9 @@ static void positionView(UIImageView *view, CGPoint position) - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + if ([touches containsObject:_swipePadTouch]) { + _swipePadTouch = nil; + } [_touches minusSet:touches]; [self touchesChanged]; } @@ -111,9 +178,28 @@ static void positionView(UIImageView *view, CGPoint position) GB_key_mask_t mask = 0; double factor = [UIScreen mainScreen].scale; double buttonRadiusSquared = 36 * 36 * factor * factor; - double dpadRadiusSquared = 75 * 75 * factor * factor; + double dpadRadius = 75 * factor; bool dpadHandled = false; + if (_usesSwipePad) { + dpadHandled = true; + if (_swipePadTouch) { + CGPoint point = [_swipePadTouch locationInView:self]; + double squaredDistance = CGPointSquaredDistance(point, _swipeOrigin); + if (squaredDistance > 16 * 16) { + double angle = CGPointAngle(point, _swipeOrigin); + mask |= angleToKeyMask(angle); + if (squaredDistance > 24 * 24) { + double deltaX = point.x - _swipeOrigin.x; + double deltaY = point.y - _swipeOrigin.y; + double distance = sqrt(squaredDistance); + _swipeOrigin.x = point.x - deltaX / distance * 24; + _swipeOrigin.y = point.y - deltaY / distance * 24; + } + } + } + } for (UITouch *touch in _touches) { + if (touch == _swipePadTouch) continue; CGPoint point = [touch locationInView:self]; point.x *= factor; point.y *= factor; @@ -129,31 +215,12 @@ static void positionView(UIImageView *view, CGPoint position) else if (CGPointSquaredDistance(point, _layout.selectLocation) <= buttonRadiusSquared) { mask |= GB_KEY_SELECT_MASK; } - else if (!dpadHandled && CGPointSquaredDistance(point, _layout.dpadLocation) <= dpadRadiusSquared) { + else if (!dpadHandled && + fabs(point.x - _layout.dpadLocation.x) <= dpadRadius && + fabs(point.y - _layout.dpadLocation.y) <= dpadRadius) { dpadHandled = true; // Don't handle the dpad twice double angle = CGPointAngle(point, _layout.dpadLocation); - signed quantizedAngle = round(angle / M_PI * 6); - if (quantizedAngle < 0) { - quantizedAngle += 12; - } - switch (quantizedAngle) { - case 12: - case 0 : mask |= GB_KEY_RIGHT_MASK; break; - case 1 : mask |= GB_KEY_RIGHT_MASK | GB_KEY_DOWN_MASK; break; - case 2 : mask |= GB_KEY_DOWN_MASK; break; - - case 3 : mask |= GB_KEY_DOWN_MASK; break; - case 4 : mask |= GB_KEY_LEFT_MASK | GB_KEY_DOWN_MASK; break; - case 5 : mask |= GB_KEY_LEFT_MASK; break; - - case 6 : mask |= GB_KEY_LEFT_MASK; break; - case 7 : mask |= GB_KEY_LEFT_MASK | GB_KEY_UP_MASK; break; - case 8 : mask |= GB_KEY_UP_MASK; break; - - case 9: mask |= GB_KEY_UP_MASK; break; - case 10: mask |= GB_KEY_RIGHT_MASK | GB_KEY_UP_MASK; break; - case 11: mask |= GB_KEY_RIGHT_MASK; break; - } + mask |= angleToKeyMask(angle); } } if (mask != _lastMask) { @@ -180,7 +247,13 @@ static void positionView(UIImageView *view, CGPoint position) _dpadShadowView.hidden = hidden; if (!hidden) { - _dpadShadowView.image = [UIImage imageNamed:diagonal? @"dpadShadowDiagonal" : @"dpadShadow"]; + if (_usesSwipePad) { + _dpadShadowView.image = [UIImage imageNamed:diagonal? @"swipepadShadowDiagonal" : @"swipepadShadow"]; + + } + else { + _dpadShadowView.image = [UIImage imageNamed:diagonal? @"dpadShadowDiagonal" : @"dpadShadow"]; + } _dpadShadowView.transform = CGAffineTransformMakeRotation(rotation); } @@ -228,4 +301,10 @@ static void positionView(UIImageView *view, CGPoint position) _screenLabel.frame = screenFrame; } +- (void)setUsesSwipePad:(bool)usesSwipePad +{ + _usesSwipePad = usesSwipePad; + _dpadView.image = [UIImage imageNamed:usesSwipePad? @"swipepad" : @"dpad"]; +} + @end diff --git a/iOS/GBLoadROMTableViewController.m b/iOS/GBLoadROMTableViewController.m index 520775d..c36ee37 100644 --- a/iOS/GBLoadROMTableViewController.m +++ b/iOS/GBLoadROMTableViewController.m @@ -160,7 +160,7 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath [self renameRow:indexPath]; }], [UIAction actionWithTitle:@"Duplicate" - image:[UIImage systemImageNamed:@"plus.rectangle.on.rectangle"] + image:[UIImage systemImageNamed:@"plus.square.on.square"] identifier:nil handler:^(__kindof UIAction *action) { [[GBROMManager sharedManager] duplicateROM:[GBROMManager sharedManager].allROMs[[indexPath indexAtPosition:1]]]; diff --git a/iOS/GBSettingsViewController.m b/iOS/GBSettingsViewController.m index 87202f2..b7c8a54 100644 --- a/iOS/GBSettingsViewController.m +++ b/iOS/GBSettingsViewController.m @@ -283,14 +283,14 @@ static NSString const *typeLightTemp = @"typeLightTemp"; @"header": @"D-pad Style", @"items": @[ // TODO: Convert to enum when implemented - @{@"type": typeRadio, @"pref": @"GBDpadMode", @"title": @"Standard", @"value": @(0),}, - @{@"type": typeRadio, @"pref": @"GBDpadMode", @"title": @"Swipe", @"value": @(1),}, + @{@"type": typeRadio, @"pref": @"GBSwipeDpad", @"title": @"Standard", @"value": @NO,}, + @{@"type": typeRadio, @"pref": @"GBSwipeDpad", @"title": @"Swipe", @"value": @YES,}, ], @"footer": ^NSString *(){ - return (NSString * const[]){ - [0] = @"Directional input is determined by the touch position.", - [1] = @"Directional input is determined by the swipe direction.", - }[MIN(1, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBDpadMode"])]; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBSwipeDpad"]) { + return @"Directional input is determined by the swipe direction."; + } + return @"Directional input is determined by the touch position."; }, }, @{ diff --git a/iOS/GBViewController.m b/iOS/GBViewController.m index d34dea8..5ceaf16 100644 --- a/iOS/GBViewController.m +++ b/iOS/GBViewController.m @@ -156,10 +156,15 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) [[NSNotificationCenter defaultCenter] postNotificationName:@"GBFilterChanged" object:nil]; } forKey:@"GBFilter"]; - __weak GBView *weakGBView = _gbView; + __weak GBView *gbview = _gbView; [self addDefaultObserver:^(id newValue) { - weakGBView.frameBlendingMode = [newValue integerValue]; + gbview.frameBlendingMode = [newValue integerValue]; } forKey:@"GBFrameBlendingMode"]; + + __weak GBBackgroundView *backgroundView = _backgroundView; + [self addDefaultObserver:^(id newValue) { + backgroundView.usesSwipePad = [newValue boolValue]; + } forKey:@"GBSwipeDpad"]; [self willRotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation diff --git a/iOS/swipepad@2x.png b/iOS/swipepad@2x.png new file mode 100644 index 0000000..79c41b8 Binary files /dev/null and b/iOS/swipepad@2x.png differ diff --git a/iOS/swipepad@3x.png b/iOS/swipepad@3x.png new file mode 100644 index 0000000..0f727d4 Binary files /dev/null and b/iOS/swipepad@3x.png differ diff --git a/iOS/swipepadShadow@2x.png b/iOS/swipepadShadow@2x.png new file mode 100644 index 0000000..8840d24 Binary files /dev/null and b/iOS/swipepadShadow@2x.png differ diff --git a/iOS/swipepadShadow@3x.png b/iOS/swipepadShadow@3x.png new file mode 100644 index 0000000..115a58a Binary files /dev/null and b/iOS/swipepadShadow@3x.png differ diff --git a/iOS/swipepadShadowDiagonal@2x.png b/iOS/swipepadShadowDiagonal@2x.png new file mode 100644 index 0000000..7afe195 Binary files /dev/null and b/iOS/swipepadShadowDiagonal@2x.png differ diff --git a/iOS/swipepadShadowDiagonal@3x.png b/iOS/swipepadShadowDiagonal@3x.png new file mode 100644 index 0000000..fa8d576 Binary files /dev/null and b/iOS/swipepadShadowDiagonal@3x.png differ