summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@digia.com>2014-10-22 13:21:34 +0200
committerTor Arne Vestbø <tor.arne.vestbo@digia.com>2014-11-03 18:45:53 +0100
commitd563f6142b9f319826ae68dbe630f1d865be29a0 (patch)
tree97a5d7c8206db99a95a02d1b4dd06069d11995d7 /src/plugins/platforms
parentd8b45a360f99149c42863067e3b40c619a892cd2 (diff)
iOS: Make QIOSTextInputResponder a proper first-responder during text input
Instead of faking it, by returning YES for isFirstResponder, which caused issues when iOS would try to dismiss the keyboard by resigning the true first-responder. Change-Id: I816c4cf9c699d72995ce7968e1f1a4aa9c9c167e Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@digia.com>
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.h8
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.mm43
-rw-r--r--src/plugins/platforms/ios/qiostextresponder.mm54
-rw-r--r--src/plugins/platforms/ios/quiview.mm10
4 files changed, 105 insertions, 10 deletions
diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h
index 8850bbf80e..46fe35d884 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.h
+++ b/src/plugins/platforms/ios/qiosinputcontext.h
@@ -65,7 +65,8 @@ public:
void showInputPanel();
void hideInputPanel();
- void hideVirtualKeyboard();
+
+ void clearCurrentFocusObject();
bool isInputPanelVisible() const;
void setFocusObject(QObject *object);
@@ -81,10 +82,15 @@ public:
const ImeState &imeState() { return m_imeState; };
+ bool isReloadingInputViewsFromUpdate() const { return m_isReloadingInputViewsFromUpdate; }
+
+ static QIOSInputContext *instance();
+
private:
QIOSKeyboardListener *m_keyboardListener;
QIOSTextInputResponder *m_textResponder;
ImeState m_imeState;
+ bool m_isReloadingInputViewsFromUpdate;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm
index 70307f7f54..c038628fd9 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.mm
+++ b/src/plugins/platforms/ios/qiosinputcontext.mm
@@ -44,6 +44,7 @@
#import <UIKit/UIGestureRecognizerSubclass.h>
#include "qiosglobal.h"
+#include "qiosintegration.h"
#include "qiostextresponder.h"
#include "qioswindow.h"
#include "quiview.h"
@@ -206,7 +207,10 @@ static QUIView *focusView()
CGPoint p = [[touches anyObject] locationInView:m_viewController.view.window];
if (CGRectContainsPoint(m_keyboardEndRect, p)) {
m_keyboardHiddenByGesture = YES;
- m_context->hideVirtualKeyboard();
+
+ UIResponder *firstResponder = [UIResponder currentFirstResponder];
+ Q_ASSERT([firstResponder isKindOfClass:[QIOSTextInputResponder class]]);
+ [firstResponder resignFirstResponder];
}
[super touchesMoved:touches withEvent:event];
@@ -279,10 +283,16 @@ Qt::InputMethodQueries ImeState::update(Qt::InputMethodQueries properties)
// -------------------------------------------------------------------------
+QIOSInputContext *QIOSInputContext::instance()
+{
+ return static_cast<QIOSInputContext *>(QIOSIntegration::instance()->inputContext());
+}
+
QIOSInputContext::QIOSInputContext()
: QPlatformInputContext()
, m_keyboardListener([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this])
, m_textResponder(0)
+ , m_isReloadingInputViewsFromUpdate(false)
{
if (isQtApplication())
connect(qGuiApp->inputMethod(), &QInputMethod::cursorRectangleChanged, this, &QIOSInputContext::cursorRectangleChanged);
@@ -310,9 +320,10 @@ void QIOSInputContext::hideInputPanel()
// No-op, keyboard controlled fully by platform based on focus
}
-void QIOSInputContext::hideVirtualKeyboard()
+void QIOSInputContext::clearCurrentFocusObject()
{
- static_cast<QWindowPrivate *>(QObjectPrivate::get(qApp->focusWindow()))->clearFocusObject();
+ if (QWindow *focusWindow = qApp->focusWindow())
+ static_cast<QWindowPrivate *>(QObjectPrivate::get(focusWindow))->clearFocusObject();
}
bool QIOSInputContext::isInputPanelVisible() const
@@ -452,12 +463,24 @@ void QIOSInputContext::update(Qt::InputMethodQueries updatedProperties)
updatedProperties |= (Qt::ImHints | Qt::ImPlatformData);
}
+ qImDebug() << "fw =" << qApp->focusWindow() << "fo =" << qApp->focusObject();
+
Qt::InputMethodQueries changedProperties = m_imeState.update(updatedProperties);
if (changedProperties & (Qt::ImEnabled | Qt::ImHints | Qt::ImPlatformData)) {
// Changes to enablement or hints require virtual keyboard reconfigure
- [m_textResponder release];
- m_textResponder = [[QIOSTextInputResponder alloc] initWithInputContext:this];
- [m_textResponder reloadInputViews];
+
+ qImDebug() << "changed IM properties" << changedProperties << "require keyboard reconfigure";
+
+ if (inputMethodAccepted()) {
+ qImDebug() << "replacing text responder with new text responder";
+ [m_textResponder autorelease];
+ m_textResponder = [[QIOSTextInputResponder alloc] initWithInputContext:this];
+ [m_textResponder becomeFirstResponder];
+ } else {
+ qImDebug() << "IM not enabled, reloading input views";
+ QScopedValueRollback<bool> recursionGuard(m_isReloadingInputViewsFromUpdate, true);
+ [[UIResponder currentFirstResponder] reloadInputViews];
+ }
} else {
[m_textResponder notifyInputDelegate:changedProperties];
}
@@ -497,6 +520,12 @@ void QIOSInputContext::commit()
@implementation QUIView (InputMethods)
- (void)reloadInputViews
{
- qApp->inputMethod()->reset();
+ if (QIOSInputContext::instance()->isReloadingInputViewsFromUpdate()) {
+ qImDebug() << "preventing recursion by reloading super";
+ [super reloadInputViews];
+ } else {
+ qImDebug() << "reseting input methods";
+ qApp->inputMethod()->reset();
+ }
}
@end
diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm
index 54362cde7a..b809fc4b51 100644
--- a/src/plugins/platforms/ios/qiostextresponder.mm
+++ b/src/plugins/platforms/ios/qiostextresponder.mm
@@ -218,11 +218,61 @@
[super dealloc];
}
-- (BOOL)isFirstResponder
+- (BOOL)canBecomeFirstResponder
{
return YES;
}
+- (BOOL)becomeFirstResponder
+{
+ FirstResponderCandidate firstResponderCandidate(self);
+
+ qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder];
+
+ if (![super becomeFirstResponder]) {
+ qImDebug() << self << "was not allowed to become first responder";
+ return NO;
+ }
+
+ qImDebug() << self << "became first responder";
+
+ return YES;
+}
+
+- (BOOL)resignFirstResponder
+{
+ qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder];
+
+ // Don't allow activation events of the window that we're doing text on behalf on
+ // to steal responder.
+ if (FirstResponderCandidate::currentCandidate() == [self nextResponder]) {
+ qImDebug() << "not allowing parent window to steal responder";
+ return NO;
+ }
+
+ if (![super resignFirstResponder])
+ return NO;
+
+ qImDebug() << self << "resigned first responder";
+
+ // Dismissing the keyboard will trigger resignFirstResponder, but so will
+ // a regular responder transfer to another window. In the former case, iOS
+ // will set the new first-responder to our next-responder, and in the latter
+ // case we'll have an active responder candidate.
+ if ([UIResponder currentFirstResponder] == [self nextResponder]) {
+ // We have resigned the keyboard, and transferred back to the parent view, so unset focus object
+ Q_ASSERT(!FirstResponderCandidate::currentCandidate());
+ qImDebug() << "keyboard was closed, clearing focus object";
+ m_inputContext->clearCurrentFocusObject();
+ } else {
+ // We've lost responder status because another window was made active
+ Q_ASSERT(FirstResponderCandidate::currentCandidate());
+ }
+
+ return YES;
+}
+
+
- (UIResponder*)nextResponder
{
return qApp->focusWindow() ?
@@ -577,7 +627,7 @@
Qt::InputMethodHints imeHints = static_cast<Qt::InputMethodHints>([self imValue:Qt::ImHints].toUInt());
if (!(imeHints & Qt::ImhMultiLine))
- m_inputContext->hideVirtualKeyboard();
+ [self resignFirstResponder];
return;
}
diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm
index 33e5b955e3..c4b92618b1 100644
--- a/src/plugins/platforms/ios/quiview.mm
+++ b/src/plugins/platforms/ios/quiview.mm
@@ -44,6 +44,7 @@
#include "qiosglobal.h"
#include "qiosintegration.h"
#include "qiosviewcontroller.h"
+#include "qiostextresponder.h"
#include "qioswindow.h"
#include "qiosmenu.h"
@@ -222,6 +223,15 @@
if ([responder isKindOfClass:[QUIView class]])
return NO;
+ // Nor do we want to deactivate the Qt window if the new responder
+ // is temporarily handling text input on behalf of a Qt window.
+ if ([responder isKindOfClass:[QIOSTextInputResponder class]]) {
+ while ((responder = [responder nextResponder])) {
+ if ([responder isKindOfClass:[QUIView class]])
+ return NO;
+ }
+ }
+
return YES;
}