summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/ios/qiosinputcontext.mm
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@digia.com>2014-10-22 13:46:35 +0200
committerTor Arne Vestbø <tor.arne.vestbo@digia.com>2014-11-08 00:20:23 +0100
commit370d7bf48afb20017852c0c7faff45d40dbf81a5 (patch)
tree2a19f38a64ca8ef06ea155571d918370146210dd /src/plugins/platforms/ios/qiosinputcontext.mm
parent82c2118cba2744000cff7cd3e5e952df775f5aba (diff)
iOS: Make hide-keyboard gesture use normal UIGestureRecognizer flow
Allows us to track state through the normal gesture recognizer states instead of custom variables. Change-Id: I4fe1b370a581132a9bbb8f51f7bee73381b80341 Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@digia.com>
Diffstat (limited to 'src/plugins/platforms/ios/qiosinputcontext.mm')
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.mm131
1 files changed, 90 insertions, 41 deletions
diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm
index 25d9f6c421..acdf6d9715 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.mm
+++ b/src/plugins/platforms/ios/qiosinputcontext.mm
@@ -46,25 +46,28 @@
#include "qiosglobal.h"
#include "qiosintegration.h"
#include "qiostextresponder.h"
+#include "qiosviewcontroller.h"
#include "qioswindow.h"
#include "quiview.h"
#include <QGuiApplication>
#include <QtGui/private/qwindow_p.h>
+// -------------------------------------------------------------------------
+
static QUIView *focusView()
{
return qApp->focusWindow() ?
reinterpret_cast<QUIView *>(qApp->focusWindow()->winId()) : 0;
}
-@interface QIOSKeyboardListener : UIGestureRecognizer {
+// -------------------------------------------------------------------------
+
+@interface QIOSKeyboardListener : UIGestureRecognizer <UIGestureRecognizerDelegate> {
@public
QIOSInputContext *m_context;
BOOL m_keyboardVisible;
BOOL m_keyboardVisibleAndDocked;
- BOOL m_touchPressWhileKeyboardVisible;
- BOOL m_keyboardHiddenByGesture;
QRectF m_keyboardRect;
CGRect m_keyboardEndRect;
NSTimeInterval m_duration;
@@ -77,13 +80,11 @@ static QUIView *focusView()
- (id)initWithQIOSInputContext:(QIOSInputContext *)context
{
- self = [super initWithTarget:self action:@selector(gestureTriggered)];
+ self = [super initWithTarget:self action:@selector(gestureStateChanged:)];
if (self) {
m_context = context;
m_keyboardVisible = NO;
m_keyboardVisibleAndDocked = NO;
- m_touchPressWhileKeyboardVisible = NO;
- m_keyboardHiddenByGesture = NO;
m_duration = 0;
m_curve = UIViewAnimationCurveEaseOut;
m_viewController = 0;
@@ -99,10 +100,9 @@ static QUIView *focusView()
Q_ASSERT(m_viewController);
// Attach 'hide keyboard' gesture to the window, but keep it disabled when the
- // keyboard is not visible. Note that we never trigger the gesture the way it is intended
- // since we don't want to cancel touch events and interrupt flicking etc. Instead we use
- // the gesture framework more as an event filter and hide the keyboard silently.
+ // keyboard is not visible.
self.enabled = NO;
+ self.cancelsTouchesInView = NO;
self.delaysTouchesEnded = NO;
[m_viewController.view.window addGestureRecognizer:self];
}
@@ -156,11 +156,19 @@ static QUIView *focusView()
// Note that UIKeyboardWillShowNotification is only sendt when the keyboard is docked.
m_keyboardVisibleAndDocked = YES;
m_keyboardEndRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
- self.enabled = YES;
+
if (!m_duration) {
m_duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
m_curve = UIViewAnimationCurve([[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue]);
}
+
+ UIResponder *firstResponder = [UIResponder currentFirstResponder];
+ if (![firstResponder isKindOfClass:[QIOSTextInputResponder class]])
+ return;
+
+ // Enable hide-keyboard gesture
+ self.enabled = YES;
+
m_context->scrollToCursor();
}
@@ -169,7 +177,7 @@ static QUIView *focusView()
// Note that UIKeyboardWillHideNotification is also sendt when the keyboard is undocked.
m_keyboardVisibleAndDocked = NO;
m_keyboardEndRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
- if (!m_keyboardHiddenByGesture) {
+ if (self.state != UIGestureRecognizerStateBegan) {
// Only disable the gesture if the hiding of the keyboard was not caused by it.
// Otherwise we need to await the final touchEnd callback for doing some clean-up.
self.enabled = NO;
@@ -202,54 +210,81 @@ static QUIView *focusView()
}
}
-- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
+// -------------------------------------------------------------------------
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
- CGPoint p = [[touches anyObject] locationInView:m_viewController.view.window];
- if (CGRectContainsPoint(m_keyboardEndRect, p)) {
- m_keyboardHiddenByGesture = YES;
+ [super touchesBegan:touches withEvent:event];
- UIResponder *firstResponder = [UIResponder currentFirstResponder];
- Q_ASSERT([firstResponder isKindOfClass:[QIOSTextInputResponder class]]);
- [firstResponder resignFirstResponder];
- }
+ Q_ASSERT(m_keyboardVisibleAndDocked);
- [super touchesMoved:touches withEvent:event];
+ if ([touches count] != 1)
+ self.state = UIGestureRecognizerStateFailed;
}
-- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
- Q_ASSERT(m_keyboardVisibleAndDocked);
- m_touchPressWhileKeyboardVisible = YES;
- [super touchesBegan:touches withEvent:event];
+ [super touchesMoved:touches withEvent:event];
+
+ if (self.state != UIGestureRecognizerStatePossible)
+ return;
+
+ CGPoint touchPoint = [[touches anyObject] locationInView:m_viewController.view.window];
+ if (CGRectContainsPoint(m_keyboardEndRect, touchPoint))
+ self.state = UIGestureRecognizerStateBegan;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
- m_touchPressWhileKeyboardVisible = NO;
- [self performSelectorOnMainThread:@selector(touchesEndedPostDelivery) withObject:nil waitUntilDone:NO];
[super touchesEnded:touches withEvent:event];
+
+ [self touchesEndedOrCancelled];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
- m_touchPressWhileKeyboardVisible = NO;
- [self performSelectorOnMainThread:@selector(touchesEndedPostDelivery) withObject:nil waitUntilDone:NO];
[super touchesCancelled:touches withEvent:event];
+
+ [self touchesEndedOrCancelled];
}
-- (void)touchesEndedPostDelivery
+- (void)touchesEndedOrCancelled
{
- // Do some clean-up _after_ touchEnd has been delivered to QUIView
- m_keyboardHiddenByGesture = NO;
+ // Defer final state change until next runloop iteration, so that Qt
+ // has a chance to process the final touch events first, before we eg.
+ // scroll the view.
+ dispatch_async(dispatch_get_main_queue (), ^{
+ // iOS will transition from began to changed by itself
+ Q_ASSERT(self.state != UIGestureRecognizerStateBegan);
+
+ if (self.state == UIGestureRecognizerStateChanged)
+ self.state = UIGestureRecognizerStateEnded;
+ else
+ self.state = UIGestureRecognizerStateFailed;
+ });
+}
+
+- (void)gestureStateChanged:(id)sender
+{
+ Q_UNUSED(sender);
+
+ if (self.state == UIGestureRecognizerStateBegan) {
+ qImDebug() << "hide keyboard gesture was triggered";
+ UIResponder *firstResponder = [UIResponder currentFirstResponder];
+ Q_ASSERT([firstResponder isKindOfClass:[QIOSTextInputResponder class]]);
+ [firstResponder resignFirstResponder];
+ }
+}
+
+- (void)reset
+{
+ [super reset];
+
if (!m_keyboardVisibleAndDocked) {
+ qImDebug() << "keyboard was hidden, disabling hide-keyboard gesture";
self.enabled = NO;
- if (qApp->focusObject()) {
- // UI Controls are told to gain focus on touch release. So when the 'hide keyboard' gesture
- // finishes, the final touch end can trigger a control to gain focus. This is in conflict with
- // the gesture, so we clear focus once more as a work-around.
- static_cast<QWindowPrivate *>(QObjectPrivate::get(qApp->focusWindow()))->clearFocusObject();
- }
} else {
+ qImDebug() << "gesture completed without triggering, scrolling view to cursor";
m_context->scrollToCursor();
}
}
@@ -353,10 +388,10 @@ void QIOSInputContext::scrollToCursor()
if (!isQtApplication())
return;
- if (m_keyboardListener->m_touchPressWhileKeyboardVisible) {
- // Don't scroll to the cursor if the user is touching the screen. This
- // interferes with selection and the 'hide keyboard' gesture. Instead
- // we update scrolling upon touchEnd.
+ if (m_keyboardListener.state == UIGestureRecognizerStatePossible && m_keyboardListener.numberOfTouches == 1) {
+ // Don't scroll to the cursor if the user is touching the screen and possibly
+ // trying to trigger the hide-keyboard gesture.
+ qImDebug() << "preventing scrolling to cursor as we're still waiting for a possible gesture";
return;
}
@@ -426,6 +461,18 @@ void QIOSInputContext::setFocusObject(QObject *focusObject)
{
Q_UNUSED(focusObject);
+ qImDebug() << "new focus object =" << focusObject;
+
+ if (m_keyboardListener.state == UIGestureRecognizerStateChanged) {
+ // A new focus object may be set as part of delivering touch events to
+ // application during the hide-keyboard gesture, but we don't want that
+ // to result in a new object getting focus and bringing the keyboard up
+ // again.
+ qImDebug() << "clearing focus object" << focusObject << "as hide-keyboard gesture is active";
+ clearCurrentFocusObject();
+ return;
+ }
+
reset();
if (m_keyboardListener->m_keyboardVisibleAndDocked)
@@ -436,6 +483,8 @@ void QIOSInputContext::focusWindowChanged(QWindow *focusWindow)
{
Q_UNUSED(focusWindow);
+ qImDebug() << "new focus window =" << focusWindow;
+
reset();
[m_keyboardListener handleKeyboardRectChanged];