diff options
author | Robert Griebl <robert.griebl@pelagicore.com> | 2017-10-24 17:32:28 +0200 |
---|---|---|
committer | Dominik Holland <dominik.holland@pelagicore.com> | 2017-11-03 10:13:12 +0000 |
commit | e2c4d58dcdd88e560506c8158f39b59ee10ddba7 (patch) | |
tree | 56e75ae02b2f6d5408bc62f53eeb827100cef63b /src | |
parent | a2f468bd5a6774023b55007ab008cbd788f77387 (diff) |
Fix single-process race condition when emitting windowReady
Make sure that the WindowManager::windowReady signal is only emitted after all
QML Component.onCompleted handlers having been dealt with. Since Qt does not
provide a suitable API for this, we need to hook into the low-level signal
emission.
Task-number: AUTOSUITE-134
Change-Id: I5ec4260aca7b4a505b48892dbc255f224e3559d7
Reviewed-by: Dominik Holland <dominik.holland@pelagicore.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/manager-lib/fakeapplicationmanagerwindow.cpp | 35 | ||||
-rw-r--r-- | src/manager-lib/fakeapplicationmanagerwindow.h | 4 | ||||
-rw-r--r-- | src/manager-lib/manager-lib.pro | 2 |
3 files changed, 38 insertions, 3 deletions
diff --git a/src/manager-lib/fakeapplicationmanagerwindow.cpp b/src/manager-lib/fakeapplicationmanagerwindow.cpp index ca0e96fa..546ee569 100644 --- a/src/manager-lib/fakeapplicationmanagerwindow.cpp +++ b/src/manager-lib/fakeapplicationmanagerwindow.cpp @@ -43,6 +43,9 @@ #include "fakeapplicationmanagerwindow.h" #include "qmlinprocessruntime.h" #include <QSGSimpleRectNode> +#include <QQmlComponent> +#include <private/qqmlcomponentattached_p.h> + QT_BEGIN_NAMESPACE_AM @@ -182,8 +185,36 @@ void FakeApplicationManagerWindow::componentComplete() prnt = prnt->parent(); } - if (m_runtime && isVisible()) - m_runtime->addWindow(this); + // This part is scary, but we need to make sure that all Component.onComplete: handlers on + // the QML side have been run, before we hand this window over to the WindowManager for the + // onWindowReady signal. The problem here is that the C++ componentComplete() handler (this + // function) is called *before* the QML side, plus, to make matters worse, the QML incubator + // could switch back to the event loop a couple of times before finally calling the QML + // onCompleted handler(s). + // The workaround is to setup watchers for all Component.onCompleted handlers for this object + // and wait until the last of them has been dealt with, to finally call our addWindow + // function (we are also relying on the signal emission order, so that our lambda is called + // after the actual QML handler). + + for (auto a = QQmlComponent::qmlAttachedProperties(this); a; a = a->next) { + auto famw = qobject_cast<FakeApplicationManagerWindow *>(a->parent()); + if (!famw || famw != this) + continue; + + m_attachedCompleteHandlers << a; + + connect(a, &QQmlComponentAttached::completed, this, [this, a]() { + m_attachedCompleteHandlers.removeAll(a); + + if (m_attachedCompleteHandlers.isEmpty()) + onVisibleChanged(); + }); + } + + // If we do not even have a single Component.onCompleted handler on the QML side, we need to + // show the window immediately. + if (m_attachedCompleteHandlers.isEmpty()) + onVisibleChanged(); } void FakeApplicationManagerWindow::onVisibleChanged() diff --git a/src/manager-lib/fakeapplicationmanagerwindow.h b/src/manager-lib/fakeapplicationmanagerwindow.h index 6ef41b2d..a526109b 100644 --- a/src/manager-lib/fakeapplicationmanagerwindow.h +++ b/src/manager-lib/fakeapplicationmanagerwindow.h @@ -45,6 +45,8 @@ #include <QQuickItem> #include <QtAppManCommon/global.h> +QT_FORWARD_DECLARE_CLASS(QQmlComponentAttached) + QT_BEGIN_NAMESPACE_AM class QmlInProcessRuntime; @@ -121,6 +123,8 @@ private: QmlInProcessRuntime *m_runtime = nullptr; QColor m_color; + QVector<QQmlComponentAttached *> m_attachedCompleteHandlers; + friend class QmlInProcessRuntime; // for setting the m_runtime member }; diff --git a/src/manager-lib/manager-lib.pro b/src/manager-lib/manager-lib.pro index f84f878d..221aa07d 100644 --- a/src/manager-lib/manager-lib.pro +++ b/src/manager-lib/manager-lib.pro @@ -5,7 +5,7 @@ MODULE = appman_manager load(am-config) QT = core network qml -!headless:QT *= gui quick +!headless:QT *= gui quick qml-private qtHaveModule(dbus):QT *= dbus QT_FOR_PRIVATE *= \ appman_common-private \ |