/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** 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 https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** 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-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../shared/util.h" #include "../shared/visualtestutil.h" using namespace QQuickVisualTestUtil; class tst_qquickdesignersupport : public QQmlDataTest { Q_OBJECT public: tst_qquickdesignersupport() {} private slots: void customData(); void customDataBindings(); void objectProperties(); void dynamicProperty(); void createComponent(); void basicStates(); void statesPropertyChanges(); void testNotifyPropertyChangeCallBack(); void testFixResourcePathsForObjectCallBack(); void testComponentOnCompleteSignal(); }; void tst_qquickdesignersupport::customData() { QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("test.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); QScopedPointer newItemScopedPointer(QQuickDesignerSupportItems::createPrimitive(QLatin1String("QtQuick/Item"), 2, 6, view->rootContext())); QObject *newItem = newItemScopedPointer.data(); QVERIFY(newItem); QVERIFY(qobject_cast(newItem)); QQuickDesignerSupportProperties::registerCustomData(newItem); QVERIFY(QQuickDesignerSupportProperties::getResetValue(newItem, "width").isValid()); int defaultWidth = QQuickDesignerSupportProperties::getResetValue(newItem, "width").toInt(); QCOMPARE(defaultWidth, 0); newItem->setProperty("width", 200); QCOMPARE(newItem->property("width").toInt(), 200); //Check if reseting property does work QQuickDesignerSupportProperties::doResetProperty(newItem, view->rootContext(), "width"); QCOMPARE(newItem->property("width").toInt(), 0); //Setting a binding on width QQuickDesignerSupportProperties::setPropertyBinding(newItem, view->engine()->contextForObject(newItem), "width", QLatin1String("Math.max(0, 200)")); QCOMPARE(newItem->property("width").toInt(), 200); QVERIFY(QQuickDesignerSupportProperties::hasBindingForProperty(newItem, view->engine()->contextForObject(newItem), "width", nullptr)); //Check if reseting property does work after setting binding QQuickDesignerSupportProperties::doResetProperty(newItem, view->rootContext(), "width"); QCOMPARE(newItem->property("width").toInt(), 0); //No custom data available for the rootItem, because not registered by QQuickDesignerSupportProperties::registerCustomData QVERIFY(!QQuickDesignerSupportProperties::getResetValue(rootItem, "width").isValid()); newItemScopedPointer.reset(); //Delete the item and check if item gets removed from the hash and an invalid QVariant is returned. QVERIFY(!QQuickDesignerSupportProperties::getResetValue(newItem, "width").isValid()); } void tst_qquickdesignersupport::customDataBindings() { QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("test.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); QQuickItem *testComponent = findItem(view->rootObject(), QLatin1String("testComponent")); QVERIFY(testComponent); QQuickDesignerSupportProperties::registerCustomData(testComponent); QQuickDesignerSupportProperties::hasValidResetBinding(testComponent, "x"); QVERIFY(QQuickDesignerSupportProperties::hasBindingForProperty(testComponent, view->engine()->contextForObject(testComponent), "x", nullptr)); QCOMPARE(testComponent->property("x").toInt(), 200); //Set property to 100 and ovveride the default binding QQmlProperty property(testComponent, "x", view->engine()->contextForObject(testComponent)); QVERIFY(property.write(100)); QCOMPARE(testComponent->property("x").toInt(), 100); QVERIFY(!QQuickDesignerSupportProperties::hasBindingForProperty(testComponent, view->engine()->contextForObject(testComponent), "x", nullptr)); //Reset the binding to the default QQuickDesignerSupportProperties::doResetProperty(testComponent, view->engine()->contextForObject(testComponent), "x"); QVERIFY(QQuickDesignerSupportProperties::hasBindingForProperty(testComponent, view->engine()->contextForObject(testComponent), "x", nullptr)); QCOMPARE(testComponent->property("x").toInt(), 200); //Set a different binding/expression QQuickDesignerSupportProperties::setPropertyBinding(testComponent, view->engine()->contextForObject(testComponent), "x", QLatin1String("Math.max(0, 300)")); QVERIFY(QQuickDesignerSupportProperties::hasBindingForProperty(testComponent, view->engine()->contextForObject(testComponent), "x", nullptr)); QCOMPARE(testComponent->property("x").toInt(), 300); //Reset the binding to the default QQuickDesignerSupportProperties::doResetProperty(testComponent, view->engine()->contextForObject(testComponent), "x"); QVERIFY(QQuickDesignerSupportProperties::hasBindingForProperty(testComponent, view->engine()->contextForObject(testComponent), "x", nullptr)); QCOMPARE(testComponent->property("x").toInt(), 200); } void tst_qquickdesignersupport::objectProperties() { QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("test.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); QQuickItem *rectangleItem = findItem(view->rootObject(), QLatin1String("rectangleItem")); QVERIFY(rectangleItem); //Read gradient property as QObject int propertyIndex = rectangleItem->metaObject()->indexOfProperty("containmentMask"); QVERIFY(propertyIndex > 0); QMetaProperty metaProperty = rectangleItem->metaObject()->property(propertyIndex); QVERIFY(metaProperty.isValid()); QVERIFY(QQuickDesignerSupportProperties::isPropertyQObject(metaProperty)); QObject *containmentItem = QQuickDesignerSupportProperties::readQObjectProperty(metaProperty, rectangleItem); QVERIFY(containmentItem); //The width property is not a QObject propertyIndex = rectangleItem->metaObject()->indexOfProperty("width"); QVERIFY(propertyIndex > 0); metaProperty = rectangleItem->metaObject()->property(propertyIndex); QVERIFY(metaProperty.isValid()); QVERIFY(!QQuickDesignerSupportProperties::isPropertyQObject(metaProperty)); } void tst_qquickdesignersupport::dynamicProperty() { QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("test.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); QQuickItem *simpleItem = findItem(view->rootObject(), QLatin1String("simpleItem")); QVERIFY(simpleItem); QQuickDesignerSupportProperties::registerNodeInstanceMetaObject(simpleItem, view->engine()); QQuickDesignerSupportProperties::getPropertyCache(simpleItem, view->engine()); QQuickDesignerSupportProperties::createNewDynamicProperty(simpleItem, view->engine(), QLatin1String("dynamicProperty")); QQmlProperty property(simpleItem, "dynamicProperty", view->engine()->contextForObject(simpleItem)); QVERIFY(property.isValid()); QVERIFY(property.write(QLatin1String("test"))); QCOMPARE(property.read().toString(), QLatin1String("test")); //Force evalutation of all bindings QQuickDesignerSupport::refreshExpressions(view->rootContext()); //Check if expression to dynamic property gets properly resolved property = QQmlProperty(simpleItem, "testProperty", view->engine()->contextForObject(simpleItem)); QVERIFY(property.isValid()); QCOMPARE(property.read().toString(), QLatin1String("test")); } void tst_qquickdesignersupport::createComponent() { QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("test.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); QObject *testComponentObject = QQuickDesignerSupportItems::createComponent(testFileUrl("TestComponent.qml"), view->rootContext()); QVERIFY(testComponentObject); QVERIFY(QQuickDesignerSupportMetaInfo::isSubclassOf(testComponentObject, "QtQuick/Item")); } void tst_qquickdesignersupport::basicStates() { QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("test.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); QQuickStateGroup *stateGroup = QQuickItemPrivate::get(rootItem)->_states(); QVERIFY(stateGroup); QCOMPARE(stateGroup->states().count(), 2 ); QQuickState *state01 = stateGroup->states().first(); QQuickState *state02 = stateGroup->states().last(); QVERIFY(state01); QVERIFY(state02); QCOMPARE(state01->property("name").toString(), QLatin1String("state01")); QCOMPARE(state02->property("name").toString(), QLatin1String("state02")); QVERIFY(!QQuickDesignerSupportStates::isStateActive(state01, view->rootContext())); QVERIFY(!QQuickDesignerSupportStates::isStateActive(state01, view->rootContext())); QQuickDesignerSupportStates::activateState(state01, view->rootContext()); QVERIFY(QQuickDesignerSupportStates::isStateActive(state01, view->rootContext())); QQuickDesignerSupportStates::activateState(state02, view->rootContext()); QVERIFY(QQuickDesignerSupportStates::isStateActive(state02, view->rootContext())); QVERIFY(!QQuickDesignerSupportStates::isStateActive(state01, view->rootContext())); QQuickDesignerSupportStates::deactivateState(state02); QVERIFY(!QQuickDesignerSupportStates::isStateActive(state01, view->rootContext())); QVERIFY(!QQuickDesignerSupportStates::isStateActive(state01, view->rootContext())); } void tst_qquickdesignersupport::statesPropertyChanges() { QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("test.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); QQuickItem *simpleItem = findItem(view->rootObject(), QLatin1String("simpleItem")); QVERIFY(simpleItem); QQuickStateGroup *stateGroup = QQuickItemPrivate::get(rootItem)->_states(); QVERIFY(stateGroup); QCOMPARE(stateGroup->states().count(), 2 ); QQuickState *state01 = stateGroup->states().first(); QQuickState *state02 = stateGroup->states().last(); QVERIFY(state01); QVERIFY(state02); QCOMPARE(state01->property("name").toString(), QLatin1String("state01")); QCOMPARE(state02->property("name").toString(), QLatin1String("state02")); //PropertyChanges are parsed lazily QQuickDesignerSupportStates::activateState(state01, view->rootContext()); QQuickDesignerSupportStates::deactivateState(state01); QQuickStatePrivate *statePrivate01 = static_cast(QQuickStatePrivate::get(state01)); QCOMPARE(state01->operationCount(), 1); QCOMPARE(statePrivate01->operations.count(), 1); QQuickStateOperation *propertyChange = statePrivate01->operations.at(0).data(); QCOMPARE(QQuickDesignerSupportPropertyChanges::stateObject(propertyChange), state01); QQuickDesignerSupportPropertyChanges::changeValue(propertyChange, "width", 300); QCOMPARE(simpleItem->property("width").toInt(), 0); QQuickDesignerSupportStates::activateState(state01, view->rootContext()); QCOMPARE(simpleItem->property("width").toInt(), 300); QQuickDesignerSupportStates::deactivateState(state01); QCOMPARE(simpleItem->property("width").toInt(), 0); //Set "base state value" in state1 using the revert list QQuickDesignerSupportStates::activateState(state01, view->rootContext()); QQuickDesignerSupportStates::changeValueInRevertList(state01, simpleItem, "width", 200); QCOMPARE(simpleItem->property("width").toInt(), 300); QQuickDesignerSupportStates::deactivateState(state01); QCOMPARE(simpleItem->property("width").toInt(), 200); //Create new PropertyChanges QQuickPropertyChanges *newPropertyChange = new QQuickPropertyChanges(); newPropertyChange->setParent(state01); QQmlListProperty changes = state01->changes(); QQuickStatePrivate::operations_append(&changes, newPropertyChange); newPropertyChange->setObject(rootItem); QQuickDesignerSupportPropertyChanges::attachToState(newPropertyChange); QCOMPARE(rootItem, QQuickDesignerSupportPropertyChanges::targetObject(newPropertyChange)); QCOMPARE(state01->operationCount(), 2); QCOMPARE(statePrivate01->operations.count(), 2); QCOMPARE(QQuickDesignerSupportPropertyChanges::stateObject(newPropertyChange), state01); //Set color for rootItem in state1 QQuickDesignerSupportPropertyChanges::changeValue(newPropertyChange, "color", QColor(Qt::red)); QQuickDesignerSupportStates::activateState(state01, view->rootContext()); QCOMPARE(rootItem->property("color").value(), QColor(Qt::red)); QQuickDesignerSupportStates::deactivateState(state01); QCOMPARE(rootItem->property("color").value(), QColor(Qt::white)); QQuickDesignerSupportPropertyChanges::removeProperty(newPropertyChange, "color"); QQuickDesignerSupportStates::activateState(state01, view->rootContext()); QCOMPARE(rootItem->property("color").value(), QColor(Qt::white)); } static QObject * s_object = nullptr; static QQuickDesignerSupport::PropertyName s_propertyName; static void notifyPropertyChangeCallBackFunction(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName) { s_object = object; s_propertyName = propertyName; } static void (*notifyPropertyChangeCallBackPointer)(QObject *, const QQuickDesignerSupport::PropertyName &) = ¬ifyPropertyChangeCallBackFunction; void tst_qquickdesignersupport::testNotifyPropertyChangeCallBack() { QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("test.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); QQuickRectangle *rectangle = static_cast(rootItem); QVERIFY(rectangle); QQuickDesignerSupportProperties::registerNodeInstanceMetaObject(rectangle, view->engine()); QQuickGradient *gradient = new QQuickGradient(rectangle); QQuickDesignerSupportMetaInfo::registerNotifyPropertyChangeCallBack(notifyPropertyChangeCallBackPointer); rectangle->setProperty("gradient", QVariant::fromValue(view->engine()->newQObject(gradient))); QVERIFY(s_object); QCOMPARE(s_object, rootItem); QCOMPARE(s_propertyName, QQuickDesignerSupport::PropertyName("gradient")); } static void fixResourcePathsForObjectCallBackFunction(QObject *object) { s_object = object; } static void (*fixResourcePathsForObjectCallBackPointer)(QObject *) = &fixResourcePathsForObjectCallBackFunction; void tst_qquickdesignersupport::testFixResourcePathsForObjectCallBack() { QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("test.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); s_object = nullptr; QQuickDesignerSupportItems::registerFixResourcePathsForObjectCallBack(fixResourcePathsForObjectCallBackPointer); QQuickItem *simpleItem = findItem(view->rootObject(), QLatin1String("simpleItem")); QVERIFY(simpleItem); QQuickDesignerSupportItems::tweakObjects(simpleItem); //Check that the fixResourcePathsForObjectCallBack was called on simpleItem QCOMPARE(simpleItem , s_object); } void doComponentCompleteRecursive(QObject *object) { if (object) { QQuickItem *item = qobject_cast(object); if (item && DesignerSupport::isComponentComplete(item)) return; DesignerSupport::emitComponentCompleteSignalForAttachedProperty(object); QList childList = object->children(); if (item) { foreach (QQuickItem *childItem, item->childItems()) { if (!childList.contains(childItem)) childList.append(childItem); } } foreach (QObject *child, childList) doComponentCompleteRecursive(child); if (item) { static_cast(item)->componentComplete(); } else { QQmlParserStatus *qmlParserStatus = dynamic_cast< QQmlParserStatus*>(object); if (qmlParserStatus) qmlParserStatus->componentComplete(); } } } void tst_qquickdesignersupport::testComponentOnCompleteSignal() { { QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("componentTest.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); QQuickItem *item = findItem(view->rootObject(), QLatin1String("topLevelComplete")); QVERIFY(item); QCOMPARE(item->property("color").value(), QColor("red")); item = findItem(view->rootObject(), QLatin1String("implemented")); QVERIFY(item); QCOMPARE(item->property("color").value(), QColor("blue")); item = findItem(view->rootObject(), QLatin1String("most inner")); QVERIFY(item); QCOMPARE(item->property("color").value(), QColor("green")); } { ComponentCompleteDisabler disableComponentComplete; QScopedPointer view(new QQuickView); view->engine()->setOutputWarningsToStandardError(false); view->setSource(testFileUrl("componentTest.qml")); QVERIFY(view->errors().isEmpty()); QQuickItem *rootItem = view->rootObject(); QVERIFY(rootItem); QQuickItem *item = findItem(view->rootObject(), QLatin1String("topLevelComplete")); QVERIFY(item); QCOMPARE(item->property("color").value(), QColor("white")); item = findItem(view->rootObject(), QLatin1String("implemented")); QVERIFY(item); QCOMPARE(item->property("color").value(), QColor("white")); item = findItem(view->rootObject(), QLatin1String("most inner")); QVERIFY(item); QCOMPARE(item->property("color").value(), QColor("white")); doComponentCompleteRecursive(rootItem); item = findItem(view->rootObject(), QLatin1String("topLevelComplete")); QVERIFY(item); QCOMPARE(item->property("color").value(), QColor("red")); item = findItem(view->rootObject(), QLatin1String("implemented")); QVERIFY(item); QCOMPARE(item->property("color").value(), QColor("blue")); item = findItem(view->rootObject(), QLatin1String("most inner")); QVERIFY(item); QCOMPARE(item->property("color").value(), QColor("green")); } } QTEST_MAIN(tst_qquickdesignersupport) #include "tst_qquickdesignersupport.moc"