summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms
diff options
context:
space:
mode:
authorRichard Moe Gustavsen <richard.gustavsen@digia.com>2013-12-11 12:16:19 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-02-06 14:19:19 +0100
commitd44b6da7c243ded8a5cae1d0e2a77c8ef74c33f6 (patch)
tree20d7d1b897cbd14e95c2b1fcf2303dd8d65a75e0 /src/plugins/platforms
parentf864bdaf592412acccae6b6c561a76bbfe35a1f0 (diff)
iOS: change logic for when to scroll screen
The current implementation will stop scrolling the screen to reveal the cursor if the input item changes transformation. This to not interfere with flicking etc. This strategy turns out to be too strict, as some qml apps/games can easily have small animations applied (e.g qtquick cork board example) that moves or scales the text areas (or their parents) upon focus. So instead of relying on input item transformation, we now scroll whenever the cursor changes position inside the input item (in addition to orientation changes etc). We also refactor scrollRootView into two functions, since we in many cases know if the keyboard should scroll up or down already when the call is made. Change-Id: If5bf349139eed69823cfc8986bb4b32c93bdf91b Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@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.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];
}
+