iOS: added delay gesture recognizer
This gesture recognizer will prevent GodotView from processing unwanted gestures. Emulates UIScrollView behavior. Fires delayed touches on significant movement.
This commit is contained in:
parent
5700429e4e
commit
e6d7e01a72
4 changed files with 229 additions and 12 deletions
|
@ -18,6 +18,7 @@ iphone_lib = [
|
|||
"godot_view.mm",
|
||||
"display_layer.mm",
|
||||
"godot_view_renderer.mm",
|
||||
"godot_view_gesture_recognizer.m",
|
||||
]
|
||||
|
||||
env_ios = env.Clone()
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "core/ustring.h"
|
||||
#import "display_layer.h"
|
||||
#include "display_server_iphone.h"
|
||||
#import "godot_view_gesture_recognizer.h"
|
||||
#import "godot_view_renderer.h"
|
||||
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
|
@ -57,6 +58,8 @@ static const int max_touches = 8;
|
|||
|
||||
@property(strong, nonatomic) CMMotionManager *motionManager;
|
||||
|
||||
@property(strong, nonatomic) GodotViewGestureRecognizer *delayGestureRecognizer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GodotView
|
||||
|
@ -139,6 +142,10 @@ static const int max_touches = 8;
|
|||
self.animationTimer = nil;
|
||||
}
|
||||
|
||||
if (self.delayGestureRecognizer) {
|
||||
self.delayGestureRecognizer = nil;
|
||||
}
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
@ -157,6 +164,12 @@ static const int max_touches = 8;
|
|||
self.motionManager = nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize delay gesture recognizer
|
||||
GodotViewGestureRecognizer *gestureRecognizer = [[GodotViewGestureRecognizer alloc] init];
|
||||
self.delayGestureRecognizer = gestureRecognizer;
|
||||
[self addGestureRecognizer:self.delayGestureRecognizer];
|
||||
[gestureRecognizer release];
|
||||
}
|
||||
|
||||
- (void)stopRendering {
|
||||
|
@ -359,9 +372,6 @@ static const int max_touches = 8;
|
|||
for (unsigned int i = 0; i < [tlist count]; i++) {
|
||||
if ([touchesSet containsObject:[tlist objectAtIndex:i]]) {
|
||||
UITouch *touch = [tlist objectAtIndex:i];
|
||||
if (touch.phase != UITouchPhaseBegan) {
|
||||
continue;
|
||||
}
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
CGPoint touchPoint = [touch locationInView:self];
|
||||
|
@ -375,9 +385,6 @@ static const int max_touches = 8;
|
|||
for (unsigned int i = 0; i < [tlist count]; i++) {
|
||||
if ([touches containsObject:[tlist objectAtIndex:i]]) {
|
||||
UITouch *touch = [tlist objectAtIndex:i];
|
||||
if (touch.phase != UITouchPhaseMoved) {
|
||||
continue;
|
||||
}
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
CGPoint touchPoint = [touch locationInView:self];
|
||||
|
@ -392,9 +399,6 @@ static const int max_touches = 8;
|
|||
for (unsigned int i = 0; i < [tlist count]; i++) {
|
||||
if ([touches containsObject:[tlist objectAtIndex:i]]) {
|
||||
UITouch *touch = [tlist objectAtIndex:i];
|
||||
if (touch.phase != UITouchPhaseEnded) {
|
||||
continue;
|
||||
}
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
[self removeTouch:touch];
|
||||
|
@ -409,9 +413,6 @@ static const int max_touches = 8;
|
|||
for (unsigned int i = 0; i < [tlist count]; i++) {
|
||||
if ([touches containsObject:[tlist objectAtIndex:i]]) {
|
||||
UITouch *touch = [tlist objectAtIndex:i];
|
||||
if (touch.phase != UITouchPhaseCancelled) {
|
||||
continue;
|
||||
}
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
DisplayServerIPhone::get_singleton()->touches_cancelled(tid);
|
||||
|
|
44
platform/iphone/godot_view_gesture_recognizer.h
Normal file
44
platform/iphone/godot_view_gesture_recognizer.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*************************************************************************/
|
||||
/* godot_view_gesture_recognizer.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
// GLViewGestureRecognizer allows iOS gestures to work currectly by
|
||||
// emulating UIScrollView's UIScrollViewDelayedTouchesBeganGestureRecognizer.
|
||||
// It catches all gestures incoming to UIView and delays them for 150ms
|
||||
// (the same value used by UIScrollViewDelayedTouchesBeganGestureRecognizer)
|
||||
// If touch cancelation or end message is fired it fires delayed
|
||||
// begin touch immediately as well as last touch signal
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface GodotViewGestureRecognizer : UIGestureRecognizer
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@end
|
171
platform/iphone/godot_view_gesture_recognizer.m
Normal file
171
platform/iphone/godot_view_gesture_recognizer.m
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*************************************************************************/
|
||||
/* godot_view_gesture_recognizer.m */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#import "godot_view_gesture_recognizer.h"
|
||||
|
||||
// Using same delay interval that is used for `UIScrollView`
|
||||
const NSTimeInterval kGLGestureDelayInterval = 0.150;
|
||||
|
||||
// Minimum distance for touches to move to fire
|
||||
// a delay timer before scheduled time.
|
||||
// Should be the low enough to not cause issues with dragging
|
||||
// but big enough to allow click to work.
|
||||
const CGFloat kGLGestureMovementDistance = 0.5;
|
||||
|
||||
@interface GodotViewGestureRecognizer ()
|
||||
|
||||
// Timer used to delay begin touch message.
|
||||
// Should work as simple emulation of UIDelayedAction
|
||||
@property(strong, nonatomic) NSTimer *delayTimer;
|
||||
|
||||
// Delayed touch parameters
|
||||
@property(strong, nonatomic) NSSet *delayedTouches;
|
||||
@property(strong, nonatomic) UIEvent *delayedEvent;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GodotViewGestureRecognizer
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
|
||||
self.cancelsTouchesInView = YES;
|
||||
self.delaysTouchesBegan = YES;
|
||||
self.delaysTouchesEnded = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (self.delayTimer) {
|
||||
[self.delayTimer invalidate];
|
||||
self.delayTimer = nil;
|
||||
}
|
||||
|
||||
if (self.delayedTouches) {
|
||||
self.delayedTouches = nil;
|
||||
}
|
||||
|
||||
if (self.delayedEvent) {
|
||||
self.delayedEvent = nil;
|
||||
}
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)delayTouches:(NSSet *)touches andEvent:(UIEvent *)event {
|
||||
[self.delayTimer fire];
|
||||
|
||||
self.delayedTouches = touches;
|
||||
self.delayedEvent = event;
|
||||
|
||||
self.delayTimer = [NSTimer
|
||||
scheduledTimerWithTimeInterval:kGLGestureDelayInterval
|
||||
target:self
|
||||
selector:@selector(fireDelayedTouches:)
|
||||
userInfo:nil
|
||||
repeats:NO];
|
||||
}
|
||||
|
||||
- (void)fireDelayedTouches:(id)timer {
|
||||
[self.delayTimer invalidate];
|
||||
self.delayTimer = nil;
|
||||
|
||||
if (self.delayedTouches) {
|
||||
[self.view touchesBegan:self.delayedTouches withEvent:self.delayedEvent];
|
||||
}
|
||||
|
||||
self.delayedTouches = nil;
|
||||
self.delayedEvent = nil;
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseBegan];
|
||||
[self delayTouches:cleared andEvent:event];
|
||||
[cleared release];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseMoved];
|
||||
|
||||
if (self.delayTimer) {
|
||||
// We should check if movement was significant enough to fire an event
|
||||
// for dragging to work correctly.
|
||||
for (UITouch *touch in cleared) {
|
||||
CGPoint from = [touch locationInView:self.view];
|
||||
CGPoint to = [touch previousLocationInView:self.view];
|
||||
CGFloat xDistance = from.x - to.x;
|
||||
CGFloat yDistance = from.y - to.y;
|
||||
|
||||
CGFloat distance = sqrt(xDistance * xDistance + yDistance * yDistance);
|
||||
|
||||
// Early exit, since one of touches has moved enough to fire a drag event.
|
||||
if (distance > kGLGestureMovementDistance) {
|
||||
[self.delayTimer fire];
|
||||
[self.view touchesMoved:cleared withEvent:event];
|
||||
[cleared release];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[cleared release];
|
||||
return;
|
||||
}
|
||||
|
||||
[self.view touchesMoved:cleared withEvent:event];
|
||||
[cleared release];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
[self.delayTimer fire];
|
||||
|
||||
NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseEnded];
|
||||
[self.view touchesEnded:cleared withEvent:event];
|
||||
[cleared release];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
[self.delayTimer fire];
|
||||
[self.view touchesCancelled:touches withEvent:event];
|
||||
};
|
||||
|
||||
- (NSSet *)copyClearedTouches:(NSSet *)touches phase:(UITouchPhase)phaseToSave {
|
||||
NSMutableSet *cleared = [touches mutableCopy];
|
||||
|
||||
for (UITouch *touch in touches) {
|
||||
if (touch.phase != phaseToSave) {
|
||||
[cleared removeObject:touch];
|
||||
}
|
||||
}
|
||||
|
||||
return cleared;
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in a new issue