diff options
-rw-r--r-- | src/plugins/platforms/ios/ios.pro | 2 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosinputcontext.h | 19 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosinputcontext.mm | 177 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiostextresponder.h | 74 | ||||
-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.mm | 4 | ||||
-rw-r--r-- | src/plugins/platforms/ios/quiview.h | 28 | ||||
-rw-r--r-- | src/plugins/platforms/ios/quiview.mm | 50 |
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" |