diff options
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 10 | ||||
-rw-r--r-- | tests/auto/qml/qml.pro | 3 | ||||
-rw-r--r-- | tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro | 7 | ||||
-rw-r--r-- | tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp | 288 |
4 files changed, 306 insertions, 2 deletions
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 11d090a415..26bdf1a94f 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1113,8 +1113,16 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo return 0; } } - if (parent) + if (instance->isWidgetType()) { + if (parent && parent->isWidgetType()) { + QAbstractDeclarativeData::setWidgetParent(instance, parent); + } else { + // No parent! Layouts need to handle this through a default property that + // reparents accordingly. Otherwise the garbage collector will collect. + } + } else if (parent) { QQml_setParent_noEvent(instance, parent); + } ddata = QQmlData::get(instance, /*create*/true); ddata->lineNumber = obj->location.line; diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 9155f5b021..9eb500f537 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -75,7 +75,8 @@ PRIVATETESTS += \ qtHaveModule(widgets) { PUBLICTESTS += \ qjsengine \ - qjsvalue + qjsvalue \ + qwidgetsinqml } SUBDIRS += $$PUBLICTESTS diff --git a/tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro b/tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro new file mode 100644 index 0000000000..c86365d5ea --- /dev/null +++ b/tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +CONFIG += parallel_test +TARGET = tst_qwidgetsinqml +macos:CONFIG -= app_bundle +QT += qml widgets testlib gui-private +SOURCES += tst_qwidgetsinqml.cpp +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp b/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp new file mode 100644 index 0000000000..bbe717859b --- /dev/null +++ b/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QQmlEngine> +#include <QtQml> +#include <QWidget> + +class tst_QWidgetsInQml : public QObject +{ + Q_OBJECT +public: + tst_QWidgetsInQml(); + +private slots: + void instantiateWidget(); + void instantiateWidgetWithoutParentWidget(); + void widgetAsDefaultPropertyCollected(); + void widgetAsDefaultPropertyKept(); + void widgetAsDefaultPropertyKeptDuringCreation(); +}; + +static void gc(QQmlEngine &engine) +{ + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); +} + +// Like QtObject, but with default property +class QObjectContainer : public QObject +{ + Q_OBJECT + Q_CLASSINFO("DefaultProperty", "data"); + Q_PROPERTY(QQmlListProperty<QObject> data READ data DESIGNABLE false); +public: + QObjectContainer() + : widgetParent(0) + , gcOnAppend(false) + {} + + QQmlListProperty<QObject> data() { + return QQmlListProperty<QObject>(this, 0, children_append, children_count, children_at, children_clear); + } + + static void children_append(QQmlListProperty<QObject> *prop, QObject *o) + { + QObjectContainer *that = static_cast<QObjectContainer*>(prop->object); + that->dataChildren.append(o); + QObject::connect(o, SIGNAL(destroyed(QObject*)), prop->object, SLOT(childDestroyed(QObject*))); + QWidget *widget = qobject_cast<QWidget*>(o); + if (widget && that->widgetParent) + widget->setParent(that->widgetParent); + + if (that->gcOnAppend) { + QQmlEngine *engine = qmlEngine(that); + gc(*engine); + } + } + + static int children_count(QQmlListProperty<QObject> *prop) + { + return static_cast<QObjectContainer*>(prop->object)->dataChildren.count(); + } + + static QObject *children_at(QQmlListProperty<QObject> *prop, int index) + { + return static_cast<QObjectContainer*>(prop->object)->dataChildren.at(index); + } + + static void children_clear(QQmlListProperty<QObject> *prop) + { + QObjectContainer *that = static_cast<QObjectContainer*>(prop->object); + foreach (QObject *c, that->dataChildren) + QObject::disconnect(c, SIGNAL(destroyed(QObject*)), that, SLOT(childDestroyed(QObject*))); + that->dataChildren.clear(); + } + + QList<QObject*> dataChildren; + QWidget *widgetParent; + bool gcOnAppend; + +protected slots: + void childDestroyed(QObject *child) { + dataChildren.removeAll(child); + } +}; + +class QWidgetContainer : public QObjectContainer +{ + Q_OBJECT +public: + QWidgetContainer() + { + widgetParent = new QWidget; + QQmlEngine::setObjectOwnership(widgetParent, QQmlEngine::CppOwnership); + } + virtual ~QWidgetContainer() + { + delete widgetParent; + widgetParent = 0; + } +}; + +class QObjectContainerWithGCOnAppend : public QObjectContainer +{ + Q_OBJECT +public: + QObjectContainerWithGCOnAppend() + { + gcOnAppend = true; + } +}; + +tst_QWidgetsInQml::tst_QWidgetsInQml() +{ + qmlRegisterType<QWidget>("Qt.Widgets", 1, 0, "QWidget"); + qmlRegisterType<QObjectContainer>("Qt.Widgets", 1, 0, "QObjectContainer"); + qmlRegisterType<QWidgetContainer>("Qt.Widgets", 1, 0, "QWidgetContainer"); + qmlRegisterType<QObjectContainerWithGCOnAppend>("Qt.Widgets", 1, 0, "QObjectContainerWithGCOnAppend"); +} + +void tst_QWidgetsInQml::instantiateWidget() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\nQWidget { property QWidget child: QWidget { objectName: 'child' } }", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QWidget *rootWidget = qobject_cast<QWidget*>(object.data()); + QVERIFY(rootWidget != 0); + QCOMPARE(rootWidget->children().count(), 1); + QWidget *firstChildWidget = qobject_cast<QWidget*>(rootWidget->children().first()); + QVERIFY(firstChildWidget != 0); + + QWidget *widgetProperty = qvariant_cast<QWidget*>(object->property("child")); + QVERIFY(widgetProperty != 0); + QCOMPARE(firstChildWidget, widgetProperty); + QCOMPARE(firstChildWidget->objectName(), QString("child")); +} + +void tst_QWidgetsInQml::instantiateWidgetWithoutParentWidget() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QtObject { property QtObject child: QWidget { objectName: 'child' } }", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QPointer<QWidget> widgetProperty = qvariant_cast<QWidget*>(object->property("child")); + QVERIFY(!widgetProperty.isNull()); + QCOMPARE(widgetProperty->objectName(), QString("child")); + + QVERIFY(!widgetProperty->parent()); + gc(engine); + // Don't collect, the property reference should keep it alive + QVERIFY(!widgetProperty.isNull()); +} + +void tst_QWidgetsInQml::widgetAsDefaultPropertyCollected() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QObjectContainer {\n" + " QWidget {\n" + " id: parentLessChild;\n" + " objectName: 'child'\n" + " }\n" + " property var widgetHolder;\n" + " Component.onCompleted: {\n" + " widgetHolder = parentLessChild;\n" + " }\n" + "}", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QObjectContainer *container = qobject_cast<QObjectContainer*>(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + QJSValue holder = qvariant_cast<QJSValue>(object->property("widgetHolder")); + QVERIFY(!holder.isNull()); + gc(engine); + QCOMPARE(container->dataChildren.count(), 1); + + holder = QJSValue(); + object->setProperty("widgetHolder", QVariant::fromValue(holder)); + + gc(engine); + // The QWidget is without a parent and nobody is referencing it anymore (the children + // list in QObjectContainer is weak!), so it should get collected. + QCOMPARE(container->dataChildren.count(), 0); +} + +void tst_QWidgetsInQml::widgetAsDefaultPropertyKept() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QWidgetContainer {\n" + " QWidget {\n" + " id: parentLessChild;\n" + " objectName: 'child'\n" + " }\n" + " property var widgetHolder;\n" + " Component.onCompleted: {\n" + " widgetHolder = parentLessChild;\n" + " }\n" + "}", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QWidgetContainer *container = qobject_cast<QWidgetContainer*>(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + QJSValue holder = qvariant_cast<QJSValue>(object->property("widgetHolder")); + QVERIFY(!holder.isNull()); + gc(engine); + QCOMPARE(container->dataChildren.count(), 1); + + holder = QJSValue(); + object->setProperty("widgetHolder", QVariant::fromValue(holder)); + + gc(engine); + QCOMPARE(container->dataChildren.count(), 1); +} + +void tst_QWidgetsInQml::widgetAsDefaultPropertyKeptDuringCreation() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QObjectContainerWithGCOnAppend {\n" + " QWidget {\n" + " id: parentLessChild;\n" + " objectName: 'child'\n" + " property var blah;\n" // Ensures that we have a JS wrapper + " }\n" + "}", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QObjectContainer *container = qobject_cast<QObjectContainer*>(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + gc(engine); + QCOMPARE(container->dataChildren.count(), 0); + +} + +QTEST_MAIN(tst_QWidgetsInQml) + +#include "tst_qwidgetsinqml.moc" |