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>
This commit is contained in:
parent
d8b45a360f
commit
d563f6142b
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user