/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** 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 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPLv3 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.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 later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include "../shared/visualtestutil.h" using namespace QQuickVisualTestUtil; struct ControlInfo { QString type; QStringList delegates; }; static const ControlInfo ControlInfos[] = { { "AbstractButton", QStringList() << "background" << "contentItem" << "indicator" }, { "ApplicationWindow", QStringList() << "background" }, { "BusyIndicator", QStringList() << "background" << "contentItem" }, { "Button", QStringList() << "background" << "contentItem" }, { "CheckBox", QStringList() << "contentItem" << "indicator" }, { "CheckDelegate", QStringList() << "background" << "contentItem" << "indicator" }, { "ComboBox", QStringList() << "background" << "contentItem" << "indicator" }, // popup not created until needed { "Container", QStringList() << "background" << "contentItem" }, { "Control", QStringList() << "background" << "contentItem" }, { "DelayButton", QStringList() << "background" << "contentItem" }, { "Dial", QStringList() << "background" << "handle" }, { "Dialog", QStringList() << "background" << "contentItem" }, { "DialogButtonBox", QStringList() << "background" << "contentItem" }, { "Drawer", QStringList() << "background" << "contentItem" }, { "Frame", QStringList() << "background" << "contentItem" }, { "GroupBox", QStringList() << "background" << "contentItem" << "label" }, { "ItemDelegate", QStringList() << "background" << "contentItem" }, { "Label", QStringList() << "background" }, { "Menu", QStringList() << "background" << "contentItem" }, { "MenuBar", QStringList() << "background" << "contentItem" }, { "MenuBarItem", QStringList() << "background" << "contentItem" }, { "MenuItem", QStringList() << "arrow" << "background" << "contentItem" << "indicator" }, { "MenuSeparator", QStringList() << "background" << "contentItem" }, { "Page", QStringList() << "background" << "contentItem" }, { "PageIndicator", QStringList() << "background" << "contentItem" }, { "Pane", QStringList() << "background" << "contentItem" }, { "Popup", QStringList() << "background" << "contentItem" }, { "ProgressBar", QStringList() << "background" << "contentItem" }, { "RadioButton", QStringList() << "contentItem" << "indicator" }, { "RadioDelegate", QStringList() << "background" << "contentItem" << "indicator" }, { "RangeSlider", QStringList() << "background" << "first.handle" << "second.handle" }, { "RoundButton", QStringList() << "background" << "contentItem" }, { "ScrollBar", QStringList() << "background" << "contentItem" }, { "ScrollIndicator", QStringList() << "background" << "contentItem" }, { "ScrollView", QStringList() << "background" }, { "Slider", QStringList() << "background" << "handle" }, { "SpinBox", QStringList() << "background" << "contentItem" << "up.indicator" << "down.indicator" }, { "StackView", QStringList() << "background" << "contentItem" }, { "SwipeDelegate", QStringList() << "background" << "contentItem" }, { "SwipeView", QStringList() << "background" << "contentItem" }, { "Switch", QStringList() << "contentItem" << "indicator" }, { "SwitchDelegate", QStringList() << "background" << "contentItem" << "indicator" }, { "TabBar", QStringList() << "background" << "contentItem" }, { "TabButton", QStringList() << "background" << "contentItem" }, { "TextField", QStringList() << "background" }, { "TextArea", QStringList() << "background" }, { "ToolBar", QStringList() << "background" << "contentItem" }, { "ToolButton", QStringList() << "background" << "contentItem" }, { "ToolSeparator", QStringList() << "background" << "contentItem" }, { "ToolTip", QStringList() << "background" << "contentItem" }, { "Tumbler", QStringList() << "background" << "contentItem" } }; class tst_customization : public QQmlDataTest { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); void creation_data(); void creation(); void override_data(); void override(); void comboPopup(); private: void reset(); void addHooks(); void removeHooks(); QObject* createControl(const QString &type, const QString &qml, QString *error); QQmlEngine *engine = nullptr; }; typedef QHash QObjectNameHash; Q_GLOBAL_STATIC(QObjectNameHash, qt_objectNames) Q_GLOBAL_STATIC(QStringList, qt_createdQObjects) Q_GLOBAL_STATIC(QStringList, qt_destroyedQObjects) Q_GLOBAL_STATIC(QStringList, qt_destroyedParentQObjects) static int qt_unparentedItemCount = 0; class ItemParentListener : public QQuickItem { Q_OBJECT public: ItemParentListener() { m_slotIndex = metaObject()->indexOfSlot("onParentChanged()"); m_signalIndex = QMetaObjectPrivate::signalIndex(QMetaMethod::fromSignal(&QQuickItem::parentChanged)); } int signalIndex() const { return m_signalIndex; } int slotIndex() const { return m_slotIndex; } public slots: void onParentChanged() { const QQuickItem *item = qobject_cast(sender()); if (!item) return; if (!item->parentItem()) ++qt_unparentedItemCount; } private: int m_slotIndex; int m_signalIndex; }; static ItemParentListener *qt_itemParentListener = nullptr; extern "C" Q_DECL_EXPORT void qt_addQObject(QObject *object) { // objectName is not set at construction time QObject::connect(object, &QObject::objectNameChanged, [object](const QString &objectName) { QString oldObjectName = qt_objectNames()->value(object); if (!oldObjectName.isEmpty()) qt_createdQObjects()->removeOne(oldObjectName); // Only track object names from our QML files, // not e.g. contentItem object names (like "ApplicationWindow"). if (objectName.contains("-")) { qt_createdQObjects()->append(objectName); qt_objectNames()->insert(object, objectName); } }); if (qt_itemParentListener) { static const int signalIndex = qt_itemParentListener->signalIndex(); static const int slotIndex = qt_itemParentListener->slotIndex(); QMetaObject::connect(object, signalIndex, qt_itemParentListener, slotIndex); } } extern "C" Q_DECL_EXPORT void qt_removeQObject(QObject *object) { QString objectName = object->objectName(); if (!objectName.isEmpty()) qt_destroyedQObjects()->append(objectName); qt_objectNames()->remove(object); QObject *parent = object->parent(); if (parent) { QString parentName = parent->objectName(); if (!parentName.isEmpty()) qt_destroyedParentQObjects()->append(parentName); } } void tst_customization::initTestCase() { QQmlDataTest::initTestCase(); qt_itemParentListener = new ItemParentListener; } void tst_customization::cleanupTestCase() { delete qt_itemParentListener; qt_itemParentListener = nullptr; } void tst_customization::init() { engine = new QQmlEngine(this); qtHookData[QHooks::AddQObject] = reinterpret_cast(&qt_addQObject); qtHookData[QHooks::RemoveQObject] = reinterpret_cast(&qt_removeQObject); } void tst_customization::cleanup() { qtHookData[QHooks::AddQObject] = 0; qtHookData[QHooks::RemoveQObject] = 0; delete engine; engine = nullptr; qmlClearTypeRegistrations(); reset(); } void tst_customization::reset() { qt_unparentedItemCount = 0; qt_createdQObjects()->clear(); qt_destroyedQObjects()->clear(); qt_destroyedParentQObjects()->clear(); } QObject* tst_customization::createControl(const QString &name, const QString &qml, QString *error) { QQmlComponent component(engine); component.setData("import QtQuick 2.10; import QtQuick.Window 2.2; import QtQuick.Controls 2.3; " + name.toUtf8() + " { " + qml.toUtf8() + " }", QUrl()); QObject *obj = component.create(); if (!obj) *error = component.errorString(); return obj; } void tst_customization::creation_data() { QTest::addColumn("style"); QTest::addColumn("type"); QTest::addColumn("delegates"); // the "empty" style does not contain any delegates for (const ControlInfo &control : ControlInfos) QTest::newRow(qPrintable("empty:" + control.type)) << "empty" << control.type << QStringList(); // the "incomplete" style is missing bindings to the delegates (must be created regardless) for (const ControlInfo &control : ControlInfos) QTest::newRow(qPrintable("incomplete:" + control.type)) << "incomplete" << control.type << control.delegates; // the "identified" style has IDs in the delegates (prevents deferred execution) for (const ControlInfo &control : ControlInfos) QTest::newRow(qPrintable("identified:" + control.type)) << "identified" << control.type << control.delegates; // the "simple" style simulates a proper style and contains bindings to/in delegates for (const ControlInfo &control : ControlInfos) QTest::newRow(qPrintable("simple:" + control.type)) << "simple" << control.type << control.delegates; // the "override" style overrides all delegates in the "simple" style for (const ControlInfo &control : ControlInfos) QTest::newRow(qPrintable("override:" + control.type)) << "override" << control.type << control.delegates; } void tst_customization::creation() { QFETCH(QString, style); QFETCH(QString, type); QFETCH(QStringList, delegates); QQuickStyle::setStyle(testFile("styles/" + style)); QString error; QScopedPointer control(createControl(type, "", &error)); QVERIFY2(control, qPrintable(error)); QByteArray templateType = "QQuick" + type.toUtf8(); QVERIFY2(control->inherits(templateType), qPrintable(type + " does not inherit " + templateType + " (" + control->metaObject()->className() + ")")); // -