summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.h8
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.mm118
2 files changed, 76 insertions, 50 deletions
diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h
index 533ba686e1..91a6939ad4 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.h
+++ b/src/plugins/platforms/ios/qiosinputcontext.h
@@ -64,14 +64,14 @@ public:
void setFocusObject(QObject *object);
void focusWindowChanged(QWindow *focusWindow);
- void scrollRootView();
-
+ void cursorRectangleChanged();
+ void scrollToCursor();
+ void scroll(int y);
private:
QIOSKeyboardListener *m_keyboardListener;
UIView<UIKeyInput> *m_focusView;
- QTransform m_inputItemTransform;
bool m_hasPendingHideRequest;
- bool m_inSetFocusObject;
+ QObject *m_focusObject;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm
index 39a22f367e..c25d76518f 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.mm
+++ b/src/plugins/platforms/ios/qiosinputcontext.mm
@@ -49,6 +49,7 @@
QIOSInputContext *m_context;
BOOL m_keyboardVisible;
BOOL m_keyboardVisibleAndDocked;
+ BOOL m_ignoreKeyboardChanges;
QRectF m_keyboardRect;
QRectF m_keyboardEndRect;
NSTimeInterval m_duration;
@@ -66,6 +67,7 @@
m_context = context;
m_keyboardVisible = NO;
m_keyboardVisibleAndDocked = NO;
+ m_ignoreKeyboardChanges = NO;
m_duration = 0;
m_curve = UIViewAnimationCurveEaseOut;
m_viewController = 0;
@@ -128,6 +130,8 @@
- (void) keyboardDidChangeFrame:(NSNotification *)notification
{
+ if (m_ignoreKeyboardChanges)
+ return;
m_keyboardRect = [self getKeyboardRect:notification];
m_context->emitKeyboardRectChanged();
@@ -140,11 +144,13 @@
// If the keyboard was visible and docked from before, this is just a geometry
// change (normally caused by an orientation change). In that case, update scroll:
if (m_keyboardVisibleAndDocked)
- m_context->scrollRootView();
+ m_context->scrollToCursor();
}
- (void) keyboardWillShow:(NSNotification *)notification
{
+ if (m_ignoreKeyboardChanges)
+ return;
// Note that UIKeyboardWillShowNotification is only sendt when the keyboard is docked.
m_keyboardVisibleAndDocked = YES;
m_keyboardEndRect = [self getKeyboardRect:notification];
@@ -152,15 +158,17 @@
m_duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
m_curve = UIViewAnimationCurve([notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16);
}
- m_context->scrollRootView();
+ m_context->scrollToCursor();
}
- (void) keyboardWillHide:(NSNotification *)notification
{
+ if (m_ignoreKeyboardChanges)
+ return;
// Note that UIKeyboardWillHideNotification is also sendt when the keyboard is undocked.
m_keyboardVisibleAndDocked = NO;
m_keyboardEndRect = [self getKeyboardRect:notification];
- m_context->scrollRootView();
+ m_context->scroll(0);
}
@end
@@ -170,10 +178,10 @@ QIOSInputContext::QIOSInputContext()
, m_keyboardListener([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this])
, m_focusView(0)
, m_hasPendingHideRequest(false)
- , m_inSetFocusObject(false)
+ , m_focusObject(0)
{
if (isQtApplication())
- connect(qGuiApp->inputMethod(), &QInputMethod::cursorRectangleChanged, this, &QIOSInputContext::scrollRootView);
+ connect(qGuiApp->inputMethod(), &QInputMethod::cursorRectangleChanged, this, &QIOSInputContext::cursorRectangleChanged);
connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &QIOSInputContext::focusWindowChanged);
}
@@ -216,12 +224,14 @@ bool QIOSInputContext::isInputPanelVisible() const
return m_keyboardListener->m_keyboardVisible;
}
-void QIOSInputContext::setFocusObject(QObject *)
+void QIOSInputContext::setFocusObject(QObject *focusObject)
{
- m_inputItemTransform = qApp->inputMethod()->inputItemTransform();
+ m_focusObject = focusObject;
- if (!m_focusView || !m_focusView.isFirstResponder)
+ if (!m_focusView || !m_focusView.isFirstResponder) {
+ scroll(0);
return;
+ }
// Since m_focusView is the first responder, it means that the keyboard is open and we
// should update keyboard layout. But there seem to be no way to tell it to reread the
@@ -230,62 +240,78 @@ void QIOSInputContext::setFocusObject(QObject *)
// go, we need to call the super implementation of resignFirstResponder. Since the call
// will cause a 'keyboardWillHide' notification to be sendt, we also block scrollRootView
// to avoid artifacts:
- m_inSetFocusObject = true;
+ m_keyboardListener->m_ignoreKeyboardChanges = true;
SEL sel = @selector(resignFirstResponder);
[[m_focusView superclass] instanceMethodForSelector:sel](m_focusView, sel);
- m_inSetFocusObject = false;
[m_focusView becomeFirstResponder];
+ m_keyboardListener->m_ignoreKeyboardChanges = false;
+
+ if (m_keyboardListener->m_keyboardVisibleAndDocked)
+ scrollToCursor();
}
void QIOSInputContext::focusWindowChanged(QWindow *focusWindow)
{
- UIView<UIKeyInput> *view = reinterpret_cast<UIView<UIKeyInput> *>(focusWindow->handle()->winId());
+ UIView<UIKeyInput> *view = focusWindow ?
+ reinterpret_cast<UIView<UIKeyInput> *>(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);
}
-void QIOSInputContext::scrollRootView()
+void QIOSInputContext::cursorRectangleChanged()
{
- // Scroll the root view (screen) if:
- // - our backend controls the root view controller on the main screen (no hybrid app)
- // - the focus object is on the same screen as the keyboard.
- // - the first responder is a QUIView, and not some other foreign UIView.
- // - the keyboard is docked. Otherwise the user can move the keyboard instead.
- // - the inputItem has not been moved/scrolled
- if (!isQtApplication() || !m_focusView || m_inSetFocusObject)
+ if (!m_keyboardListener->m_keyboardVisibleAndDocked)
return;
- if (m_inputItemTransform != qApp->inputMethod()->inputItemTransform()) {
- // The inputItem has moved since the last scroll update. To avoid competing
- // with the application where the cursor/inputItem should be, we bail:
+ // Check if the cursor has changed position inside the input item. Since
+ // qApp->inputMethod()->cursorRectangle() will also change when the input item
+ // itself moves, we need to ask the focus object for ImCursorRectangle:
+ static QPoint prevCursor;
+ QInputMethodQueryEvent queryEvent(Qt::ImCursorRectangle);
+ QCoreApplication::sendEvent(m_focusObject, &queryEvent);
+ QPoint cursor = queryEvent.value(Qt::ImCursorRectangle).toRect().topLeft();
+ if (cursor != prevCursor)
+ scrollToCursor();
+ prevCursor = cursor;
+}
+
+void QIOSInputContext::scrollToCursor()
+{
+ if (!isQtApplication() || !m_focusView)
return;
- }
UIView *view = m_keyboardListener->m_viewController.view;
- qreal scrollTo = 0;
-
- if (m_focusView.isFirstResponder
- && m_keyboardListener->m_keyboardVisibleAndDocked
- && m_focusView.window == view.window) {
- QRectF cursorRect = qGuiApp->inputMethod()->cursorRectangle();
- cursorRect.translate(m_focusView.qwindow->geometry().topLeft());
- qreal keyboardY = m_keyboardListener->m_keyboardEndRect.y();
- int statusBarY = qGuiApp->primaryScreen()->availableGeometry().y();
- const int margin = 20;
-
- if (cursorRect.bottomLeft().y() > keyboardY - margin)
- scrollTo = qMin(view.bounds.size.height - keyboardY, cursorRect.y() - statusBarY - margin);
- }
+ if (view.window != m_focusView.window)
+ return;
- if (scrollTo != view.bounds.origin.y) {
- // Scroll the view the same way a UIScrollView works: by changing bounds.origin:
- CGRect newBounds = view.bounds;
- newBounds.origin.y = scrollTo;
- [UIView animateWithDuration:m_keyboardListener->m_duration delay:0
- options:m_keyboardListener->m_curve
- animations:^{ view.bounds = newBounds; }
- completion:0];
- }
+ const int margin = 20;
+ QRectF translatedCursorPos = qApp->inputMethod()->cursorRectangle();
+ translatedCursorPos.translate(m_focusView.qwindow->geometry().topLeft());
+ qreal keyboardY = m_keyboardListener->m_keyboardEndRect.y();
+ int statusBarY = qGuiApp->primaryScreen()->availableGeometry().y();
+
+ scroll((translatedCursorPos.bottomLeft().y() < keyboardY - margin) ? 0
+ : qMin(view.bounds.size.height - keyboardY, translatedCursorPos.y() - statusBarY - margin));
+}
+
+void QIOSInputContext::scroll(int y)
+{
+ // Scroll the view the same way a UIScrollView
+ // works: by changing bounds.origin:
+ UIView *view = m_keyboardListener->m_viewController.view;
+ if (y == view.bounds.origin.y)
+ return;
+
+ CGRect newBounds = view.bounds;
+ newBounds.origin.y = y;
+ [UIView animateWithDuration:m_keyboardListener->m_duration delay:0
+ options:m_keyboardListener->m_curve
+ animations:^{ view.bounds = newBounds; }
+ completion:0];
}
+