summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@digia.com>2014-09-03 15:29:16 +0200
committerTor Arne Vestbø <tor.arne.vestbo@digia.com>2014-09-20 11:53:28 +0200
commit6910b8a552de6f0cd98fdfa50620825d59d63363 (patch)
tree8b89386acf7e8fe0ed5fd4390099d028ab0dc0d9 /src
parent6e4dc7073a195a73d9002e48dc4e27eb6b354d1e (diff)
iOS: Refactor text input handling to standalone responder
Instead of coupling the visibility of the virtual keyboard to the first-responder status of the currently active QUIView, we now treat first-responder as a separate state, tied directly to QWindow activation. This fits better with the concept of first-responders in iOS, as a UIView can become first-responder without dealing with text input, eg when dealing with touch events or menu actions. The decision point on whether or not to show the virtual keyboard is then handled by implementing the conformsToProtocol method and selectively returning YES for the UIKeyInput protocol. iOS internally calls _requiresKeyboardWhenFirstResponder on the UIResponder to determine this, but since we can't override a private method (like WKContentView in WebKit does) we have to rely on the fact that the implementation of the method uses the protocol conformance to make its decision. Once the virtual keyboard is up, we then need to react to changes to its configuration, such as keyboard type or the type of return key. Normally this would be a simple call to [view reloadInputViews], but iOS will not reload the built-in keyboards unless the UIResponder returns YES for _requiresKeyboardResetOnReload. Since we again can't override this private method (like WebKit does), we work around it by taking advantage of the fact that iOS will treat any change to the first-responder as a reason to do a keyboard reset. By using a stand-alone UIResponder for text input we can init and destroy these responders as needed, so that every call to reloadInputViews will trigger a reset, as the responder has not been seen before. We keep track of changes to the input-method-query, and detect whether or not we need to bring up a new UIResponder for text handling. As part of this refactoring we now tie the visibility of the virtual keyboard to the presence of a focus object that has input-methods enabled. This means that we automatically will track changes to input-elements through the focus changes, and reconfigure or hide the keyboard as appropriate. As a result the hide() method of QInputMethod becomes a no-op on iOS. Change-Id: I4c4834df490bc8b0bac32aeedbd819780bd5aaba Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/platforms/ios/ios.pro2
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.h19
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.mm177
-rw-r--r--src/plugins/platforms/ios/qiostextresponder.h74
-rw-r--r--src/plugins/platforms/ios/qiostextresponder.mm (renamed from src/plugins/platforms/ios/quiview_textinput.mm)227
-rw-r--r--src/plugins/platforms/ios/qioswindow.mm4
-rw-r--r--src/plugins/platforms/ios/quiview.h28
-rw-r--r--src/plugins/platforms/ios/quiview.mm50
8 files changed, 335 insertions, 246 deletions
diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro
index ad9912a8d0..82f0bd91c4 100644
--- a/src/plugins/platforms/ios/ios.pro
+++ b/src/plugins/platforms/ios/ios.pro
@@ -27,6 +27,7 @@ OBJECTIVE_SOURCES = \
qiosclipboard.mm \
quiaccessibilityelement.mm \
qiosplatformaccessibility.mm \
+ qiostextresponder.mm \
HEADERS = \
qiosintegration.h \
@@ -46,6 +47,7 @@ HEADERS = \
qiosclipboard.h \
quiaccessibilityelement.h \
qiosplatformaccessibility.h \
+ qiostextresponder.h \
OTHER_FILES = \
quiview_textinput.mm \
diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h
index 13255ada56..7f94a9836a 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.h
+++ b/src/plugins/platforms/ios/qiosinputcontext.h
@@ -44,13 +44,21 @@
#include <UIKit/UIKit.h>
+#include <QtGui/qevent.h>
#include <QtGui/qtransform.h>
#include <qpa/qplatforminputcontext.h>
QT_BEGIN_NAMESPACE
@class QIOSKeyboardListener;
-@class QUIView;
+@class QIOSTextInputResponder;
+
+struct ImeState
+{
+ ImeState() : currentState(0) {}
+ Qt::InputMethodQueries update(Qt::InputMethodQueries properties);
+ QInputMethodQueryEvent currentState;
+};
class QIOSInputContext : public QPlatformInputContext
{
@@ -59,8 +67,11 @@ public:
~QIOSInputContext();
QRectF keyboardRect() const;
+
void showInputPanel();
void hideInputPanel();
+ void hideVirtualKeyboard();
+
bool isInputPanelVisible() const;
void setFocusObject(QObject *object);
@@ -73,10 +84,12 @@ public:
void reset();
void commit();
+ const ImeState &imeState() { return m_imeState; };
+
private:
QIOSKeyboardListener *m_keyboardListener;
- QUIView *m_focusView;
- bool m_hasPendingHideRequest;
+ QIOSTextInputResponder *m_textResponder;
+ ImeState m_imeState;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm
index 064098157d..cbf3fb4ff2 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.mm
+++ b/src/plugins/platforms/ios/qiosinputcontext.mm
@@ -44,8 +44,10 @@
#import <UIKit/UIGestureRecognizerSubclass.h>
#include "qiosglobal.h"
+#include "qiostextresponder.h"
#include "qioswindow.h"
#include "quiview.h"
+
#include <QGuiApplication>
#include <QtGui/private/qwindow_p.h>
@@ -158,8 +160,6 @@
- (void) keyboardWillShow:(NSNotification *)notification
{
- if ([QUIView inUpdateKeyboardLayout])
- return;
// Note that UIKeyboardWillShowNotification is only sendt when the keyboard is docked.
m_keyboardVisibleAndDocked = YES;
m_keyboardEndRect = [self getKeyboardRect:notification];
@@ -173,8 +173,6 @@
- (void) keyboardWillHide:(NSNotification *)notification
{
- if ([QUIView inUpdateKeyboardLayout])
- return;
// Note that UIKeyboardWillHideNotification is also sendt when the keyboard is undocked.
m_keyboardVisibleAndDocked = NO;
m_keyboardEndRect = [self getKeyboardRect:notification];
@@ -207,7 +205,7 @@
QPointF p = fromCGPoint([[touches anyObject] locationInView:m_viewController.view]);
if (m_keyboardRect.contains(p)) {
m_keyboardHiddenByGesture = YES;
- m_context->hideInputPanel();
+ m_context->hideVirtualKeyboard();
}
[super touchesMoved:touches withEvent:event];
@@ -253,11 +251,43 @@
@end
+// -------------------------------------------------------------------------
+
+Qt::InputMethodQueries ImeState::update(Qt::InputMethodQueries properties)
+{
+ if (!properties)
+ return 0;
+
+ QInputMethodQueryEvent newState(properties);
+
+ if (qApp && qApp->focusObject())
+ QCoreApplication::sendEvent(qApp->focusObject(), &newState);
+
+ Qt::InputMethodQueries updatedProperties;
+ for (uint i = 0; i < (sizeof(Qt::ImQueryAll) * CHAR_BIT); ++i) {
+ if (Qt::InputMethodQuery property = Qt::InputMethodQuery(int(properties & (1 << i)))) {
+ if (newState.value(property) != currentState.value(property)) {
+ updatedProperties |= property;
+ currentState.setValue(property, newState.value(property));
+ }
+ }
+ }
+
+ return updatedProperties;
+}
+
+// -------------------------------------------------------------------------
+
+static QUIView *focusView()
+{
+ return qApp->focusWindow() ?
+ reinterpret_cast<QUIView *>(qApp->focusWindow()->handle()->winId()) : 0;
+}
+
QIOSInputContext::QIOSInputContext()
: QPlatformInputContext()
, m_keyboardListener([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this])
- , m_focusView(0)
- , m_hasPendingHideRequest(false)
+ , m_textResponder(0)
{
if (isQtApplication())
connect(qGuiApp->inputMethod(), &QInputMethod::cursorRectangleChanged, this, &QIOSInputContext::cursorRectangleChanged);
@@ -267,7 +297,7 @@ QIOSInputContext::QIOSInputContext()
QIOSInputContext::~QIOSInputContext()
{
[m_keyboardListener release];
- [m_focusView release];
+ [m_textResponder release];
}
QRectF QIOSInputContext::keyboardRect() const
@@ -277,61 +307,22 @@ QRectF QIOSInputContext::keyboardRect() const
void QIOSInputContext::showInputPanel()
{
- if (m_keyboardListener->m_keyboardHiddenByGesture) {
- // We refuse to re-show the keyboard until the touch
- // sequence that triggered the gesture has ended.
- return;
- }
-
- // Documentation tells that one should call (and recall, if necessary) becomeFirstResponder/resignFirstResponder
- // to show/hide the keyboard. This is slightly inconvenient, since there exist no API to get the current first
- // responder. Rather than searching for it from the top, we let the active QIOSWindow tell us which view to use.
- // Note that Qt will forward keyevents to whichever QObject that needs it, regardless of which UIView the input
- // actually came from. So in this respect, we're undermining iOS' responder chain.
- m_hasPendingHideRequest = false;
- [m_focusView becomeFirstResponder];
+ // No-op, keyboard controlled fully by platform based on focus
}
void QIOSInputContext::hideInputPanel()
{
- // Delay hiding the keyboard for cases where the user is transferring focus between
- // 'line edits'. In that case the 'line edit' that lost focus will close the input
- // panel, just to see that the new 'line edit' will open it again:
- m_hasPendingHideRequest = true;
- dispatch_async(dispatch_get_main_queue(), ^{
- if (m_hasPendingHideRequest)
- [m_focusView resignFirstResponder];
- });
+ // No-op, keyboard controlled fully by platform based on focus
}
-bool QIOSInputContext::isInputPanelVisible() const
+void QIOSInputContext::hideVirtualKeyboard()
{
- return m_keyboardListener->m_keyboardVisible;
+ static_cast<QWindowPrivate *>(QObjectPrivate::get(qApp->focusWindow()))->clearFocusObject();
}
-void QIOSInputContext::setFocusObject(QObject *focusObject)
-{
- if (!focusObject || !m_focusView || !m_focusView.isFirstResponder) {
- scroll(0);
- return;
- }
-
- reset();
-
- if (m_keyboardListener->m_keyboardVisibleAndDocked)
- scrollToCursor();
-}
-
-void QIOSInputContext::focusWindowChanged(QWindow *focusWindow)
+bool QIOSInputContext::isInputPanelVisible() const
{
- QUIView *view = focusWindow ? reinterpret_cast<QUIView *>(focusWindow->handle()->winId()) : 0;
- if ([m_focusView isFirstResponder])
- [view becomeFirstResponder];
- [m_focusView release];
- m_focusView = [view retain];
-
- if (view.window != m_keyboardListener->m_viewController.view)
- scroll(0);
+ return m_keyboardListener->m_keyboardVisible;
}
void QIOSInputContext::cursorRectangleChanged()
@@ -353,7 +344,7 @@ void QIOSInputContext::cursorRectangleChanged()
void QIOSInputContext::scrollToCursor()
{
- if (!isQtApplication() || !m_focusView)
+ if (!isQtApplication())
return;
if (m_keyboardListener->m_touchPressWhileKeyboardVisible) {
@@ -364,12 +355,12 @@ void QIOSInputContext::scrollToCursor()
}
UIView *view = m_keyboardListener->m_viewController.view;
- if (view.window != m_focusView.window)
+ if (view.window != focusView().window)
return;
const int margin = 20;
QRectF translatedCursorPos = qApp->inputMethod()->cursorRectangle();
- translatedCursorPos.translate(m_focusView.qwindow->geometry().topLeft());
+ translatedCursorPos.translate(focusView().qwindow->geometry().topLeft());
qreal keyboardY = m_keyboardListener->m_keyboardEndRect.y();
int statusBarY = qGuiApp->primaryScreen()->availableGeometry().y();
@@ -398,18 +389,84 @@ void QIOSInputContext::scroll(int y)
];
}
-void QIOSInputContext::update(Qt::InputMethodQueries query)
+// -------------------------------------------------------------------------
+
+void QIOSInputContext::setFocusObject(QObject *focusObject)
+{
+ Q_UNUSED(focusObject);
+
+ reset();
+
+ if (m_keyboardListener->m_keyboardVisibleAndDocked)
+ scrollToCursor();
+}
+
+void QIOSInputContext::focusWindowChanged(QWindow *focusWindow)
+{
+ Q_UNUSED(focusWindow);
+
+ reset();
+
+ if (m_keyboardListener->m_keyboardVisibleAndDocked)
+ scrollToCursor();
+}
+
+/*!
+ Called by the input item to inform the platform input methods when there has been
+ state changes in editor's input method query attributes. When calling the function
+ \a queries parameter has to be used to tell what has changes, which input method
+ can use to make queries for attributes it's interested with QInputMethodQueryEvent.
+*/
+void QIOSInputContext::update(Qt::InputMethodQueries updatedProperties)
{
- [m_focusView updateInputMethodWithQuery:query];
+ // Mask for properties that we are interested in and see if any of them changed
+ updatedProperties &= (Qt::ImEnabled | Qt::ImHints | Qt::ImQueryInput);
+
+ Qt::InputMethodQueries changedProperties = m_imeState.update(updatedProperties);
+ if (changedProperties & (Qt::ImEnabled | Qt::ImHints)) {
+ // Changes to enablement or hints require virtual keyboard reconfigure
+ [m_textResponder release];
+ m_textResponder = [[QIOSTextInputResponder alloc] initWithInputContext:this];
+ [m_textResponder reloadInputViews];
+ } else {
+ [m_textResponder notifyInputDelegate:changedProperties];
+ }
}
+/*!
+ Called by the input item to reset the input method state.
+*/
void QIOSInputContext::reset()
{
- [m_focusView reset];
+ update(Qt::ImQueryAll);
+
+ [m_textResponder setMarkedText:@"" selectedRange:NSMakeRange(0, 0)];
+ [m_textResponder notifyInputDelegate:Qt::ImQueryInput];
}
+/*!
+ Commits the word user is currently composing to the editor. The function is
+ mostly needed by the input methods with text prediction features and by the
+ methods where the script used for typing characters is different from the
+ script that actually gets appended to the editor. Any kind of action that
+ interrupts the text composing needs to flush the composing state by calling the
+ commit() function, for example when the cursor is moved elsewhere.
+*/
void QIOSInputContext::commit()
{
- [m_focusView commit];
+ [m_textResponder unmarkText];
+ [m_textResponder notifyInputDelegate:Qt::ImSurroundingText];
}
+// -------------------------------------------------------------------------
+
+@interface QUIView (InputMethods)
+- (void)reloadInputViews;
+@end
+
+@implementation QUIView (InputMethods)
+- (void)reloadInputViews
+{
+ qApp->inputMethod()->reset();
+}
+@end
diff --git a/src/plugins/platforms/ios/qiostextresponder.h b/src/plugins/platforms/ios/qiostextresponder.h
new file mode 100644
index 0000000000..7290d9e454
--- /dev/null
+++ b/src/plugins/platforms/ios/qiostextresponder.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#import <UIKit/UIKit.h>
+
+#include <QtCore/qstring.h>
+
+class QIOSInputContext;
+
+@interface QIOSTextInputResponder : UIResponder <UITextInputTraits, UIKeyInput, UITextInput>
+{
+ @public
+ QString m_markedText;
+ BOOL m_inSendEventToFocusObject;
+
+ @private
+ QIOSInputContext *m_inputContext;
+}
+
+- (id)initWithInputContext:(QIOSInputContext *)context;
+- (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties;
+
+// UITextInputTraits
+@property(nonatomic) UITextAutocapitalizationType autocapitalizationType;
+@property(nonatomic) UITextAutocorrectionType autocorrectionType;
+@property(nonatomic) UITextSpellCheckingType spellCheckingType;
+@property(nonatomic) BOOL enablesReturnKeyAutomatically;
+@property(nonatomic) UIKeyboardAppearance keyboardAppearance;
+@property(nonatomic) UIKeyboardType keyboardType;
+@property(nonatomic) UIReturnKeyType returnKeyType;
+@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry;
+
+// UITextInput
+@property(nonatomic, assign) id<UITextInputDelegate> inputDelegate;
+
+@end
diff --git a/src/plugins/platforms/ios/quiview_textinput.mm b/src/plugins/platforms/ios/qiostextresponder.mm
index 6b2707e706..ea6b5ffb94 100644
--- a/src/plugins/platforms/ios/quiview_textinput.mm
+++ b/src/plugins/platforms/ios/qiostextresponder.mm
@@ -39,31 +39,22 @@
**
****************************************************************************/
+#include "qiostextresponder.h"
+
+#include "qiosglobal.h"
#include "qiosinputcontext.h"
+#include "quiview.h"
+
+#include <QtCore/qscopedvaluerollback.h>
+#include <QtGui/qevent.h>
#include <QtGui/qtextformat.h>
#include <QtGui/private/qguiapplication_p.h>
-
-class StaticVariables
-{
-public:
- QInputMethodQueryEvent inputMethodQueryEvent;
- bool inUpdateKeyboardLayout;
-
- StaticVariables()
- : inputMethodQueryEvent(Qt::ImQueryInput)
- , inUpdateKeyboardLayout(false)
- {
- }
-};
-
-Q_GLOBAL_STATIC(StaticVariables, staticVariables);
+#include <QtGui/qpa/qplatformwindow.h>
// -------------------------------------------------------------------------
@interface QUITextPosition : UITextPosition
-{
-}
@property (nonatomic) NSUInteger index;
+ (QUITextPosition *)positionWithIndex:(NSUInteger)index;
@@ -84,8 +75,6 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
// -------------------------------------------------------------------------
@interface QUITextRange : UITextRange
-{
-}
@property (nonatomic) NSRange range;
+ (QUITextRange *)rangeWithNSRange:(NSRange)range;
@@ -125,51 +114,82 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
// -------------------------------------------------------------------------
-@implementation QUIView (TextInput)
+@implementation QIOSTextInputResponder
-- (BOOL)canBecomeFirstResponder
+- (id)initWithInputContext:(QIOSInputContext *)inputContext
{
- return YES;
+ if (!(self = [self init]))
+ return self;
+
+ m_inSendEventToFocusObject = NO;
+ m_inputContext = inputContext;
+
+ Qt::InputMethodHints hints = Qt::InputMethodHints([self imValue:Qt::ImHints].toUInt());
+
+ self.returnKeyType = (hints & Qt::ImhMultiLine) ? UIReturnKeyDefault : UIReturnKeyDone;
+ self.secureTextEntry = BOOL(hints & Qt::ImhHiddenText);
+ self.autocorrectionType = (hints & Qt::ImhNoPredictiveText) ?
+ UITextAutocorrectionTypeNo : UITextAutocorrectionTypeDefault;
+ self.spellCheckingType = (hints & Qt::ImhNoPredictiveText) ?
+ UITextSpellCheckingTypeNo : UITextSpellCheckingTypeDefault;
+
+ if (hints & Qt::ImhUppercaseOnly)
+ self.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
+ else if (hints & Qt::ImhNoAutoUppercase)
+ self.autocapitalizationType = UITextAutocapitalizationTypeNone;
+ else
+ self.autocapitalizationType = UITextAutocapitalizationTypeSentences;
+
+ if (hints & Qt::ImhUrlCharactersOnly)
+ self.keyboardType = UIKeyboardTypeURL;
+ else if (hints & Qt::ImhEmailCharactersOnly)
+ self.keyboardType = UIKeyboardTypeEmailAddress;
+ else if (hints & Qt::ImhDigitsOnly)
+ self.keyboardType = UIKeyboardTypeNumberPad;
+ else if (hints & Qt::ImhFormattedNumbersOnly)
+ self.keyboardType = UIKeyboardTypeDecimalPad;
+ else if (hints & Qt::ImhDialableCharactersOnly)
+ self.keyboardType = UIKeyboardTypeNumberPad;
+ else
+ self.keyboardType = UIKeyboardTypeDefault;
+
+ return self;
}
-- (BOOL)becomeFirstResponder
+- (void)dealloc
{
- // Note: QIOSInputContext controls our first responder status based on
- // whether or not the keyboard should be open or closed.
- [self updateTextInputTraits];
- return [super becomeFirstResponder];
+ [super dealloc];
}
-- (BOOL)resignFirstResponder
+- (BOOL)isFirstResponder
{
- // Resigning first responed status means that the virtual keyboard was closed, or
- // some other view became first responder. In either case we clear the focus object to
- // avoid blinking cursors in line edits etc:
- if (m_qioswindow)
- static_cast<QWindowPrivate *>(QObjectPrivate::get(m_qioswindow->window()))->clearFocusObject();
- return [super resignFirstResponder];
+ return YES;
}
-+ (bool)inUpdateKeyboardLayout
+- (UIResponder*)nextResponder
{
- return staticVariables()->inUpdateKeyboardLayout;
+ return qApp->focusWindow() ?
+ reinterpret_cast<QUIView *>(qApp->focusWindow()->handle()->winId()) : 0;
}
-- (void)updateKeyboardLayout
+/*!
+ iOS uses [UIResponder(Internal) _requiresKeyboardWhenFirstResponder] to check if the
+ current responder should bring up the keyboard, which in turn checks if the responder
+ supports the UIKeyInput protocol. By dynamically reporting our protocol conformance
+ we can control the keyboard visibility depending on whether or not we have a focus
+ object with IME enabled.
+*/
+- (BOOL)conformsToProtocol:(Protocol *)protocol
{
- if (![self isFirstResponder])
- return;
+ if (protocol == @protocol(UIKeyInput))
+ return m_inputContext->inputMethodAccepted();
- // There seems to be no API to inform that the keyboard layout needs to update.
- // As a work-around, we quickly resign first responder just to reassign it again.
- QScopedValueRollback<bool> rollback(staticVariables()->inUpdateKeyboardLayout);
- staticVariables()->inUpdateKeyboardLayout = true;
- [super resignFirstResponder];
- [self updateTextInputTraits];
- [super becomeFirstResponder];
+ return [super conformsToProtocol:protocol];
}
-- (void)updateUITextInputDelegate:(Qt::InputMethodQueries)query
+// -------------------------------------------------------------------------
+
+- (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties
{
// As documented, we should not report textWillChange/textDidChange unless the text
// was changed externally. That will cause spell checking etc to fail. But we don't
@@ -179,34 +199,17 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
if (m_inSendEventToFocusObject)
return;
- if (query & (Qt::ImCursorPosition | Qt::ImAnchorPosition)) {
- [self.inputDelegate selectionWillChange:id<UITextInput>(self)];
- [self.inputDelegate selectionDidChange:id<UITextInput>(self)];
+ if (updatedProperties & (Qt::ImCursorPosition | Qt::ImAnchorPosition)) {
+ [self.inputDelegate selectionWillChange:self];
+ [self.inputDelegate selectionDidChange:self];
}
- if (query & Qt::ImSurroundingText) {
- [self.inputDelegate textWillChange:id<UITextInput>(self)];
- [self.inputDelegate textDidChange:id<UITextInput>(self)];
+ if (updatedProperties & Qt::ImSurroundingText) {
+ [self.inputDelegate textWillChange:self];
+ [self.inputDelegate textDidChange:self];
}
}
-- (void)updateInputMethodWithQuery:(Qt::InputMethodQueries)query
-{
- Q_UNUSED(query);
-
- QObject *focusObject = QGuiApplication::focusObject();
- if (!focusObject)
- return;
-
- // Note that we ignore \a query, and instead update using Qt::ImQueryInput. This enables us to just
- // store the event without copying out the result from the event each time. Besides, we seem to be
- // called with Qt::ImQueryInput when only changing selection, and always if typing text. So there would
- // not be any performance gain by only updating \a query.
- staticVariables()->inputMethodQueryEvent = QInputMethodQueryEvent(Qt::ImQueryInput);
- QCoreApplication::sendEvent(focusObject, &staticVariables()->inputMethodQueryEvent);
- [self updateUITextInputDelegate:query];
-}
-
- (void)sendEventToFocusObject:(QEvent &)e
{
QObject *focusObject = QGuiApplication::focusObject();
@@ -221,32 +224,9 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
QCoreApplication::sendEvent(focusObject, &e);
}
-- (void)reset
-{
- [self setMarkedText:@"" selectedRange:NSMakeRange(0, 0)];
- [self updateInputMethodWithQuery:Qt::ImQueryInput];
-
- // Guard agains recursive callbacks by posting calls to UITextInput
- dispatch_async(dispatch_get_main_queue(), ^{
- [self updateKeyboardLayout];
- [self updateUITextInputDelegate:Qt::ImQueryInput];
- });
-}
-
-- (void)commit
-{
- [self unmarkText];
-
- // Guard agains recursive callbacks by posting calls to UITextInput
- dispatch_async(dispatch_get_main_queue(), ^{
- [self updateKeyboardLayout];
- [self updateUITextInputDelegate:Qt::ImSurroundingText];
- });
-}
-
- (QVariant)imValue:(Qt::InputMethodQuery)query
{
- return staticVariables()->inputMethodQueryEvent.value(query);
+ return m_inputContext->imeState().currentState.value(query);
}
-(id<UITextInputTokenizer>)tokenizer
@@ -274,7 +254,8 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
[self sendEventToFocusObject:e];
}
-- (UITextRange *)selectedTextRange {
+- (UITextRange *)selectedTextRange
+{
int cursorPos = [self imValue:Qt::ImCursorPosition].toInt();
int anchorPos = [self imValue:Qt::ImAnchorPosition].toInt();
return [QUITextRange rangeWithNSRange:NSMakeRange(qMin(cursorPos, anchorPos), qAbs(anchorPos - cursorPos))];
@@ -333,7 +314,8 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
return NSOrderedSame;
}
-- (UITextRange *)markedTextRange {
+- (UITextRange *)markedTextRange
+{
return m_markedText.isEmpty() ? nil : [QUITextRange rangeWithNSRange:NSMakeRange(0, m_markedText.length())];
}
@@ -377,7 +359,8 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
// to be relative to the view this method returns.
// Since QInputMethod returns rects relative to the top level
// QWindow, that is also the view we need to return.
- QPlatformWindow *topLevel = m_qioswindow;
+ Q_ASSERT(qApp->focusWindow()->handle());
+ QPlatformWindow *topLevel = qApp->focusWindow()->handle();
while (QPlatformWindow *p = topLevel->parent())
topLevel = p;
return reinterpret_cast<UIView *>(topLevel->winId());
@@ -530,14 +513,15 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
return;
if ([text isEqualToString:@"\n"]) {
- if (self.returnKeyType == UIReturnKeyDone)
- qApp->inputMethod()->hide();
-
QKeyEvent press(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier);
QKeyEvent release(QEvent::KeyRelease, Qt::Key_Return, Qt::NoModifier);
[self sendEventToFocusObject:press];
[self sendEventToFocusObject:release];
+ Qt::InputMethodHints imeHints = static_cast<Qt::InputMethodHints>([self imValue:Qt::ImHints].toUInt());
+ if (!(imeHints & Qt::ImhMultiLine))
+ m_inputContext->hideVirtualKeyboard();
+
return;
}
@@ -557,47 +541,4 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
[self sendEventToFocusObject:release];
}
-- (void)updateTextInputTraits
-{
- // Ask the current focus object what kind of input it
- // expects, and configure the keyboard appropriately:
- QObject *focusObject = QGuiApplication::focusObject();
- if (!focusObject)
- return;
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints);
- if (!QCoreApplication::sendEvent(focusObject, &queryEvent))
- return;
- if (!queryEvent.value(Qt::ImEnabled).toBool())
- return;
-
- Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt());
-
- self.returnKeyType = (hints & Qt::ImhMultiLine) ? UIReturnKeyDefault : UIReturnKeyDone;
- self.secureTextEntry = BOOL(hints & Qt::ImhHiddenText);
- self.autocorrectionType = (hints & Qt::ImhNoPredictiveText) ?
- UITextAutocorrectionTypeNo : UITextAutocorrectionTypeDefault;
- self.spellCheckingType = (hints & Qt::ImhNoPredictiveText) ?
- UITextSpellCheckingTypeNo : UITextSpellCheckingTypeDefault;
-
- if (hints & Qt::ImhUppercaseOnly)
- self.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
- else if (hints & Qt::ImhNoAutoUppercase)
- self.autocapitalizationType = UITextAutocapitalizationTypeNone;
- else
- self.autocapitalizationType = UITextAutocapitalizationTypeSentences;
-
- if (hints & Qt::ImhUrlCharactersOnly)
- self.keyboardType = UIKeyboardTypeURL;
- else if (hints & Qt::ImhEmailCharactersOnly)
- self.keyboardType = UIKeyboardTypeEmailAddress;
- else if (hints & Qt::ImhDigitsOnly)
- self.keyboardType = UIKeyboardTypeNumberPad;
- else if (hints & Qt::ImhFormattedNumbersOnly)
- self.keyboardType = UIKeyboardTypeDecimalPad;
- else if (hints & Qt::ImhDialableCharactersOnly)
- self.keyboardType = UIKeyboardTypeNumberPad;
- else
- self.keyboardType = UIKeyboardTypeDefault;
-}
-
@end
diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm
index aa33a9b21d..2874d272fe 100644
--- a/src/plugins/platforms/ios/qioswindow.mm
+++ b/src/plugins/platforms/ios/qioswindow.mm
@@ -264,12 +264,12 @@ void QIOSWindow::requestActivateWindow()
if (blockedByModal())
return;
+ Q_ASSERT(m_view.window);
[m_view.window makeKeyWindow];
+ [m_view becomeFirstResponder];
if (window()->isTopLevel())
raise();
-
- QWindowSystemInterface::handleWindowActivated(window());
}
void QIOSWindow::raiseOrLower(bool raise)
diff --git a/src/plugins/platforms/ios/quiview.h b/src/plugins/platforms/ios/quiview.h
index 99c710ffee..c5bf3b6cbe 100644
--- a/src/plugins/platforms/ios/quiview.h
+++ b/src/plugins/platforms/ios/quiview.h
@@ -56,42 +56,14 @@ class QIOSWindow;
QHash<UITouch *, QWindowSystemInterface::TouchPoint> m_activeTouches;
int m_nextTouchId;
- @public
- UITextAutocapitalizationType autocapitalizationType;
- UITextAutocorrectionType autocorrectionType;
- BOOL enablesReturnKeyAutomatically;
- UIKeyboardAppearance keyboardAppearance;
- UIKeyboardType keyboardType;
- UIReturnKeyType returnKeyType;
- BOOL secureTextEntry;
- QString m_markedText;
- BOOL m_inSendEventToFocusObject;
-
@private
NSMutableArray *m_accessibleElements;
}
-@property(nonatomic, assign) id<UITextInputDelegate> inputDelegate;
-@property(nonatomic) UITextAutocapitalizationType autocapitalizationType;
-@property(nonatomic) UITextAutocorrectionType autocorrectionType;
-@property(nonatomic) UITextSpellCheckingType spellCheckingType;
-@property(nonatomic) BOOL enablesReturnKeyAutomatically;
-@property(nonatomic) UIKeyboardAppearance keyboardAppearance;
-@property(nonatomic) UIKeyboardType keyboardType;
-@property(nonatomic) UIReturnKeyType returnKeyType;
-@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry;
-
- (id)initWithQIOSWindow:(QIOSWindow *)window;
- (void)sendUpdatedExposeEvent;
@end
-@interface QUIView (TextInput) <UITextInput>
-- (void)updateInputMethodWithQuery:(Qt::InputMethodQueries)query;
-- (void)reset;
-- (void)commit;
-+ (bool)inUpdateKeyboardLayout;
-@end
-
@interface QUIView (Accessibility)
- (void)clearAccessibleCache;
@end
diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm
index e534bdcd69..5687c078ea 100644
--- a/src/plugins/platforms/ios/quiview.mm
+++ b/src/plugins/platforms/ios/quiview.mm
@@ -50,14 +50,6 @@
@implementation QUIView
-@synthesize autocapitalizationType;
-@synthesize autocorrectionType;
-@synthesize enablesReturnKeyAutomatically;
-@synthesize keyboardAppearance;
-@synthesize keyboardType;
-@synthesize returnKeyType;
-@synthesize secureTextEntry;
-
+ (Class)layerClass
{
return [CAEAGLLayer class];
@@ -86,7 +78,6 @@
self.hidden = YES;
self.multipleTouchEnabled = YES;
- m_inSendEventToFocusObject = NO;
}
return self;
@@ -202,6 +193,46 @@
QWindowSystemInterface::flushWindowSystemEvents();
}
+// -------------------------------------------------------------------------
+
+- (BOOL)canBecomeFirstResponder
+{
+ return YES;
+}
+
+- (BOOL)becomeFirstResponder
+{
+ if ([super becomeFirstResponder]) {
+ QWindowSystemInterface::handleWindowActivated(m_qioswindow->window());
+ QWindowSystemInterface::flushWindowSystemEvents();
+
+ return YES;
+ }
+
+ return NO;
+}
+
+- (BOOL)resignFirstResponder
+{
+ if ([super resignFirstResponder]) {
+ // We don't want to send window deactivation in case we're in the process
+ // of activating another window. The handleWindowActivated of the activation
+ // will take care of both.
+ dispatch_async(dispatch_get_main_queue (), ^{
+ if (![[UIResponder currentFirstResponder] isKindOfClass:[QUIView class]])
+ QWindowSystemInterface::handleWindowActivated(0);
+ QWindowSystemInterface::flushWindowSystemEvents();
+ });
+
+ return YES;
+ }
+
+ return NO;
+}
+
+// -------------------------------------------------------------------------
+
+
- (void)updateTouchList:(NSSet *)touches withState:(Qt::TouchPointState)state
{
// We deliver touch events in global coordinates. But global in this respect
@@ -342,5 +373,4 @@
@end
// Include category as an alternative to using -ObjC (Apple QA1490)
-#include "quiview_textinput.mm"
#include "quiview_accessibility.mm"