summaryrefslogtreecommitdiffstats
path: root/src/platformsupport
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@digia.com>2013-09-02 19:43:16 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-12 16:47:54 +0200
commit5d926657f4149cde9aaa447808785abda835147f (patch)
treed523245019f3f491bc171d6aa79acc2e83ac75ca /src/platformsupport
parent880b614c8ffd530251b9383e085ba094a1edbca8 (diff)
iOS: Teach event-dispatcher to handle changes to run-loop mode at runtime
UIKit changes the run-loop mode during scrolling to UITrackingMode, which presumably prioritizes touch events and other sources related to a smooth scrolling experience. It signals this change by interrupting the current run-loop pass, and the outer loop is responsible for re-entering the run-loop in the new mode. Failing to enter the run-loop with this new mode results in UIScrollViews losing their kinetic feel when flicking. This can be observed by e.g. bringing up the Emoji keyboard and scrolling it horizontally. We keep track of the current run-loop mode by listening for push and pop notifications on the UIApplication object. The current mode is then used in our Q_FOREVER-loop when re-entering CFRunLoopRunInMode. For now we don't add our posted event source or timer source to the new modes, under the assumption that the system prefers to limit the number of sources that will fire during scrolling. If this turns out to give a bad user-experience for Qt applications we should consider changing it. Change-Id: I3a612b3cfc77c74b658963057732dc4d61684df8 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@digia.com>
Diffstat (limited to 'src/platformsupport')
-rw-r--r--src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm71
-rw-r--r--src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h8
2 files changed, 77 insertions, 2 deletions
diff --git a/src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm b/src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm
index c0080a0daa..ab26b4c1d8 100644
--- a/src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm
+++ b/src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm
@@ -55,6 +55,71 @@
#include <UIKit/UIApplication.h>
+@interface RunLoopModeTracker : NSObject {
+ QStack<CFStringRef> m_runLoopModes;
+}
+@end
+
+@implementation RunLoopModeTracker
+
+- (id) init
+{
+ if (self = [super init]) {
+ m_runLoopModes.push(kCFRunLoopDefaultMode);
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(receivedNotification:)
+ name:nil
+ object:[UIApplication sharedApplication]];
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [super dealloc];
+}
+
+static CFStringRef runLoopMode(NSDictionary *dictionary)
+{
+ for (NSString *key in dictionary) {
+ if (CFStringHasSuffix((CFStringRef)key, CFSTR("RunLoopMode")))
+ return (CFStringRef)[dictionary objectForKey: key];
+ }
+
+ return nil;
+}
+
+- (void) receivedNotification:(NSNotification *) notification
+{
+ if (CFStringHasSuffix((CFStringRef)notification.name, CFSTR("RunLoopModePushNotification"))) {
+ if (CFStringRef mode = runLoopMode(notification.userInfo))
+ m_runLoopModes.push(mode);
+ else
+ qWarning("Encountered run loop push notification without run loop mode!");
+
+ } else if (CFStringHasSuffix((CFStringRef)notification.name, CFSTR("RunLoopModePopNotification"))) {
+ CFStringRef mode = runLoopMode(notification.userInfo);
+ if (CFStringCompare(mode, [self currentMode], 0) == kCFCompareEqualTo)
+ m_runLoopModes.pop();
+ else
+ qWarning("Tried to pop run loop mode '%s' that was never pushed!", qPrintable(QCFString::toQString(mode)));
+
+ Q_ASSERT(m_runLoopModes.size() >= 1);
+ }
+}
+
+- (CFStringRef) currentMode
+{
+ return m_runLoopModes.top();
+}
+
+@end
+
QT_BEGIN_NAMESPACE
QT_USE_NAMESPACE
@@ -120,6 +185,7 @@ QEventDispatcherCoreFoundation::QEventDispatcherCoreFoundation(QObject *parent)
kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting
#endif
)
+ , m_runLoopModeTracker([[RunLoopModeTracker alloc] init])
, m_runLoopTimer(0)
, m_blockedRunLoopTimer(0)
, m_overdueTimerScheduled(false)
@@ -195,11 +261,11 @@ bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlag
ProcessEventsState previousState = m_processEvents;
m_processEvents = ProcessEventsState(flags);
- CFStringRef mode = kCFRunLoopDefaultMode;
-
bool returnAfterSingleSourceHandled = !(m_processEvents.flags & QEventLoop::EventLoopExec);
Q_FOREVER {
+ CFStringRef mode = [m_runLoopModeTracker currentMode];
+
CFTimeInterval duration = (m_processEvents.flags & QEventLoop::WaitForMoreEvents) ?
kCFTimeIntervalDistantFuture : kCFTimeIntervalMinimum;
@@ -552,5 +618,6 @@ void QEventDispatcherCoreFoundation::invalidateTimer()
}
#include "qeventdispatcher_cf.moc"
+#include "moc_qeventdispatcher_cf_p.cpp"
QT_END_NAMESPACE
diff --git a/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h b/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h
index bd62927f09..0b9dda2b7f 100644
--- a/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h
+++ b/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h
@@ -84,6 +84,12 @@
#include <QtPlatformSupport/private/qcfsocketnotifier_p.h>
#include <CoreFoundation/CoreFoundation.h>
+#ifdef __OBJC__
+@class RunLoopModeTracker;
+#else
+typedef struct objc_object RunLoopModeTracker;
+#endif
+
QT_BEGIN_NAMESPACE
class QEventDispatcherCoreFoundation;
@@ -206,6 +212,8 @@ private:
RunLoopSource<> m_postedEventsRunLoopSource;
RunLoopObserver<> m_runLoopActivityObserver;
+ RunLoopModeTracker *m_runLoopModeTracker;
+
QTimerInfoList m_timerInfoList;
CFRunLoopTimerRef m_runLoopTimer;
CFRunLoopTimerRef m_blockedRunLoopTimer;