diff options
author | Bernd Weimer <bernd.weimer@pelagicore.com> | 2017-10-16 16:11:22 +0200 |
---|---|---|
committer | Robert Griebl <robert.griebl@pelagicore.com> | 2017-11-27 15:59:25 +0000 |
commit | e6d5d275f3fa7057701c19b3a399a49dcd073e51 (patch) | |
tree | b04071790269299c1fd6c4c2a3d5b1abcb9ce9b9 | |
parent | 4d205ac87daeedbb34d0d886c762d5834aa434fb (diff) |
Hide properties in FakeApplicationManagerWindow
To mitigate the difference between single- and multi-process mode,
properties and signals in FakeApplicationManagerWindow (Item) will be
hidden, that are not available in ApplicationManagerWindow (Window).
The same warnings will be printed and applications will not start,
which use such properties. This patch only hides "parent" - it
provides a base for further properties that will be hidden.
To achieve this an InProcessSurfaceItem has been introduced that
is actually exposed to the systemUI and serves as an item parent for
FakeApplicationManagerWindows.
Also changed the lifetime of the application's object hierarchy.
Task-number: AUTOSUITE-143
Change-Id: Ib4881ec39267907fb24bebbd167796270661ab8c
Reviewed-by: Robert Griebl <robert.griebl@pelagicore.com>
32 files changed, 942 insertions, 181 deletions
diff --git a/src/manager-lib/abstractruntime.cpp b/src/manager-lib/abstractruntime.cpp index 521ea49b..c9f63987 100644 --- a/src/manager-lib/abstractruntime.cpp +++ b/src/manager-lib/abstractruntime.cpp @@ -93,6 +93,13 @@ QVariantMap AbstractRuntime::systemProperties() const return QVariantMap(); } +#if !defined(AM_HEADLESS) +void AbstractRuntime::inProcessSurfaceItemReleased(QQuickItem *) +{ + // generally there is nothing to do +} +#endif + QByteArray AbstractRuntime::securityToken() const { return m_securityToken; diff --git a/src/manager-lib/abstractruntime.h b/src/manager-lib/abstractruntime.h index eddfd703..a6cad10b 100644 --- a/src/manager-lib/abstractruntime.h +++ b/src/manager-lib/abstractruntime.h @@ -133,6 +133,10 @@ public: virtual bool start() = 0; virtual void stop(bool forceKill = false) = 0; +#if !defined(AM_HEADLESS) + virtual void inProcessSurfaceItemReleased(QQuickItem *); +#endif + signals: void stateChanged(QT_PREPEND_NAMESPACE_AM(AbstractRuntime::State) newState); void finished(int exitCode, QProcess::ExitStatus status); diff --git a/src/manager-lib/fakeapplicationmanagerwindow.cpp b/src/manager-lib/fakeapplicationmanagerwindow.cpp index 546ee569..c6a8a736 100644 --- a/src/manager-lib/fakeapplicationmanagerwindow.cpp +++ b/src/manager-lib/fakeapplicationmanagerwindow.cpp @@ -41,25 +41,65 @@ #include "logging.h" #include "fakeapplicationmanagerwindow.h" +#include "inprocesssurfaceitem.h" #include "qmlinprocessruntime.h" #include <QSGSimpleRectNode> #include <QQmlComponent> +#include <QQmlEngine> +#include <QQmlContext> +#include <QQmlInfo> #include <private/qqmlcomponentattached_p.h> QT_BEGIN_NAMESPACE_AM +static QByteArray nameToKey(const QString &name) +{ + return QByteArray("_am_") + name.toUtf8(); +} + +static QString keyToName(const QByteArray &key) +{ + return QString::fromUtf8(key.mid(4)); +} + +static bool isName(const QByteArray &key) +{ + return key.startsWith("_am_"); +} + + FakeApplicationManagerWindow::FakeApplicationManagerWindow(QQuickItem *parent) : QQuickItem(parent) + , m_windowProperties(new QObject) { - qCDebug(LogSystem) << "FakeApplicationManagerWindow ctor! this:" << this; setFlag(ItemHasContents); connect(this, &QQuickItem::visibleChanged, this, &FakeApplicationManagerWindow::onVisibleChanged); + + m_windowProperties.data()->installEventFilter(this); } FakeApplicationManagerWindow::~FakeApplicationManagerWindow() { - qCDebug(LogSystem) << "FakeApplicationManagerWindow dtor! this: " << this; + if (m_surfaceItem) { + m_runtime->removeWindow(this); + m_surfaceItem->m_contentItem = nullptr; + } +} + +bool FakeApplicationManagerWindow::isFakeVisible() const +{ + return m_fakeVisible; +} + +void FakeApplicationManagerWindow::setFakeVisible(bool visible) +{ + if (visible != m_fakeVisible) { + m_fakeVisible = visible; + setVisible(visible); + if (m_surfaceItem) + m_surfaceItem->setVisible(visible); + } } QColor FakeApplicationManagerWindow::color() const @@ -94,45 +134,27 @@ void FakeApplicationManagerWindow::showNormal() // doesn't work in wayland right now, so do nothing... revisit later (after andies resize-redesign) } - - -static QByteArray nameToKey(const QString &name) -{ - return QByteArray("_am_") + name.toUtf8(); -} - -static QString keyToName(const QByteArray &key) -{ - return QString::fromUtf8(key.mid(4)); -} - -static bool isName(const QByteArray &key) -{ - return key.startsWith("_am_"); -} - - bool FakeApplicationManagerWindow::setWindowProperty(const QString &name, const QVariant &value) { QByteArray key = nameToKey(name); - QVariant oldValue = property(key); + QVariant oldValue = m_windowProperties->property(key); bool changed = !oldValue.isValid() || (oldValue != value); - if (changed) { - setProperty(key, value); - } + if (changed) + m_windowProperties->setProperty(key, value); + return true; } QVariant FakeApplicationManagerWindow::windowProperty(const QString &name) const { QByteArray key = nameToKey(name); - return property(key); + return m_windowProperties->property(key); } QVariantMap FakeApplicationManagerWindow::windowProperties() const { - const QList<QByteArray> keys = dynamicPropertyNames(); + const QList<QByteArray> keys = m_windowProperties->dynamicPropertyNames(); QVariantMap map; for (const QByteArray &key : keys) { @@ -140,27 +162,25 @@ QVariantMap FakeApplicationManagerWindow::windowProperties() const continue; QString name = keyToName(key); - map[name] = property(key); + map[name] = m_windowProperties->property(key); } return map; } -bool FakeApplicationManagerWindow::event(QEvent *e) +bool FakeApplicationManagerWindow::eventFilter(QObject *o, QEvent *e) { - if (e->type() == QEvent::DynamicPropertyChange) { + if ((o == m_windowProperties) && (e->type() == QEvent::DynamicPropertyChange)) { QDynamicPropertyChangeEvent *dpce = static_cast<QDynamicPropertyChangeEvent *>(e); QByteArray key = dpce->propertyName(); if (isName(key)) { QString name = keyToName(dpce->propertyName()); - emit windowPropertyChanged(name, property(key)); - - //qWarning() << "FPW: got change" << name << " --> " << property(key).toString(); + emit windowPropertyChanged(name, m_windowProperties->property(key)); } } - return QQuickItem::event(e); + return QQuickItem::eventFilter(o, e); } QSGNode *FakeApplicationManagerWindow::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) @@ -173,17 +193,24 @@ QSGNode *FakeApplicationManagerWindow::updatePaintNode(QSGNode *oldNode, QQuickI return node; } +void FakeApplicationManagerWindow::determineRuntime() +{ + if (!m_runtime) { + QQmlContext *ctx = QQmlEngine::contextForObject(this); + while (ctx && !m_runtime) { + if (ctx->property(QmlInProcessRuntime::s_runtimeKey).isValid()) + m_runtime = ctx->property(QmlInProcessRuntime::s_runtimeKey).value<QmlInProcessRuntime*>(); + ctx = ctx->parentContext(); + } + } +} + void FakeApplicationManagerWindow::componentComplete() { qCDebug(LogSystem) << "FakeApplicationManagerWindow componentComplete() this:" << this; QQuickItem::componentComplete(); - - QObject *prnt = parent(); - while (prnt && !m_runtime) { - m_runtime = prnt->property("AM-RUNTIME").value<QtAM::QmlInProcessRuntime*>(); - prnt = prnt->parent(); - } + determineRuntime(); // 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 @@ -223,4 +250,26 @@ void FakeApplicationManagerWindow::onVisibleChanged() m_runtime->addWindow(this); } +QJSValue FakeApplicationManagerWindow::getUndefined() const +{ + return QJSValue(); +} + +void FakeApplicationManagerWindow::connectNotify(const QMetaMethod &signal) +{ + static int parentMetaIdx = FakeApplicationManagerWindow::staticMetaObject.indexOfSignal("parentChanged(QQuickItem*)"); + + if (signal.methodIndex() == parentMetaIdx) { + determineRuntime(); + if (m_runtime) + m_runtime->m_componentError = true; + +#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) + qWarning() << "QML ApplicationManagerWindow: Cannot assign to non-existent property \"onParentChanged\""; +#else + qmlWarning(this) << "Cannot assign to non-existent property \"onParentChanged\""; +#endif + } +} + QT_END_NAMESPACE_AM diff --git a/src/manager-lib/fakeapplicationmanagerwindow.h b/src/manager-lib/fakeapplicationmanagerwindow.h index eed6112b..4a0b41d6 100644 --- a/src/manager-lib/fakeapplicationmanagerwindow.h +++ b/src/manager-lib/fakeapplicationmanagerwindow.h @@ -51,6 +51,7 @@ QT_FORWARD_DECLARE_CLASS(QQmlComponentAttached) QT_BEGIN_NAMESPACE_AM class QmlInProcessRuntime; +class InProcessSurfaceItem; class FakeApplicationManagerWindow : public QQuickItem { @@ -58,6 +59,8 @@ class FakeApplicationManagerWindow : public QQuickItem Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QString title READ dummyGetterString WRITE dummySetterString) + Q_PROPERTY(bool visible READ isFakeVisible WRITE setFakeVisible NOTIFY visibleChanged FINAL) + // for API compatibility with QWaylandQuickItem - we cannot really simulate these, // but at least the QML code will not throw errors due to missing properties. Q_PROPERTY(bool paintEnabled READ dummyGetter WRITE dummySetter) @@ -65,6 +68,8 @@ class FakeApplicationManagerWindow : public QQuickItem Q_PROPERTY(bool inputEventsEnabled READ dummyGetter WRITE dummySetter) Q_PROPERTY(bool focusOnClick READ dummyGetter WRITE dummySetter) + Q_PROPERTY(QJSValue parent READ getUndefined CONSTANT) + public: explicit FakeApplicationManagerWindow(QQuickItem *parent = nullptr); ~FakeApplicationManagerWindow(); @@ -72,6 +77,9 @@ public: QColor color() const; void setColor(const QColor &c); + bool isFakeVisible() const; + void setFakeVisible(bool visible); + Q_INVOKABLE bool setWindowProperty(const QString &name, const QVariant &value); Q_INVOKABLE QVariant windowProperty(const QString &name) const; Q_INVOKABLE QVariantMap windowProperties() const; @@ -109,24 +117,32 @@ signals: void fakeNoFullScreenSignal(); // TODO this should be replaced by 'normal' and 'maximized' as soon as needed void windowPropertyChanged(const QString &name, const QVariant &value); void colorChanged(); + void visibleChanged(); protected: - bool event(QEvent *e) override; + bool eventFilter(QObject *o, QEvent *e) override; QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override; + void connectNotify(const QMetaMethod &signal) override; private: bool dummyGetter() const { return false; } void dummySetter(bool) { } QString dummyGetterString() const { return QString(); } void dummySetterString(const QString&) {} + + void determineRuntime(); void onVisibleChanged(); + QJSValue getUndefined() const; + InProcessSurfaceItem *m_surfaceItem = nullptr; + QSharedPointer<QObject> m_windowProperties; QmlInProcessRuntime *m_runtime = nullptr; QColor m_color; - + bool m_fakeVisible = true; QVector<QQmlComponentAttached *> m_attachedCompleteHandlers; friend class QmlInProcessRuntime; // for setting the m_runtime member + friend class InProcessSurfaceItem; }; QT_END_NAMESPACE_AM diff --git a/src/manager-lib/inprocesssurfaceitem.cpp b/src/manager-lib/inprocesssurfaceitem.cpp new file mode 100644 index 00000000..a18808c4 --- /dev/null +++ b/src/manager-lib/inprocesssurfaceitem.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include "inprocesssurfaceitem.h" +#include "fakeapplicationmanagerwindow.h" + +QT_BEGIN_NAMESPACE_AM + +InProcessSurfaceItem::InProcessSurfaceItem(FakeApplicationManagerWindow *content) + : m_contentItem(content) +{ + content->m_surfaceItem = this; + m_windowProperties = content->m_windowProperties; + setParentItem(content->parentItem()); + content->setParentItem(this); +} + +InProcessSurfaceItem::~InProcessSurfaceItem() +{ + if (m_contentItem) + m_contentItem->m_surfaceItem = nullptr; +} + +QSharedPointer<QObject> InProcessSurfaceItem::windowProperties() +{ + return m_windowProperties; +} + +void InProcessSurfaceItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickItem::geometryChanged(newGeometry, oldGeometry); + if (m_contentItem) { + m_contentItem->setWidth(newGeometry.width()); + m_contentItem->setHeight(newGeometry.height()); + } +} + +QT_END_NAMESPACE_AM diff --git a/src/manager-lib/inprocesssurfaceitem.h b/src/manager-lib/inprocesssurfaceitem.h new file mode 100644 index 00000000..b9da7447 --- /dev/null +++ b/src/manager-lib/inprocesssurfaceitem.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#pragma once + +#if !defined(AM_HEADLESS) + +#include <QQuickItem> +#include <QtAppManCommon/global.h> + +QT_BEGIN_NAMESPACE_AM + +class FakeApplicationManagerWindow; + +/* + * Item exposed to the system UI, FakeApplicationManagerWindows will be wrapped inside those. + */ +class InProcessSurfaceItem : public QQuickItem +{ + Q_OBJECT +public: + InProcessSurfaceItem(FakeApplicationManagerWindow *famw); + ~InProcessSurfaceItem(); + + QSharedPointer<QObject> windowProperties(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + +private: + FakeApplicationManagerWindow *m_contentItem = nullptr; + QSharedPointer<QObject> m_windowProperties; + + friend class QmlInProcessRuntime; + friend class FakeApplicationManagerWindow; +}; + +QT_END_NAMESPACE_AM + +#endif // !AM_HEADLESS diff --git a/src/manager-lib/manager-lib.pro b/src/manager-lib/manager-lib.pro index 221aa07d..93f728f8 100644 --- a/src/manager-lib/manager-lib.pro +++ b/src/manager-lib/manager-lib.pro @@ -43,6 +43,7 @@ linux:HEADERS += \ !headless:HEADERS += \ fakeapplicationmanagerwindow.h \ + inprocesssurfaceitem.h multi-process:HEADERS += \ nativeruntime.h \ @@ -74,6 +75,7 @@ linux:SOURCES += \ !headless:SOURCES += \ fakeapplicationmanagerwindow.cpp \ + inprocesssurfaceitem.cpp multi-process:SOURCES += \ nativeruntime.cpp \ diff --git a/src/manager-lib/qmlinprocessruntime.cpp b/src/manager-lib/qmlinprocessruntime.cpp index 33364e97..51e7d0da 100644 --- a/src/manager-lib/qmlinprocessruntime.cpp +++ b/src/manager-lib/qmlinprocessruntime.cpp @@ -49,6 +49,7 @@ # include <QQuickView> # include "fakeapplicationmanagerwindow.h" +# include "inprocesssurfaceitem.h" #endif #include "logging.h" @@ -96,6 +97,9 @@ static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory) } +const char *QmlInProcessRuntime::s_runtimeKey = "_am_runtime"; + + QmlInProcessRuntime::QmlInProcessRuntime(const Application *app, QmlInProcessRuntimeManager *manager) : AbstractRuntime(nullptr, app, manager) { } @@ -105,21 +109,17 @@ QmlInProcessRuntime::~QmlInProcessRuntime() #if !defined(AM_HEADLESS) // if there is still a window present at this point, fire the 'closing' signal (probably) again, // because it's still the duty of WindowManager together with qml-ui to free and delete this item!! - for (int i = m_windows.size(); i; --i) - emit inProcessSurfaceItemClosing(m_windows.at(i-1)); + for (int i = m_surfaces.size(); i; --i) + emit inProcessSurfaceItemClosing(m_surfaces.at(i-1)); #endif } bool QmlInProcessRuntime::start() { - setState(Startup); #if !defined(AM_HEADLESS) - QQuickItem *win = qobject_cast<QQuickItem*>(m_rootObject); - if (win) { // if there is already a window present, just emit ready signal and return true (=="start successful") - emit inProcessSurfaceItemReady(win); - return true; - } + Q_ASSERT(!m_rootObject); #endif + setState(Startup); if (!m_inProcessQmlEngine) return false; @@ -144,6 +144,7 @@ bool QmlInProcessRuntime::start() qCDebug(LogSystem) << "Updated Qml import paths:" << m_inProcessQmlEngine->importPathList(); } + m_componentError = false; QQmlComponent *component = new QQmlComponent(m_inProcessQmlEngine, m_app->absoluteCodeFilePath()); if (!component->isReady()) { @@ -157,41 +158,35 @@ bool QmlInProcessRuntime::start() m_applicationIf = new QmlInProcessApplicationInterface(this); appContext->setContextProperty(qSL("ApplicationInterface"), m_applicationIf); connect(m_applicationIf, &QmlInProcessApplicationInterface::quitAcknowledged, - this, [=]() { finish(0, QProcess::NormalExit); }); + this, [this]() { finish(0, QProcess::NormalExit); }); - QObject *obj = component->beginCreate(appContext); + if (appContext->setProperty(s_runtimeKey, QVariant::fromValue(this))) + qCritical() << "Could not set" << s_runtimeKey << "property in QML context"; - if (!obj) { - qCCritical(LogSystem) << "could not load" << m_app->absoluteCodeFilePath() << ": no root object"; - delete obj; - delete appContext; - delete m_applicationIf; - m_applicationIf = nullptr; - return false; - } + QObject *obj = component->beginCreate(appContext); + QTimer::singleShot(0, this, [component, appContext, obj, this]() { + component->completeCreate(); + if (!obj || m_componentError) { + qCCritical(LogSystem) << "could not load" << m_app->absoluteCodeFilePath() << ": no root object"; + delete obj; + delete appContext; + delete m_applicationIf; + m_applicationIf = nullptr; + finish(3, QProcess::NormalExit); + } else { #if !defined(AM_HEADLESS) - - FakeApplicationManagerWindow *window = qobject_cast<FakeApplicationManagerWindow*>(obj); - if (window) { - window->m_runtime = this; - } else { - QQuickItem *item = qobject_cast<QQuickItem*>(obj); - if (item) - addWindow(item); - } - Q_ASSERT(obj->metaObject()->indexOfProperty("AM-RUNTIME") == -1); - if (obj->setProperty("AM-RUNTIME", QVariant::fromValue(this))) - qCritical() << "ApplicationManagerWindow must not have an AM-RUNTIME property"; - m_rootObject = obj; - + if (!qobject_cast<FakeApplicationManagerWindow*>(obj)) { + QQuickItem *item = qobject_cast<QQuickItem*>(obj); + if (item) + addWindow(item); + } + m_rootObject = obj; #endif - - QTimer::singleShot(0, this, [component, this]() { - component->completeCreate(); - if (!m_document.isEmpty()) - openDocument(m_document, QString()); - setState(Active); + if (!m_document.isEmpty()) + openDocument(m_document, QString()); + setState(Active); + } delete component; }); return true; @@ -203,10 +198,13 @@ void QmlInProcessRuntime::stop(bool forceKill) emit aboutToStop(); #if !defined(AM_HEADLESS) - for (int i = m_windows.size(); i; --i) - emit inProcessSurfaceItemClosing(m_windows.at(i-1)); - m_windows.clear(); - m_rootObject = nullptr; + for (int i = m_surfaces.size(); i; --i) + emit inProcessSurfaceItemClosing(m_surfaces.at(i-1)); + + if (m_surfaces.isEmpty()) { + delete m_rootObject; + m_rootObject = nullptr; + } #endif if (forceKill) { @@ -236,42 +234,62 @@ void QmlInProcessRuntime::stop(bool forceKill) void QmlInProcessRuntime::finish(int exitCode, QProcess::ExitStatus status) { QTimer::singleShot(0, this, [this, exitCode, status]() { + qCDebug(LogSystem) << "QmlInProcessRuntime (id:" << (m_app ? m_app->id() : qSL("(none)")) + << ") exited with code:" << exitCode << "status:" << status; emit finished(exitCode, status); setState(Inactive); +#if !defined(AM_HEADLESS) + if (m_surfaces.isEmpty()) + deleteLater(); +#else deleteLater(); +#endif }); } #if !defined(AM_HEADLESS) +void QmlInProcessRuntime::inProcessSurfaceItemReleased(QQuickItem *surface) +{ + // TODO: Take a snapshot of the last window frame and use this for potential systemUI animations. + // Stop the application (delete its object hierarchy) immediately and remove this workaround. + m_surfaces.removeOne(surface); + if (state() != Active && m_surfaces.isEmpty()) { + delete m_rootObject; + m_rootObject = nullptr; + if (state() == Inactive) + deleteLater(); + } +} + void QmlInProcessRuntime::onWindowClose() { - QQuickItem* window = reinterpret_cast<QQuickItem*>(sender()); // reinterpret_cast because the object might be broken down already! - Q_ASSERT(window && m_windows.contains(window)); + QQuickItem* surface = reinterpret_cast<QQuickItem*>(sender()); // reinterpret_cast because the object might be broken down already! + Q_ASSERT(surface && m_surfaces.contains(surface)); - emit inProcessSurfaceItemClosing(window); + emit inProcessSurfaceItemClosing(surface); } void QmlInProcessRuntime::onWindowDestroyed() { QObject* sndr = sender(); - m_windows.removeAll(reinterpret_cast<QQuickItem*>(sndr)); // reinterpret_cast because the object might be broken down already! + m_surfaces.removeAll(reinterpret_cast<QQuickItem*>(sndr)); // reinterpret_cast because the object might be broken down already! if (m_rootObject == sndr) m_rootObject = nullptr; } void QmlInProcessRuntime::onEnableFullscreen() { - FakeApplicationManagerWindow *window = qobject_cast<FakeApplicationManagerWindow *>(sender()); + FakeApplicationManagerWindow *surface = qobject_cast<FakeApplicationManagerWindow *>(sender()); - emit inProcessSurfaceItemFullscreenChanging(window, true); + emit inProcessSurfaceItemFullscreenChanging(surface, true); } void QmlInProcessRuntime::onDisableFullscreen() { - FakeApplicationManagerWindow *window = qobject_cast<FakeApplicationManagerWindow *>(sender()); + FakeApplicationManagerWindow *surface = qobject_cast<FakeApplicationManagerWindow *>(sender()); - emit inProcessSurfaceItemFullscreenChanging(window, false); + emit inProcessSurfaceItemFullscreenChanging(surface, false); } void QmlInProcessRuntime::addWindow(QQuickItem *window) @@ -279,21 +297,32 @@ void QmlInProcessRuntime::addWindow(QQuickItem *window) // Below check is only needed if the root element is a QtObject. // It should be possible to remove this, once proper visible handling is in place. if (state() != Inactive && state() != Shutdown) { - if (m_windows.indexOf(window) == -1) { - m_windows.append(window); - - if (auto pcw = qobject_cast<FakeApplicationManagerWindow *>(window)) { - connect(pcw, &FakeApplicationManagerWindow::fakeFullScreenSignal, this, &QmlInProcessRuntime::onEnableFullscreen); - connect(pcw, &FakeApplicationManagerWindow::fakeNoFullScreenSignal, this, &QmlInProcessRuntime::onDisableFullscreen); - connect(pcw, &FakeApplicationManagerWindow::fakeCloseSignal, this, &QmlInProcessRuntime::onWindowClose); - connect(pcw, &QObject::destroyed, this, &QmlInProcessRuntime::onWindowDestroyed); + auto famw = qobject_cast<FakeApplicationManagerWindow *>(window); + QQuickItem *surface = famw ? famw->m_surfaceItem : window; + + if (!m_surfaces.contains(surface)) { + if (famw) { + surface = new InProcessSurfaceItem(famw); + connect(famw, &FakeApplicationManagerWindow::fakeFullScreenSignal, this, &QmlInProcessRuntime::onEnableFullscreen); + connect(famw, &FakeApplicationManagerWindow::fakeNoFullScreenSignal, this, &QmlInProcessRuntime::onDisableFullscreen); + connect(famw, &FakeApplicationManagerWindow::fakeCloseSignal, this, &QmlInProcessRuntime::onWindowClose); + connect(famw, &QObject::destroyed, this, &QmlInProcessRuntime::onWindowDestroyed); } + m_surfaces.append(surface); } - emit inProcessSurfaceItemReady(window); + emit inProcessSurfaceItemReady(surface); } } +void QmlInProcessRuntime::removeWindow(QQuickItem *window) +{ + auto famw = qobject_cast<FakeApplicationManagerWindow *>(window); + QQuickItem *surface = famw ? famw->m_surfaceItem : window; + if (m_surfaces.removeOne(surface)) + emit inProcessSurfaceItemClosing(surface); +} + #endif // !AM_HEADLESS void QmlInProcessRuntime::openDocument(const QString &document, const QString &mimeType) diff --git a/src/manager-lib/qmlinprocessruntime.h b/src/manager-lib/qmlinprocessruntime.h index 91ac932b..6e8208c2 100644 --- a/src/manager-lib/qmlinprocessruntime.h +++ b/src/manager-lib/qmlinprocessruntime.h @@ -79,6 +79,9 @@ public: public slots: bool start() override; void stop(bool forceKill = false) override; +#if !defined(AM_HEADLESS) + void inProcessSurfaceItemReleased(QQuickItem *window) override; +#endif signals: void aboutToStop(); // used for the ApplicationInterface @@ -94,15 +97,19 @@ private slots: void finish(int exitCode, QProcess::ExitStatus status); private: + static const char *s_runtimeKey; + QString m_document; QmlInProcessApplicationInterface *m_applicationIf = nullptr; + bool m_componentError; #if !defined(AM_HEADLESS) // used by FakeApplicationManagerWindow to register windows void addWindow(QQuickItem *window); + void removeWindow(QQuickItem *window); QObject *m_rootObject = nullptr; - QList<QQuickItem *> m_windows; + QList<QQuickItem *> m_surfaces; friend class FakeApplicationManagerWindow; // for emitting signals on behalf of this class in onComplete #endif diff --git a/src/window-lib/inprocesswindow.cpp b/src/window-lib/inprocesswindow.cpp index 94bb2839..4a508bab 100644 --- a/src/window-lib/inprocesswindow.cpp +++ b/src/window-lib/inprocesswindow.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "inprocesswindow.h" +#include "inprocesssurfaceitem.h" QT_BEGIN_NAMESPACE_AM @@ -62,38 +63,44 @@ static bool isName(const QByteArray &key) InProcessWindow::InProcessWindow(const Application *app, QQuickItem *surfaceItem) : Window(app, surfaceItem) { - surfaceItem->installEventFilter(this); + auto ipsi = qobject_cast<InProcessSurfaceItem *>(surfaceItem); + if (ipsi) + m_windowProperties = ipsi->windowProperties(); + else + m_windowProperties.reset(new QObject()); + + m_windowProperties->installEventFilter(this); } bool InProcessWindow::setWindowProperty(const QString &name, const QVariant &value) { QByteArray key = nameToKey(name); - QVariant oldValue = windowItem()->property(key); + QVariant oldValue = m_windowProperties->property(key); bool changed = !oldValue.isValid() || (oldValue != value); - if (changed) { - windowItem()->setProperty(key, value); - } + if (changed) + m_windowProperties->setProperty(key, value); + return true; } QVariant InProcessWindow::windowProperty(const QString &name) const { QByteArray key = nameToKey(name); - return windowItem()->property(key); + return m_windowProperties->property(key); } QVariantMap InProcessWindow::windowProperties() const { - const QList<QByteArray> keys = windowItem()->dynamicPropertyNames(); + const QList<QByteArray> keys = m_windowProperties->dynamicPropertyNames(); QVariantMap map; for (const QByteArray &key : keys) { if (!isName(key)) continue; - QString name = QString::fromUtf8(key.mid(4)); - map[name] = windowItem()->property(key); + QString name = keyToName(key); + map[name] = m_windowProperties->property(key); } return map; @@ -101,15 +108,13 @@ QVariantMap InProcessWindow::windowProperties() const bool InProcessWindow::eventFilter(QObject *o, QEvent *e) { - if ((o == windowItem()) && (e->type() == QEvent::DynamicPropertyChange)) { + if ((o == m_windowProperties) && (e->type() == QEvent::DynamicPropertyChange)) { QDynamicPropertyChangeEvent *dpce = static_cast<QDynamicPropertyChangeEvent *>(e); QByteArray key = dpce->propertyName(); if (isName(key)) { QString name = keyToName(dpce->propertyName()); - emit windowPropertyChanged(name, windowItem()->property(key)); - - //qWarning() << "IPW: got change" << name << " --> " << surfaceItem()->property(key).toString(); + emit windowPropertyChanged(name, m_windowProperties->property(key)); } } diff --git a/src/window-lib/inprocesswindow.h b/src/window-lib/inprocesswindow.h index dba21d60..c5e0f92c 100644 --- a/src/window-lib/inprocesswindow.h +++ b/src/window-lib/inprocesswindow.h @@ -57,8 +57,6 @@ public: bool isInProcess() const override { return true; } - //bool isClosing() const override; - bool setWindowProperty(const QString &name, const QVariant &value) override; QVariant windowProperty(const QString &name) const override; QVariantMap windowProperties() const override; @@ -67,7 +65,7 @@ protected: bool eventFilter(QObject *o, QEvent *e) override; private: - QVariantMap m_windowProperties; + QSharedPointer<QObject> m_windowProperties; }; QT_END_NAMESPACE_AM diff --git a/src/window-lib/windowmanager.cpp b/src/window-lib/windowmanager.cpp index 6c31cc31..989bf8b9 100644 --- a/src/window-lib/windowmanager.cpp +++ b/src/window-lib/windowmanager.cpp @@ -593,6 +593,8 @@ void WindowManager::setupInProcessRuntime(AbstractRuntime *runtime) this, static_cast<void (WindowManager::*)(QQuickItem *)>(&WindowManager::inProcessSurfaceItemCreated), Qt::QueuedConnection); connect(runtime, &AbstractRuntime::inProcessSurfaceItemClosing, this, static_cast<void (WindowManager::*)(QQuickItem *)>(&WindowManager::inProcessSurfaceItemClosing), Qt::QueuedConnection); + connect(this, &WindowManager::windowReleased, runtime, + &AbstractRuntime::inProcessSurfaceItemReleased, Qt::QueuedConnection); } } @@ -619,10 +621,13 @@ void WindowManager::releaseWindow(QQuickItem *window) qCWarning(LogGraphics) << "releaseWindow was called with an invalid window pointer" << window; return; } + Window *win = d->windows.at(index); if (!win) return; + emit windowReleased(window); + beginRemoveRows(QModelIndex(), index, index); d->windows.removeAt(index); endRemoveRows(); diff --git a/src/window-lib/windowmanager.h b/src/window-lib/windowmanager.h index 7ba30704..a9c5f8fa 100644 --- a/src/window-lib/windowmanager.h +++ b/src/window-lib/windowmanager.h @@ -113,6 +113,8 @@ signals: void windowClosing(int index, QQuickItem *window); void windowLost(int index, QQuickItem *window); + void windowReleased(QQuickItem *window); + void windowPropertyChanged(QQuickItem *window, const QString &name, const QVariant &value); void compositorViewRegistered(QQuickWindow *view); diff --git a/tests/qml/fakeamwindow/am-config.yaml b/tests/qml/fakeamwindow/am-config.yaml new file mode 100644 index 00000000..7ea683dd --- /dev/null +++ b/tests/qml/fakeamwindow/am-config.yaml @@ -0,0 +1,15 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + installedAppsManifestDir: "/tmp/am-famw-test/manifests" + appImageMountDir: "/tmp/am-famw-test/image-mounts" + database: "/tmp/am-famw-test/apps.db" + +installationLocations: +- id: "internal-0" + installationPath: "/tmp/am-famw-test/apps" + documentPath: "/tmp/am-famw-test/docs" + mountPoint: "/tmp" + isDefault: true diff --git a/tests/qml/fakeamwindow/apps/test.famw.parent1/icon.png b/tests/qml/fakeamwindow/apps/test.famw.parent1/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/qml/fakeamwindow/apps/test.famw.parent1/icon.png diff --git a/tests/qml/fakeamwindow/apps/test.famw.parent1/info.yaml b/tests/qml/fakeamwindow/apps/test.famw.parent1/info.yaml new file mode 100644 index 00000000..6c77b8d4 --- /dev/null +++ b/tests/qml/fakeamwindow/apps/test.famw.parent1/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.famw.parent1' +icon: 'icon.png' +code: 'parent1.qml' +runtime: 'qml' +name: + en: 'FakeApplicationManagerWindow onParentChanged' diff --git a/tests/qml/fakeamwindow/apps/test.famw.parent1/parent1.qml b/tests/qml/fakeamwindow/apps/test.famw.parent1/parent1.qml new file mode 100644 index 00000000..87dccf3b --- /dev/null +++ b/tests/qml/fakeamwindow/apps/test.famw.parent1/parent1.qml @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + onParentChanged: console.log("Impossible"); +} diff --git a/tests/qml/fakeamwindow/apps/test.famw.parent2/icon.png b/tests/qml/fakeamwindow/apps/test.famw.parent2/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/qml/fakeamwindow/apps/test.famw.parent2/icon.png diff --git a/tests/qml/fakeamwindow/apps/test.famw.parent2/info.yaml b/tests/qml/fakeamwindow/apps/test.famw.parent2/info.yaml new file mode 100644 index 00000000..f5ebf781 --- /dev/null +++ b/tests/qml/fakeamwindow/apps/test.famw.parent2/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.famw.parent2' +icon: 'icon.png' +code: 'parent2.qml' +runtime: 'qml' +name: + en: 'FakeApplicationManagerWindow parent property' diff --git a/tests/qml/fakeamwindow/apps/test.famw.parent2/parent2.qml b/tests/qml/fakeamwindow/apps/test.famw.parent2/parent2.qml new file mode 100644 index 00000000..dbd96c6d --- /dev/null +++ b/tests/qml/fakeamwindow/apps/test.famw.parent2/parent2.qml @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + id: root + + Item { + id: container + } + + Connections { + target: ApplicationInterface + onOpenDocument: { + if (documentUrl === 'set') + root.parent = container; + } + } + + Component.onCompleted: console.warn("The parent is: " + root.parent); +} diff --git a/tests/qml/fakeamwindow/apps/test.famw.parent3/Test.qml b/tests/qml/fakeamwindow/apps/test.famw.parent3/Test.qml new file mode 100644 index 00000000..87dccf3b --- /dev/null +++ b/tests/qml/fakeamwindow/apps/test.famw.parent3/Test.qml @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + onParentChanged: console.log("Impossible"); +} diff --git a/tests/qml/fakeamwindow/apps/test.famw.parent3/icon.png b/tests/qml/fakeamwindow/apps/test.famw.parent3/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/qml/fakeamwindow/apps/test.famw.parent3/icon.png diff --git a/tests/qml/fakeamwindow/apps/test.famw.parent3/info.yaml b/tests/qml/fakeamwindow/apps/test.famw.parent3/info.yaml new file mode 100644 index 00000000..ec5292d7 --- /dev/null +++ b/tests/qml/fakeamwindow/apps/test.famw.parent3/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.famw.parent3' +icon: 'icon.png' +code: 'parent3.qml' +runtime: 'qml' +name: + en: 'Dynamically loaded FakeApplicationManagerWindow' diff --git a/tests/qml/fakeamwindow/apps/test.famw.parent3/parent3.qml b/tests/qml/fakeamwindow/apps/test.famw.parent3/parent3.qml new file mode 100644 index 00000000..b20f1bfa --- /dev/null +++ b/tests/qml/fakeamwindow/apps/test.famw.parent3/parent3.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + id: root + + Loader { + id: ldr + } + + Connections { + target: ApplicationInterface + onOpenDocument: { + if (documentUrl === "load") + ldr.source = "Test.qml"; + } + } +} diff --git a/tests/qml/fakeamwindow/fakeamwindow.pro b/tests/qml/fakeamwindow/fakeamwindow.pro new file mode 100644 index 00000000..9b7501a2 --- /dev/null +++ b/tests/qml/fakeamwindow/fakeamwindow.pro @@ -0,0 +1,4 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_fakeamwindow.qml + +load(am-qml-testcase) diff --git a/tests/qml/fakeamwindow/tst_fakeamwindow.qml b/tests/qml/fakeamwindow/tst_fakeamwindow.qml new file mode 100644 index 00000000..a127852b --- /dev/null +++ b/tests/qml/fakeamwindow/tst_fakeamwindow.qml @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtQuick 2.3 +import QtTest 1.0 +import QtApplicationManager 1.0 + +Item { + TestCase { + name: "Dummy" // workaround to make actual test below optional + when: windowShown + + function test_dummy() {} + } + + TestCase { + name: "FakeApplicationManagerWindow" + when: ApplicationManager.singleProcess + optional: true + + function test_parent1() { + ignoreWarning('QML ApplicationManagerWindow: Cannot assign to non-existent property "onParentChanged"'); + ApplicationManager.startApplication("test.famw.parent1"); + } + + function test_parent2() { + ignoreWarning('The parent is: undefined'); + ApplicationManager.startApplication("test.famw.parent2"); + wait(0); + ignoreWarning('TypeError: Cannot assign to read-only property "parent"'); + ApplicationManager.startApplication("test.famw.parent2", "set"); + } + + function test_parent3() { + ApplicationManager.startApplication("test.famw.parent3"); + wait(0); + ignoreWarning('QML ApplicationManagerWindow: Cannot assign to non-existent property "onParentChanged"'); + ApplicationManager.startApplication("test.famw.parent3", "load"); + } + } +} diff --git a/tests/qml/qml.pro b/tests/qml/qml.pro index 968ead34..906bde2c 100644 --- a/tests/qml/qml.pro +++ b/tests/qml/qml.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs SUBDIRS = \ simple \ - windowmapping + windowmapping \ + fakeamwindow diff --git a/tests/qml/windowmapping/apps/test.winmap.loader/SubWin.qml b/tests/qml/windowmapping/apps/test.winmap.loader/SubWin.qml new file mode 100644 index 00000000..2d65b8ea --- /dev/null +++ b/tests/qml/windowmapping/apps/test.winmap.loader/SubWin.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + Component.onCompleted: setWindowProperty("type", "sub"); +} diff --git a/tests/qml/windowmapping/apps/test.winmap.loader/icon.png b/tests/qml/windowmapping/apps/test.winmap.loader/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/qml/windowmapping/apps/test.winmap.loader/icon.png diff --git a/tests/qml/windowmapping/apps/test.winmap.loader/info.yaml b/tests/qml/windowmapping/apps/test.winmap.loader/info.yaml new file mode 100644 index 00000000..6e486078 --- /dev/null +++ b/tests/qml/windowmapping/apps/test.winmap.loader/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.winmap.loader' +icon: 'icon.png' +code: 'loader.qml' +runtime: 'qml' +name: + en: 'Dynamic loading' diff --git a/tests/qml/windowmapping/apps/test.winmap.loader/loader.qml b/tests/qml/windowmapping/apps/test.winmap.loader/loader.qml new file mode 100644 index 00000000..5d09e43f --- /dev/null +++ b/tests/qml/windowmapping/apps/test.winmap.loader/loader.qml @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + id: root + visible: true + + Loader { + id: ldr + active: false + source: "SubWin.qml" + } + + Connections { + target: ApplicationInterface + onOpenDocument: { + switch (documentUrl) { + case "show-sub": ldr.active = true; break; + case "hide-sub": ldr.active = false; break; + } + } + } +} diff --git a/tests/qml/windowmapping/tst_windowmapping.qml b/tests/qml/windowmapping/tst_windowmapping.qml index 606a2f6b..0ea7a81c 100644 --- a/tests/qml/windowmapping/tst_windowmapping.qml +++ b/tests/qml/windowmapping/tst_windowmapping.qml @@ -92,85 +92,40 @@ TestCase { } } - function test_default_data() { - return [ { tag: "ApplicationManagerWindow", appId: "test.winmap.amwin" }, - // skipping QtObject, as it doesn't show anything - { tag: "Rectangle", appId: "test.winmap.rectangle" }, - { tag: "Window", appId: "test.winmap.window" } ]; - } - - function test_default(data) { - if (ApplicationManager.singleProcess && data.tag === "Window") - skip("Window root element is not properly supported in single process mode."); + function test_amwin_advanced() { + var appId = "test.winmap.amwin2"; + ApplicationManager.startApplication(appId, "show-sub"); + wait(2000); + compare(windowReadySpy.count, 0); - var appId = data.appId; - compare(chrome.children.length, 1); - ApplicationManager.startApplication(appId); - windowReadySpy.wait(2000); - compare(windowReadySpy.count, 1); + ApplicationManager.startApplication(appId, "show-main"); + windowReadySpy.wait(3000); + compare(windowReadySpy.count, 2); windowReadySpy.clear(); - compare(chrome.children.length, 2); ApplicationManager.stopApplication(appId); - windowLostSpy.wait(2000); - compare(windowLostSpy.count, 1); + ensureAppTerminated(appId); windowLostSpy.clear(); - ensureAppTerminated(appId); } - function test_mapping_data() { - return [ { tag: "ApplicationManagerWindow", appId: "test.winmap.amwin" }, - { tag: "QtObject", appId: "test.winmap.qtobject" }, - { tag: "Rectangle", appId: "test.winmap.rectangle" }, - { tag: "Window", appId: "test.winmap.window" } ]; - } - - function test_mapping(data) { - if (ApplicationManager.singleProcess && data.tag === "Window") - skip("Window root element is not properly supported in single process mode."); - - var appId = data.appId; - compare(chrome.children.length, 1); - ApplicationManager.startApplication(appId, "show-main"); - windowReadySpy.wait(2000); - compare(windowReadySpy.count, 1); - windowReadySpy.clear(); - compare(chrome.children.length, 2); + function test_amwin_loader() { + if (!ApplicationManager.singleProcess) + skip("Sporadically crashes in QtWaylandClient::QWaylandDisplay::flushRequests()"); - compare(subChrome.children.length, 0); + var appId = "test.winmap.loader"; ApplicationManager.startApplication(appId, "show-sub"); - windowReadySpy.wait(2000); - compare(windowReadySpy.count, 1); + windowReadySpy.wait(3000); + compare(windowReadySpy.count, 2); windowReadySpy.clear(); - compare(subChrome.children.length, 1); - var openWindows = 2; - // visible handling needs to be fixed for single process mode (AUTOSUITE-131): - if (!ApplicationManager.singleProcess) { - ApplicationManager.startApplication(appId, "hide-sub"); - windowLostSpy.wait(2000); - compare(windowLostSpy.count, 1); - windowLostSpy.clear(); - openWindows = 1; - compare(subChrome.children.length, 0); - } - - ApplicationManager.stopApplication(appId); + ApplicationManager.startApplication(appId, "hide-sub"); windowLostSpy.wait(2000); - compare(windowLostSpy.count, openWindows); + compare(windowLostSpy.count, 1); windowLostSpy.clear(); - ensureAppTerminated(appId); - } - function test_amwin_advanced() { - var appId = "test.winmap.amwin2"; ApplicationManager.startApplication(appId, "show-sub"); - wait(2000); - compare(windowReadySpy.count, 0); - - ApplicationManager.startApplication(appId, "show-main"); windowReadySpy.wait(3000); - compare(windowReadySpy.count, 2); + compare(windowReadySpy.count, 1); windowReadySpy.clear(); ApplicationManager.stopApplication(appId); @@ -232,4 +187,74 @@ TestCase { ensureAppTerminated(appId); windowLostSpy.clear(); } + + function test_default_data() { + return [ { tag: "ApplicationManagerWindow", appId: "test.winmap.amwin" }, + // skipping QtObject, as it doesn't show anything + { tag: "Rectangle", appId: "test.winmap.rectangle" }, + { tag: "Window", appId: "test.winmap.window" } ]; + } + + function test_default(data) { + if (ApplicationManager.singleProcess && data.tag === "Window") + skip("Window root element is not properly supported in single process mode."); + + var appId = data.appId; + compare(chrome.children.length, 1); + ApplicationManager.startApplication(appId); + windowReadySpy.wait(2000); + compare(windowReadySpy.count, 1); + windowReadySpy.clear(); + compare(chrome.children.length, 2); + + ApplicationManager.stopApplication(appId); + windowLostSpy.wait(2000); + compare(windowLostSpy.count, 1); + windowLostSpy.clear(); + ensureAppTerminated(appId); + } + + function test_mapping_data() { + return [ { tag: "ApplicationManagerWindow", appId: "test.winmap.amwin" }, + { tag: "QtObject", appId: "test.winmap.qtobject" }, + { tag: "Rectangle", appId: "test.winmap.rectangle" }, + { tag: "Window", appId: "test.winmap.window" } ]; + } + + function test_mapping(data) { + if (ApplicationManager.singleProcess && data.tag === "Window") + skip("Window root element is not properly supported in single process mode."); + + var appId = data.appId; + compare(chrome.children.length, 1); + ApplicationManager.startApplication(appId, "show-main"); + windowReadySpy.wait(2000); + compare(windowReadySpy.count, 1); + windowReadySpy.clear(); + compare(chrome.children.length, 2); + + compare(subChrome.children.length, 0); + ApplicationManager.startApplication(appId, "show-sub"); + windowReadySpy.wait(2000); + compare(windowReadySpy.count, 1); + windowReadySpy.clear(); + compare(subChrome.children.length, 1); + + var openWindows = 2; + // visible handling needs to be fixed for single process mode (AUTOSUITE-131): + if (!ApplicationManager.singleProcess) { + ApplicationManager.startApplication(appId, "hide-sub"); + windowLostSpy.wait(2000); + compare(windowLostSpy.count, 1); + windowLostSpy.clear(); + openWindows = 1; + compare(subChrome.children.length, 0); + } + + ApplicationManager.stopApplication(appId); + windowLostSpy.wait(2000); + compare(windowLostSpy.count, openWindows); + windowLostSpy.clear(); + ensureAppTerminated(appId); + } } |