From 1e44d36ef7f43b6740c23530ae511a98f2a59105 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 18 May 2016 14:24:10 +0200 Subject: Fix links in changelog Use https and qt.io links. Change-Id: I089098317e38b52484a23bdfad128ef47eec8a21 Reviewed-by: Robin Burchell --- dist/changes-5.6.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/changes-5.6.1 b/dist/changes-5.6.1 index 5a3c9b62e6..9506987cd3 100644 --- a/dist/changes-5.6.1 +++ b/dist/changes-5.6.1 @@ -4,7 +4,7 @@ compatibility (source and binary) with Qt 5.6.0. For more details, refer to the online documentation included in this distribution. The documentation is also available online: - http://qt-project.org/doc/qt-5.6 + https://doc.qt.io/qt-5.6 The Qt version 5.6 series is binary compatible with the 5.5.x series. Applications compiled for 5.5 will continue to run with 5.6. @@ -12,7 +12,7 @@ Applications compiled for 5.5 will continue to run with 5.6. Some of the changes listed in this file include issue tracking numbers corresponding to tasks in the Qt Bug Tracker: - http://bugreports.qt-project.org/ + https://bugreports.qt.io/ Each of these identifiers can be entered in the bug tracker to obtain more information about a particular change. -- cgit v1.2.3 From a82094b63b8993630c37f7a71dac19d3f96b2804 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 19 May 2016 13:26:40 +0200 Subject: Bump version Change-Id: Id97d3672ea1bd153df57f8986994accd70ae798f --- .qmake.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.qmake.conf b/.qmake.conf index 5c42719444..9665d2902a 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -2,4 +2,4 @@ load(qt_build_config) CONFIG += qt_example_installs CONFIG += warning_clean -MODULE_VERSION = 5.6.1 +MODULE_VERSION = 5.6.2 -- cgit v1.2.3 From 18e77550f149722e0554f24ddfa326e03a9fef10 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 19 May 2016 15:56:10 -0700 Subject: Workaround for crashes in QtQml code relating to null this pointers When compiled in release mode with GCC 6, QtQml crashes. This option gets works around the issue by instructing the compiler not to delete null pointer checks for pointers that the standard says cannot be null, yet apparently are. This is a temporary workaround until a proper solution is found. Change-Id: Id3aab65533904562a6cbfffd14501a185fc91179 Reviewed-by: Lars Knoll --- src/qml/qml.pro | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qml/qml.pro b/src/qml/qml.pro index e30c39c8b9..f4862a17a6 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -16,6 +16,11 @@ exists("qqml_enable_gcov") { LIBS_PRIVATE += -lgcov } +greaterThan(QT_GCC_MAJOR_VERSION, 5) { + # Our code is bad. Temporary workaround. + QMAKE_CXXFLAGS += -fno-delete-null-pointer-checks +} + QMAKE_DOCS = $$PWD/doc/qtqml.qdocconf # 2415: variable "xx" of static storage duration was declared but never referenced -- cgit v1.2.3 From e7da97bf71108a0acd72629e383f884e2d756477 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Mon, 9 Feb 2015 01:16:51 +0100 Subject: QQuickWindow: Only send focus events to items that actually gain focus Sending focus events may result in further changes to which item actually has focus, so we cannot blindly send the focus events without first checking that it hasn't subsequently changed. To accomplish this, we delay sending events as long as possible, ensuring that all necessary bookkeeping is done first to ensure internal consistency. Task-number: QTBUG-40145 Change-Id: I7d93b3f8e3fea2ecce2151c88c29601deda12453 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 31 ++++++++++++---- .../qquicktextinput/data/focusOnlyOneOnPress.qml | 32 +++++++++++++++++ .../quick/qquicktextinput/tst_qquicktextinput.cpp | 41 ++++++++++++++++++++++ 3 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 tests/auto/quick/qquicktextinput/data/focusOnlyOneOnPress.qml diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ef534fe35d..14e7915dba 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -785,8 +785,10 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + QQuickItem *oldActiveFocusItem = 0; QQuickItem *currentActiveFocusItem = activeFocusItem; QQuickItem *newActiveFocusItem = 0; + bool sendFocusIn = false; lastFocusReason = reason; @@ -794,7 +796,6 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q // Does this change the active focus? if (item == contentItem || scopePrivate->activeFocus) { - QQuickItem *oldActiveFocusItem = 0; oldActiveFocusItem = activeFocusItem; if (item->isEnabled()) { newActiveFocusItem = item; @@ -813,8 +814,6 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q #endif activeFocusItem = 0; - QFocusEvent event(QEvent::FocusOut, reason); - q->sendEvent(oldActiveFocusItem, &event); QQuickItem *afi = oldActiveFocusItem; while (afi && afi != scope) { @@ -859,7 +858,19 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q afi = afi->parentItem(); } updateFocusItemTransform(); + sendFocusIn = true; + } + + // Now that all the state is changed, emit signals & events + // We must do this last, as this process may result in further changes to + // focus. + if (oldActiveFocusItem) { + QFocusEvent event(QEvent::FocusOut, reason); + q->sendEvent(oldActiveFocusItem, &event); + } + // Make sure that the FocusOut didn't result in another focus change. + if (sendFocusIn && activeFocusItem == newActiveFocusItem) { QFocusEvent event(QEvent::FocusIn, reason); q->sendEvent(newActiveFocusItem, &event); } @@ -912,9 +923,6 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, activeFocusItem = 0; if (oldActiveFocusItem) { - QFocusEvent event(QEvent::FocusOut, reason); - q->sendEvent(oldActiveFocusItem, &event); - QQuickItem *afi = oldActiveFocusItem; while (afi && afi != scope) { if (QQuickItemPrivate::get(afi)->activeFocus) { @@ -944,7 +952,18 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Q_ASSERT(newActiveFocusItem == scope); activeFocusItem = scope; updateFocusItemTransform(); + } + + // Now that all the state is changed, emit signals & events + // We must do this last, as this process may result in further changes to + // focus. + if (oldActiveFocusItem) { + QFocusEvent event(QEvent::FocusOut, reason); + q->sendEvent(oldActiveFocusItem, &event); + } + // Make sure that the FocusOut didn't result in another focus change. + if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) { QFocusEvent event(QEvent::FocusIn, reason); q->sendEvent(newActiveFocusItem, &event); } diff --git a/tests/auto/quick/qquicktextinput/data/focusOnlyOneOnPress.qml b/tests/auto/quick/qquicktextinput/data/focusOnlyOneOnPress.qml new file mode 100644 index 0000000000..037b36c8ff --- /dev/null +++ b/tests/auto/quick/qquicktextinput/data/focusOnlyOneOnPress.qml @@ -0,0 +1,32 @@ +import QtQuick 2.2 + +Rectangle { + width: 400 + height: 400 + + Column { + spacing: 5 + TextInput { + objectName: "first" + onEditingFinished: second.focus = true + width: 100 + Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 } + } + TextInput { + id: second + objectName: "second" + onEditingFinished: third.focus = true + width: 100 + Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 } + } + TextInput { + objectName: "third" + id: third + width: 100 + Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 } + } + Component.onCompleted: { + second.focus = true + } + } +} diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 70d3906ff3..e57a95184c 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -158,6 +158,7 @@ private slots: #endif void readOnly(); void focusOnPress(); + void focusOnPressOnlyOneItem(); void openInputPanel(); void setHAlignClearCache(); @@ -3457,6 +3458,46 @@ void tst_qquicktextinput::focusOnPress() QTest::mouseRelease(&window, Qt::LeftButton, noModifiers); } +void tst_qquicktextinput::focusOnPressOnlyOneItem() +{ + QQuickView window(testFileUrl("focusOnlyOneOnPress.qml")); + window.show(); + window.requestActivate(); + QTest::qWaitForWindowActive(&window); + + QQuickTextInput *first = window.rootObject()->findChild("first"); + QQuickTextInput *second = window.rootObject()->findChild("second"); + QQuickTextInput *third = window.rootObject()->findChild("third"); + + // second is focused onComplete + QVERIFY(second->hasActiveFocus()); + + // and first will try focus when we press it + QVERIFY(first->focusOnPress()); + + // write some text to start editing + QTest::keyClick(&window, Qt::Key_A); + + // click the first input. naturally, we are giving focus on press, but + // second's editingFinished also attempts to assign focus. lastly, focus + // should bounce back to second from first's editingFinished signal. + // + // this is a contrived example to be sure, but at the end of this, the + // important thing is that only one thing should have activeFocus. + Qt::KeyboardModifiers noModifiers = 0; + QTest::mousePress(&window, Qt::LeftButton, noModifiers, QPoint(10, 10)); + + // make sure the press is processed. + QGuiApplication::processEvents(); + + QVERIFY(second->hasActiveFocus()); // make sure it's still there + QVERIFY(!third->hasActiveFocus()); // make sure it didn't end up anywhere else + QVERIFY(!first->hasActiveFocus()); // make sure it didn't end up anywhere else + + // reset state + QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, QPoint(10, 10)); +} + void tst_qquicktextinput::openInputPanel() { PlatformInputContext platformInputContext; -- cgit v1.2.3 From 9b5063dba6f400c494e29e36748975db4b592700 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 12 May 2016 22:42:05 +0200 Subject: Fix grammar in QQuickItem::grabToImage() documentation Change-Id: Ic63f1b2ba80ea4121f127432333757a469337870 Reviewed-by: Frank Meerkoetter Reviewed-by: Robin Burchell --- src/quick/items/qquickitemgrabresult.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp index d05ecc9076..5820c0b007 100644 --- a/src/quick/items/qquickitemgrabresult.cpp +++ b/src/quick/items/qquickitemgrabresult.cpp @@ -300,7 +300,7 @@ QQuickItemGrabResult *QQuickItemGrabResultPrivate::create(QQuickItem *item, cons * Use \a targetSize to specify the size of the target image. By default, the * result will have the same size as item. * - * If the grab could not be initiated, the function returns a \c null. + * If the grab could not be initiated, the function returns \c null. * * \note This function will render the item to an offscreen surface and * copy that surface from the GPU's memory into the CPU's memory, which can -- cgit v1.2.3 From 82b0b31fe47a6a54c500f2cbea45b37fc28f3bee Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Mon, 2 May 2016 13:11:26 +0200 Subject: Fix crash for unknown QQmlListModel roles in debug builds If a role is unknown, trying to access it will crash in getExistingRole. Fixed that and now return QVariant() for unknown roles. Change-Id: Iad5c1292a4faee893fbc5a69984cf776aca85d70 Reviewed-by: Shawn Rutledge Reviewed-by: Robin Burchell --- src/qml/types/qqmllistmodel.cpp | 2 ++ tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 4b0aa47c63..3d71621290 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -392,6 +392,8 @@ void ListModel::updateCacheIndices() QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng) { + if (roleIndex >= m_layout->roleCount()) + return QVariant(); ListElement *e = elements[elementIndex]; const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); return e->getProperty(r, owner, eng); diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index d26c1c584b..6b1deceaf2 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -110,6 +110,7 @@ private slots: void get_nested(); void get_nested_data(); void crash_model_with_multiple_roles(); + void crash_model_with_unknown_roles(); void set_model_cache(); void property_changes(); void property_changes_data(); @@ -968,6 +969,21 @@ void tst_qqmllistmodel::crash_model_with_multiple_roles() delete rootItem; } +void tst_qqmllistmodel::crash_model_with_unknown_roles() +{ + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("multipleroles.qml")); + QScopedPointer rootItem(component.create()); + QVERIFY(component.errorString().isEmpty()); + QVERIFY(rootItem != 0); + QQmlListModel *model = rootItem->findChild("listModel"); + QVERIFY(model != 0); + + // used to cause a crash in debug builds + model->index(0, 0, QModelIndex()).data(Qt::DisplayRole); + model->index(0, 0, QModelIndex()).data(Qt::UserRole); +} + //QTBUG-15190 void tst_qqmllistmodel::set_model_cache() { -- cgit v1.2.3 From 5e651178b2d1ac9cea70913de21e0c5b2b7aaa0d Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 19 May 2016 13:10:19 +0200 Subject: Fix QQuickItem change listeners All listeners should get invoked, even if they remove themselves while iterating the listeners. An index-based loop would skip the next listener in the list. This change replaces the QPODVector with a QVector, so we can make a cheap copy before iterating the listeners. Change-Id: I2430b3763184a40ad1c5c3a68d36fecafcadb3ee Task-number: QTBUG-53453 Reviewed-by: Robin Burchell --- src/quick/items/qquickitem.cpp | 78 +++++----- src/quick/items/qquickitem_p.h | 6 +- tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 198 ++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 39 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 3573788855..a8c9d34110 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2336,8 +2336,9 @@ QQuickItem::~QQuickItem() while (!d->childItems.isEmpty()) d->childItems.constFirst()->setParentItem(0); - for (int ii = 0; ii < d->changeListeners.count(); ++ii) { - QQuickAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); + const auto listeners = d->changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { + QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate(); if (anchor) anchor->clearItem(this); } @@ -2346,14 +2347,13 @@ QQuickItem::~QQuickItem() update item anchors that depended on us unless they are our child (and will also be destroyed), or our sibling, and our parent is also being destroyed. */ - for (int ii = 0; ii < d->changeListeners.count(); ++ii) { - QQuickAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); + for (const QQuickItemPrivate::ChangeListener &change : listeners) { + QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate(); if (anchor && anchor->item && anchor->item->parentItem() && anchor->item->parentItem() != this) anchor->update(); } - for (int ii = 0; ii < d->changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Destroyed) change.listener->itemDestroyed(this); } @@ -3595,8 +3595,8 @@ QQuickAnchors *QQuickItemPrivate::anchors() const void QQuickItemPrivate::siblingOrderChanged() { Q_Q(QQuickItem); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + const auto listeners = changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::SiblingOrder) { change.listener->itemSiblingOrderChanged(q); } @@ -3704,8 +3704,8 @@ void QQuickItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo bool widthChange = (newGeometry.width() != oldGeometry.width()); bool heightChange = (newGeometry.height() != oldGeometry.height()); - for (int ii = 0; ii < d->changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + const auto listeners = d->changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Geometry) { if (change.gTypes == QQuickItemPrivate::GeometryChange) { change.listener->itemGeometryChanged(this, newGeometry, oldGeometry); @@ -3841,7 +3841,7 @@ void QQuickItemPrivate::removeItemChangeListener(QQuickItemChangeListener *liste void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types) { ChangeListener change(listener, types); - int index = changeListeners.find(change); + int index = changeListeners.indexOf(change); if (index > -1) changeListeners[index].gTypes = change.gTypes; //we may have different GeometryChangeTypes else @@ -3855,7 +3855,7 @@ void QQuickItemPrivate::updateOrRemoveGeometryChangeListener(QQuickItemChangeLis if (types == NoChange) { changeListeners.removeOne(change); } else { - int index = changeListeners.find(change); + int index = changeListeners.indexOf(change); if (index > -1) changeListeners[index].gTypes = change.gTypes; //we may have different GeometryChangeTypes } @@ -4264,8 +4264,8 @@ void QQuickItem::setBaselineOffset(qreal offset) d->baselineOffset = offset; - for (int ii = 0; ii < d->changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + const auto listeners = d->changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Geometry) { QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate(); if (anchor) @@ -5936,66 +5936,72 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt { Q_Q(QQuickItem); switch (change) { - case QQuickItem::ItemChildAddedChange: + case QQuickItem::ItemChildAddedChange: { q->itemChange(change, data); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + const auto listeners = changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Children) { change.listener->itemChildAdded(q, data.item); } } break; - case QQuickItem::ItemChildRemovedChange: + } + case QQuickItem::ItemChildRemovedChange: { q->itemChange(change, data); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + const auto listeners = changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Children) { change.listener->itemChildRemoved(q, data.item); } } break; + } case QQuickItem::ItemSceneChange: q->itemChange(change, data); break; - case QQuickItem::ItemVisibleHasChanged: + case QQuickItem::ItemVisibleHasChanged: { q->itemChange(change, data); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + const auto listeners = changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Visibility) { change.listener->itemVisibilityChanged(q); } } break; - case QQuickItem::ItemParentHasChanged: + } + case QQuickItem::ItemParentHasChanged: { q->itemChange(change, data); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + const auto listeners = changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Parent) { change.listener->itemParentChanged(q, data.item); } } break; - case QQuickItem::ItemOpacityHasChanged: + } + case QQuickItem::ItemOpacityHasChanged: { q->itemChange(change, data); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + const auto listeners = changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Opacity) { change.listener->itemOpacityChanged(q); } } break; + } case QQuickItem::ItemActiveFocusHasChanged: q->itemChange(change, data); break; - case QQuickItem::ItemRotationHasChanged: + case QQuickItem::ItemRotationHasChanged: { q->itemChange(change, data); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + const auto listeners = changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Rotation) { change.listener->itemRotationChanged(q); } } break; + } case QQuickItem::ItemAntialiasingHasChanged: // fall through case QQuickItem::ItemDevicePixelRatioHasChanged: @@ -6350,8 +6356,8 @@ void QQuickItem::resetWidth() void QQuickItemPrivate::implicitWidthChanged() { Q_Q(QQuickItem); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + const auto listeners = changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::ImplicitWidth) { change.listener->itemImplicitWidthChanged(q); } @@ -6514,8 +6520,8 @@ void QQuickItem::resetHeight() void QQuickItemPrivate::implicitHeightChanged() { Q_Q(QQuickItem); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + const auto listeners = changeListeners; + for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::ImplicitHeight) { change.listener->itemImplicitHeightChanged(q); } diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index ad649e5b5f..fed3e88b68 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -62,7 +62,6 @@ #include #include "qquickclipnode_p.h" -#include #include #include #include @@ -332,7 +331,7 @@ public: Q_DECLARE_FLAGS(GeometryChangeTypes, GeometryChangeType) struct ChangeListener { - ChangeListener(QQuickItemChangeListener *l, QQuickItemPrivate::ChangeTypes t) : listener(l), types(t), gTypes(GeometryChange) {} + ChangeListener(QQuickItemChangeListener *l = Q_NULLPTR, QQuickItemPrivate::ChangeTypes t = 0) : listener(l), types(t), gTypes(GeometryChange) {} ChangeListener(QQuickItemChangeListener *l, QQuickItemPrivate::GeometryChangeTypes gt) : listener(l), types(Geometry), gTypes(gt) {} QQuickItemChangeListener *listener; QQuickItemPrivate::ChangeTypes types; @@ -386,7 +385,7 @@ public: inline Qt::MouseButtons acceptedMouseButtons() const; - QPODVector changeListeners; + QVector changeListeners; void addItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types); void removeItemChangeListener(QQuickItemChangeListener *, ChangeTypes types); @@ -932,6 +931,7 @@ QSGNode *QQuickItemPrivate::childContainerNode() } Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickItemPrivate::ChangeTypes) +Q_DECLARE_TYPEINFO(QQuickItemPrivate::ChangeListener, Q_PRIMITIVE_TYPE); QT_END_NAMESPACE diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 62ff09e698..a087efd6b8 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include "../../shared/util.h" @@ -107,6 +108,7 @@ private slots: void childrenProperty(); void resourcesProperty(); + void changeListener(); void transformCrash(); void implicitSize(); void qtbug_16871(); @@ -2694,6 +2696,202 @@ void tst_QQuickItem::childrenRectBottomRightCorner() delete window; } +struct TestListener : public QQuickItemChangeListener +{ + TestListener(bool remove = false) : remove(remove) { reset(); } + + void itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &) override { ++itemGeometryChanges; value = newGeometry; } + void itemSiblingOrderChanged(QQuickItem *) override { ++itemSiblingOrderChanges; } + void itemVisibilityChanged(QQuickItem *) override { ++itemVisibilityChanges; } + void itemOpacityChanged(QQuickItem *) override { ++itemOpacityChanges; } + void itemRotationChanged(QQuickItem *) override { ++itemRotationChanges; } + void itemImplicitWidthChanged(QQuickItem *) override { ++itemImplicitWidthChanges; } + void itemImplicitHeightChanged(QQuickItem *) override { ++itemImplicitHeightChanges; } + + void itemDestroyed(QQuickItem *item) override + { + ++itemDestructions; + // QTBUG-53453 + if (remove) + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed); + } + void itemChildAdded(QQuickItem *item, QQuickItem *child) override + { + ++itemChildAdditions; + value = QVariant::fromValue(child); + // QTBUG-53453 + if (remove) + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Children); + } + void itemChildRemoved(QQuickItem *item, QQuickItem *child) override + { + ++itemChildRemovals; + value = QVariant::fromValue(child); + // QTBUG-53453 + if (remove) + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Children); + } + void itemParentChanged(QQuickItem *item, QQuickItem *parent) override + { + ++itemParentChanges; + value = QVariant::fromValue(parent); + // QTBUG-53453 + if (remove) + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Parent); + } + + QQuickAnchorsPrivate *anchorPrivate() override { return nullptr; } + + void reset() + { + value = QVariant(); + itemGeometryChanges = 0; + itemSiblingOrderChanges = 0; + itemVisibilityChanges = 0; + itemOpacityChanges = 0; + itemDestructions = 0; + itemChildAdditions = 0; + itemChildRemovals = 0; + itemParentChanges = 0; + itemRotationChanges = 0; + itemImplicitWidthChanges = 0; + itemImplicitHeightChanges = 0; + } + + bool remove; + QVariant value; + int itemGeometryChanges; + int itemSiblingOrderChanges; + int itemVisibilityChanges; + int itemOpacityChanges; + int itemDestructions; + int itemChildAdditions; + int itemChildRemovals; + int itemParentChanges; + int itemRotationChanges; + int itemImplicitWidthChanges; + int itemImplicitHeightChanges; +}; + +void tst_QQuickItem::changeListener() +{ + QQuickItem item; + TestListener itemListener; + QQuickItemPrivate::get(&item)->addItemChangeListener(&itemListener, QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | + QQuickItemPrivate::Opacity | QQuickItemPrivate::Rotation); + + item.setImplicitWidth(50); + QCOMPARE(itemListener.itemImplicitWidthChanges, 1); + QCOMPARE(itemListener.itemGeometryChanges, 1); + QCOMPARE(itemListener.value, QVariant(QRectF(0,0,50,0))); + + item.setImplicitHeight(50); + QCOMPARE(itemListener.itemImplicitHeightChanges, 1); + QCOMPARE(itemListener.itemGeometryChanges, 2); + QCOMPARE(itemListener.value, QVariant(QRectF(0,0,50,50))); + + item.setWidth(100); + QCOMPARE(itemListener.itemGeometryChanges, 3); + QCOMPARE(itemListener.value, QVariant(QRectF(0,0,100,50))); + + item.setHeight(100); + QCOMPARE(itemListener.itemGeometryChanges, 4); + QCOMPARE(itemListener.value, QVariant(QRectF(0,0,100,100))); + + item.setOpacity(0.5); + QCOMPARE(itemListener.itemOpacityChanges, 1); + + item.setRotation(90); + QCOMPARE(itemListener.itemRotationChanges, 1); + + QQuickItem *parent = new QQuickItem; + TestListener parentListener; + QQuickItemPrivate::get(parent)->addItemChangeListener(&parentListener, QQuickItemPrivate::Children); + + QQuickItem *child1 = new QQuickItem; + QQuickItem *child2 = new QQuickItem; + TestListener child1Listener; + TestListener child2Listener; + QQuickItemPrivate::get(child1)->addItemChangeListener(&child1Listener, QQuickItemPrivate::Parent | QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::Destroyed); + QQuickItemPrivate::get(child2)->addItemChangeListener(&child2Listener, QQuickItemPrivate::Parent | QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::Destroyed); + + child1->setParentItem(parent); + QCOMPARE(parentListener.itemChildAdditions, 1); + QCOMPARE(parentListener.value, QVariant::fromValue(child1)); + QCOMPARE(child1Listener.itemParentChanges, 1); + QCOMPARE(child1Listener.value, QVariant::fromValue(parent)); + + child2->setParentItem(parent); + QCOMPARE(parentListener.itemChildAdditions, 2); + QCOMPARE(parentListener.value, QVariant::fromValue(child2)); + QCOMPARE(child2Listener.itemParentChanges, 1); + QCOMPARE(child2Listener.value, QVariant::fromValue(parent)); + + child2->stackBefore(child1); + QCOMPARE(child1Listener.itemSiblingOrderChanges, 1); + QCOMPARE(child2Listener.itemSiblingOrderChanges, 1); + + child1->setParentItem(nullptr); + QCOMPARE(parentListener.itemChildRemovals, 1); + QCOMPARE(parentListener.value, QVariant::fromValue(child1)); + QCOMPARE(child1Listener.itemParentChanges, 2); + QCOMPARE(child1Listener.value, QVariant::fromValue(nullptr)); + + delete child1; + QCOMPARE(child1Listener.itemDestructions, 1); + + delete child2; + QCOMPARE(parentListener.itemChildRemovals, 2); + QCOMPARE(parentListener.value, QVariant::fromValue(child2)); + QCOMPARE(child2Listener.itemParentChanges, 2); + QCOMPARE(child2Listener.value, QVariant::fromValue(nullptr)); + QCOMPARE(child2Listener.itemDestructions, 1); + + QQuickItemPrivate::get(parent)->removeItemChangeListener(&parentListener, QQuickItemPrivate::Children); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + + // QTBUG-53453: all listeners should get invoked even if they remove themselves while iterating the listeners + QList listeners; + for (int i = 0; i < 5; ++i) + listeners << new TestListener(true); + + // itemChildAdded() x 5 + foreach (TestListener *listener, listeners) + QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + child1 = new QQuickItem(parent); + foreach (TestListener *listener, listeners) + QCOMPARE(listener->itemChildAdditions, 1); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + + // itemParentChanged() x 5 + foreach (TestListener *listener, listeners) + QQuickItemPrivate::get(child1)->addItemChangeListener(listener, QQuickItemPrivate::Parent); + QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.count(), listeners.count()); + child1->setParentItem(nullptr); + foreach (TestListener *listener, listeners) + QCOMPARE(listener->itemParentChanges, 1); + QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.count(), 0); + + // itemChildRemoved() x 5 + child1->setParentItem(parent); + foreach (TestListener *listener, listeners) + QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + delete child1; + foreach (TestListener *listener, listeners) + QCOMPARE(listener->itemChildRemovals, 1); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + + // itemDestroyed() x 5 + foreach (TestListener *listener, listeners) + QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Destroyed); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + delete parent; + foreach (TestListener *listener, listeners) + QCOMPARE(listener->itemDestructions, 1); +} + // QTBUG-13893 void tst_QQuickItem::transformCrash() { -- cgit v1.2.3 From 40a80ec9d712d8c40ebd6a6373322c3332ff5b50 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 23 May 2016 16:01:44 +0200 Subject: Avoid Canvas crashes with qtquickcompiler Change-Id: Ic87052308706b8ef71e2f27837abfbaea57c43cc Task-number: QTBUG-49692 Reviewed-by: Mitch Curtis --- src/quick/items/context2d/qquickcanvasitem.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index 9fb49f9c9e..47ac2c49da 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -675,10 +675,14 @@ void QQuickCanvasItem::itemChange(QQuickItem::ItemChange change, const QQuickIte QSGRenderContext *context = QQuickWindowPrivate::get(d->window)->context; // Rendering to FramebufferObject needs a valid OpenGL context. - if (context != 0 && (d->renderTarget != FramebufferObject || context->isValid())) - sceneGraphInitialized(); - else + if (context != 0 && (d->renderTarget != FramebufferObject || context->isValid())) { + // Defer the call. In some (arguably incorrect) cases we get here due + // to ItemSceneChange with the user-supplied property values not yet + // set. Work this around by a deferred invoke. (QTBUG-49692) + QMetaObject::invokeMethod(this, "sceneGraphInitialized", Qt::QueuedConnection); + } else { connect(d->window, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized())); + } } void QQuickCanvasItem::updatePolish() -- cgit v1.2.3 From ba87f2a375b33564ea2b6484d2c22a37ff979804 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Wed, 20 Apr 2016 21:23:02 +0200 Subject: do not enable example installs explicitly any more it's done centrally now. Change-Id: I4c0641f988da7c6bd0522453a2c7e10c8d640f02 Reviewed-by: Simon Hausmann --- .qmake.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/.qmake.conf b/.qmake.conf index 9665d2902a..c945d77ed8 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,5 +1,4 @@ load(qt_build_config) -CONFIG += qt_example_installs CONFIG += warning_clean MODULE_VERSION = 5.6.2 -- cgit v1.2.3 From 818abf6a8cc9c862c10e14700f41b63006e9ecca Mon Sep 17 00:00:00 2001 From: Marco Benelli Date: Wed, 13 Apr 2016 14:58:28 +0200 Subject: qmlplugindump: add a command line option. Add the option -noforceqtquick that prevent the inclusion of the QtQuick dependency in the resulting qmltypes file. Including the QtQuick dependency interfere with the generation of some qmltypes, such that the one of QtQuick itself. Change-Id: I63a44632b8dbabdd1ffecf16da050a9429e557da Reviewed-by: Kai Koehne Reviewed-by: Thomas Hartmann Reviewed-by: Erik Verbruggen --- tools/qmlplugindump/main.cpp | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 395b3cd195..d821c25b4e 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -75,6 +75,12 @@ static const uint qtQmlMajorVersion = 2; static const uint qtQmlMinorVersion = 2; +static const uint qtQuickMajorVersion = 2; +static const uint qtQuickMinorVersion = 7; + +const QString qtQuickQualifiedName = QString::fromLatin1("QtQuick %1.%2") + .arg(qtQuickMajorVersion) + .arg(qtQuickMinorVersion); QString pluginImportPath; bool verbose = false; @@ -730,7 +736,7 @@ void sigSegvHandler(int) { void printUsage(const QString &appName) { std::cerr << qPrintable(QString( - "Usage: %1 [-v] [-noinstantiate] [-defaultplatform] [-[non]relocatable] [-dependencies ] [-merge ] module.uri version [module/import/path]\n" + "Usage: %1 [-v] [-noinstantiate] [-defaultplatform] [-[non]relocatable] [-dependencies ] [-merge ] [-noforceqtquick] module.uri version [module/import/path]\n" " %1 [-v] [-noinstantiate] -path path/to/qmldir/directory [version]\n" " %1 [-v] -builtins\n" "Example: %1 Qt.labs.folderlistmodel 2.0 /home/user/dev/qt-install/imports").arg( @@ -738,7 +744,8 @@ void printUsage(const QString &appName) } static bool readDependenciesData(QString dependenciesFile, const QByteArray &fileData, - QStringList *dependencies, const QStringList &urisToSkip) { + QStringList *dependencies, const QStringList &urisToSkip, + bool forceQtQuickDependency = true) { if (verbose) { std::cerr << "parsing " << qPrintable( dependenciesFile ) << " skipping"; @@ -793,8 +800,8 @@ static bool readDependenciesData(QString dependenciesFile, const QByteArray &fil // qmlplugindump used to import QtQuick, so all types defined in QtQuick used to be skipped when dumping. // Now that it imports only Qt, it is no longer the case: if no dependency is found all the types defined // in QtQuick will be dumped, causing conflicts. - if (dependencies->isEmpty()) - dependencies->push_back(QLatin1String("QtQuick 2.0")); + if (forceQtQuickDependency && dependencies->isEmpty()) + dependencies->push_back(qtQuickQualifiedName); return true; } @@ -812,11 +819,12 @@ static bool readDependenciesFile(const QString &dependenciesFile, QStringList *d return false; } QByteArray fileData = f.readAll(); - return readDependenciesData(dependenciesFile, fileData, dependencies, urisToSkip); + return readDependenciesData(dependenciesFile, fileData, dependencies, urisToSkip, false); } static bool getDependencies(const QQmlEngine &engine, const QString &pluginImportUri, - const QString &pluginImportVersion, QStringList *dependencies) + const QString &pluginImportVersion, QStringList *dependencies, + bool forceQtQuickDependency) { QFileInfo selfExe(QCoreApplication::applicationFilePath()); QString command = selfExe.absoluteDir().filePath(QLatin1String("qmlimportscanner") @@ -849,7 +857,7 @@ static bool getDependencies(const QQmlEngine &engine, const QString &pluginImpor } QByteArray depencenciesData = importScanner.readAllStandardOutput(); if (!readDependenciesData(QLatin1String(""), depencenciesData, - dependencies, QStringList(pluginImportUri))) { + dependencies, QStringList(pluginImportUri), forceQtQuickDependency)) { std::cerr << "failed to proecess output of qmlimportscanner" << std::endl; return false; } @@ -986,6 +994,7 @@ int main(int argc, char *argv[]) bool relocatable = true; QString dependenciesFile; QString mergeFile; + bool forceQtQuickDependency = true; enum Action { Uri, Path, Builtins }; Action action = Uri; { @@ -1030,6 +1039,9 @@ int main(int argc, char *argv[]) action = Builtins; } else if (arg == QLatin1String("-v")) { verbose = true; + } else if (arg == QLatin1String("--noforceqtquick") + || arg == QLatin1String("-noforceqtquick")){ + forceQtQuickDependency = false; } else if (arg == QLatin1String("--defaultplatform") || arg == QLatin1String("-defaultplatform")) { continue; @@ -1098,9 +1110,12 @@ int main(int argc, char *argv[]) calculateDependencies = !readDependenciesFile(dependenciesFile, &dependencies, QStringList(pluginImportUri)) && calculateDependencies; if (calculateDependencies) - getDependencies(engine, pluginImportUri, pluginImportVersion, &dependencies); + getDependencies(engine, pluginImportUri, pluginImportVersion, &dependencies, + forceQtQuickDependency); + compactDependencies(&dependencies); + QString qtQmlImportString = QString::fromLatin1("import QtQml %1.%2") .arg(qtQmlMajorVersion) .arg(qtQmlMinorVersion); -- cgit v1.2.3 From 441baca74eba3cfcb6a63b0a63be54e6a038fba7 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 24 May 2016 13:38:10 +0200 Subject: Fix tst_qquicktext::lineLaidOutRelayout() with > 1 font MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When more than one font is needed to display the text in the layout, then the line height of the layout may not be constant. This rewrites the test to properly handle this case. Task-number: QTBUG-51934 Change-Id: I0e0cdfdc06b4a39684b33d10efdd58f98dea06b8 Reviewed-by: Tony Sarajärvi Reviewed-by: Simon Hausmann --- tests/auto/quick/qquicktext/tst_qquicktext.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index 0e8811bb17..50c75046a6 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -2772,18 +2772,19 @@ void tst_qquicktext::lineLaidOutRelayout() QVERIFY(!textPrivate->extra.isAllocated()); - qreal maxH = 0; + qreal y = 0.0; for (int i = 0; i < textPrivate->layout.lineCount(); ++i) { QTextLine line = textPrivate->layout.lineAt(i); const QRectF r = line.rect(); - const qreal h = line.height(); if (r.x() == 0) { - QCOMPARE(r.y(), i * h); - maxH = qMax(maxH, r.y() + h); + QCOMPARE(r.y(), y); } else { + if (qFuzzyIsNull(r.y())) + y = 0.0; QCOMPARE(r.x(), myText->width() / 2); - QCOMPARE(r.y(), i * h - maxH); + QCOMPARE(r.y(), y); } + y += line.height(); } delete window; -- cgit v1.2.3 From 3b2a72068f86258511fae8fd4764f0f535e5d4c8 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 25 May 2016 11:41:20 +0200 Subject: Flickable: MovementEndingTimerInterval is used only on OS X MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After 9b8d0bff, some compilers complain that it's an unused variable. Change-Id: I4a89ce7a7d73cfc039a2e2d1ad48ec7d073cf7cc Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickflickable.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 7d602db57e..32d445dbc5 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -59,7 +59,9 @@ static const int FlickThreshold = 15; // will ensure the Flickable retains the grab on consecutive flicks. static const int RetainGrabVelocity = 100; +#ifdef Q_OS_OSX static const int MovementEndingTimerInterval = 100; +#endif static qreal EaseOvershoot(qreal t) { return qAtan(t); -- cgit v1.2.3 From 0e0535284379e6b740d439fa3dba2e2b5f695756 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 24 May 2016 16:22:33 +0200 Subject: QmlProfiler: When flushing data, send it in the right order Some of the adapters immediately return dataReady() when reportData() is invoked. This means that there is only one adapter in the start times list then, which in turn causes all the data from that adapter to be sent at once, without caring for the other adapters' timestamps. Change-Id: Ic1e12fdcefb0a691067518fba100368f13c927f7 Task-number: QTBUG-53590 Reviewed-by: Simon Hausmann --- src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp | 8 ++++++-- .../qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index 2654cf662b..a5ee494ced 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -407,20 +407,24 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message) void QQmlProfilerServiceImpl::flush() { QMutexLocker lock(&m_configMutex); + QList reporting; foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers) { if (profiler->isRunning()) { m_startTimes.insert(-1, profiler); - profiler->reportData(); + reporting.append(profiler); } } foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) { if (profiler->isRunning()) { m_startTimes.insert(-1, profiler); - profiler->reportData(); + reporting.append(profiler); } } + + foreach (QQmlAbstractProfilerAdapter *profiler, reporting) + profiler->reportData(); } QT_END_NAMESPACE diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index 0e63e18952..670d58e4fd 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -135,7 +135,7 @@ public: }; QQmlProfilerClient(QQmlDebugConnection *connection) - : QQmlDebugClient(QLatin1String("CanvasFrameRate"), connection) + : QQmlDebugClient(QLatin1String("CanvasFrameRate"), connection), lastTimestamp(-1) { } @@ -145,6 +145,8 @@ public: QVector asynchronousMessages; QVector pixmapMessages; + qint64 lastTimestamp; + void setTraceState(bool enabled, quint32 flushInterval = 0) { QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); @@ -343,6 +345,10 @@ void QQmlProfilerClient::messageReceived(const QByteArray &message) break; } QVERIFY(stream.atEnd()); + + QVERIFY(data.time >= lastTimestamp); + lastTimestamp = data.time; + if (data.messageType == QQmlProfilerClient::PixmapCacheEvent) pixmapMessages.append(data); else if (data.messageType == QQmlProfilerClient::SceneGraphFrame || -- cgit v1.2.3 From 7dcda224fe73cb51a29e8946afd641a989d7209a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 25 May 2016 16:22:44 +0200 Subject: Fix crash with SignalTransition Don't crash when using SignalTransition with a signal object instead of the slot used to emit the signal. A signal object is just as good. Task-number: QTBUG-53596 Change-Id: I8a419d16ec0c257c9a798a83ee5bad338794cdd2 Reviewed-by: Michael Brasser --- src/imports/statemachine/signaltransition.cpp | 26 ++++++-- src/qml/jsruntime/qv4qobjectwrapper_p.h | 2 +- .../qmltest/statemachine/tst_signaltransition.qml | 76 ++++++++++++++++++++++ 3 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 tests/auto/qmltest/statemachine/tst_signaltransition.qml diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index 33ee11cbe7..4f6c769324 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -105,15 +105,27 @@ void SignalTransition::setSignal(const QJSValue &signal) QV4::ExecutionEngine *jsEngine = QV8Engine::getV4(QQmlEngine::contextForObject(this)->engine()); QV4::Scope scope(jsEngine); - QV4::Scoped qobjectSignal(scope, QJSValuePrivate::convertedToValue(jsEngine, m_signal)); - Q_ASSERT(qobjectSignal); - - QObject *sender = qobjectSignal->object(); - Q_ASSERT(sender); - QMetaMethod metaMethod = sender->metaObject()->method(qobjectSignal->methodIndex()); + QObject *sender; + QMetaMethod signalMethod; + + QV4::ScopedValue value(scope, QJSValuePrivate::convertedToValue(jsEngine, m_signal)); + + // Did we get the "slot" that can be used to invoke the signal? + if (QV4::QObjectMethod *signalSlot = value->as()) { + sender = signalSlot->object(); + Q_ASSERT(sender); + signalMethod = sender->metaObject()->method(signalSlot->methodIndex()); + } else if (QV4::QmlSignalHandler *signalObject = value->as()) { // or did we get the signal object (the one with the connect()/disconnect() functions) ? + sender = signalObject->object(); + Q_ASSERT(sender); + signalMethod = sender->metaObject()->method(signalObject->signalIndex()); + } else { + qmlInfo(this) << tr("Specified signal does not exist."); + return; + } QSignalTransition::setSenderObject(sender); - QSignalTransition::setSignal(metaMethod.methodSignature()); + QSignalTransition::setSignal(signalMethod.methodSignature()); connectTriggered(); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 1126013806..0fc39b222f 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -166,7 +166,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); }; -struct QmlSignalHandler : public QV4::Object +struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object { V4_OBJECT2(QmlSignalHandler, QV4::Object) V4_PROTOTYPE(signalHandlerPrototype) diff --git a/tests/auto/qmltest/statemachine/tst_signaltransition.qml b/tests/auto/qmltest/statemachine/tst_signaltransition.qml new file mode 100644 index 0000000000..0e35207670 --- /dev/null +++ b/tests/auto/qmltest/statemachine/tst_signaltransition.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Copyright (C) 2016 The Qt Company +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +import QtTest 1.1 +import QtQml.StateMachine 1.0 + +TestCase { + id: testCase + StateMachine { + id: machine + initialState: startState + State { + id: startState + SignalTransition { + id: signalTrans + signal: testCase.onMysignal + targetState: finalState + } + } + FinalState { + id: finalState + } + } + + SignalSpy { + id: finalStateActive + target: finalState + signalName: "activeChanged" + } + + signal mysignal() + + name: "testSignalTransition" + function test_signalTransition() + { + // Start statemachine, should not have reached finalState yet. + machine.start() + tryCompare(finalStateActive, "count", 0) + tryCompare(machine, "running", true) + + testCase.mysignal() + tryCompare(finalStateActive, "count", 1) + tryCompare(machine, "running", false) + } +} -- cgit v1.2.3 From 2d70b4b5c38263e866d9b317a576c952d3501874 Mon Sep 17 00:00:00 2001 From: Antti Kokko Date: Tue, 24 May 2016 12:17:19 +0300 Subject: Add QtDeclarative Qt5.7.0 changelog Change-Id: Ibc9b609d1ba4d949b1f41d8e5cb4e8150794b615 Reviewed-by: Robin Burchell Reviewed-by: Simon Hausmann Reviewed-by: Lars Knoll Reviewed-by: Shawn Rutledge --- dist/changes-5.7.0 | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 dist/changes-5.7.0 diff --git a/dist/changes-5.7.0 b/dist/changes-5.7.0 new file mode 100644 index 0000000000..0545fe7eb1 --- /dev/null +++ b/dist/changes-5.7.0 @@ -0,0 +1,98 @@ +Qt 5.7 introduces many new features and improvements as well as bugfixes +over the 5.6.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + + http://doc.qt.io/qt-5/index.html + +The Qt version 5.7 series is binary compatible with the 5.6.x series. +Applications compiled for 5.6 will continue to run with 5.7. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Important Behavior Changes * +**************************************************************************** + +QtQuick +------- + + * [QTBUG-41833] QQuickItem::childAt was incorrectly including any child + whose right or bottom edge was adjacent to the point being checked, + as if it had width+1 and height+1. An Item with a width of 100 + covers pixels from x=0..x=99, and likewise with height; so now, + calling childAt(100, 100) on its parent will not return it. + + * [QTBUG-51115] TextEdit and TextInput now clear their selection when + becoming read-only. + + * QtQuick.Layouts moved to the qtdeclarative repository. + +**************************************************************************** +* Library * +**************************************************************************** + +QtQml +----- + + - [QTBUG-52556] Made the QML Engine capable of locating QML sub-modules + from within a versioned parent module path. For example, QtQml.Models + 2.x can be either in QT_INSTALL_QML/QtQml/Models.2 or in + QT_INSTALL_QML/QtQml.2/Models. + - [QTBUG-36350] Added Connections::enabled property to allow toggling of the + signal handlers inside a Connections element. + - Enabled JIT for x86/x64 targets on Windows 10 and later. + - Enabled JIT for Aarch64. + +QtQuick +------- + + - Window: + * Added Window.window attached property, allowing access to the QQuickWindow + an Item belongs to. + + - GridView & ListView: + * [QTBUG-17051] Added keyNavigationEnabled property to allow mouse and + keyboard interaction to be selectively enabled/disabled. + * Sticky headers or footers are now correctly positioned in the case of + an empty view. + + - MouseArea: + * Added mouse.source property to enable distinguishing genuine mouse + events from those that are synthesized from touch or tablet events. + + - PathView: + * Added PathView::movementDirection, which sets the direction in which items + move when setting currentIndex. + + - QQuickItem: + * Added isAncestorOf() to determine if an item is the ancestor of another + item (i.e. the parent, or a parent further up the item tree). + * [QTBUG-28668] Added support for mapping item's coordinates to and from global + screen coordinates, in the form of Item::mapToGlobal() and + Item::mapFromGlobal(). + + - TextEdit/TextInput: + * [QTBUG-49503] Added TextEdit::preeditText & TextInput::preeditText, + which allow access to partial (uncommitted) text from an input method. + * [QTBUG-50428] Added TextEdit::clear() and TextInput::clear() which sets the + text to an empty string, but in addition, also clears partial (uncommitted) + text. + + - Loader: + * [QTBUG-29789] Object creation previously started asynchronously can be + forced to complete synchronously by changing the "asynchronous" property + from true to false. + +Qt.labs.folderlistmodel +----------------------- + + - FolderListModel + * [QTBUG-45566] Added FolderListModel::caseSensitive, to control whether or + not filtering is applied case sensitively. + -- cgit v1.2.3 From 181229127b439c9c2200a39daf70bd4f50f5670e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 26 May 2016 13:05:22 +0200 Subject: scene graph: document the qt.scenegraph.info category not env QSG_INFO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The use of the env var has been obsolete for some time, since change 7a8cc93a0546bc38c54343d640e63062c3b398b2, although it's still supported for backwards compatibility. Change-Id: Ibe57de2298c33bfa7df5a95c714d69403a0e9166 Reviewed-by: Topi Reiniö --- src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc index 443d189f58..9ccb05cdf1 100644 --- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc @@ -180,8 +180,8 @@ dedicated thread. Qt attempts to choose a suitable loop based on the platform and possibly the graphics drivers in use. When this is not satisfactory, or for testing purposes, the environment variable \c QSG_RENDER_LOOP can be used to force the usage of a given loop. To -verify which render loop is in use, launch the application with -\c QSG_INFO set to \c 1. +verify which render loop is in use, enable the \c qt.scenegraph.info +\l {QLoggingCategory}{logging category}. \note The \c threaded and \c windows render loops rely on the OpenGL implementation for throttling by requesting a swap interval of 1. Some -- cgit v1.2.3 From cba2ef0a37f1e6972f1eb641c7157f9c69861c13 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 20 May 2016 16:18:46 +0200 Subject: Fix grammar in PathView docs Change-Id: Ibce14fddcbb608ffddb69f01e168d10c924c2957 Reviewed-by: Frank Meerkoetter Reviewed-by: Shawn Rutledge Reviewed-by: Simon Hausmann Reviewed-by: Andy Shaw --- src/quick/items/qquickpathview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index deb5582495..73146f015e 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1019,7 +1019,7 @@ void QQuickPathView::setHighlightMoveDuration(int duration) /*! \qmlproperty real QtQuick::PathView::dragMargin - This property holds the maximum distance from the path that initiate mouse dragging. + This property holds the maximum distance from the path that initiates mouse dragging. By default the path can only be dragged by clicking on an item. If dragMargin is greater than zero, a drag can be initiated by clicking -- cgit v1.2.3 From dbf7efde1e04ac0ff445f64438078d87eb035b68 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 26 May 2016 17:22:34 +0200 Subject: V4: Mirror jsAlloc behavior at stack allocation. Allocating from the JS stack will zero out the memory, and thus indicate to valgrind that memory is not uninitialized. By first marking the whole stack as uninitialized and only then allocating 2 entries, the behavior for those two entries will now match the allocation behavior. This fixes a false positive when using valgrind. Change-Id: Icdb5279e1cfbfe6b5c385cc42c556edf721fa74b Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4engine.cpp | 8 ++++---- src/qml/jsruntime/qv4scopedvalue_p.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index fefc5b6308..5dc3e6151f 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -170,6 +170,10 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) /* writable */ true, /* executable */ false, /* includesGuardPages */ true); jsStackBase = (Value *)jsStack->base(); +#ifdef V4_USE_VALGRIND + VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, 2*JSStackLimit); +#endif + jsStackTop = jsStackBase; exceptionValue = jsAlloca(1); @@ -179,10 +183,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) typedArrayCtors = static_cast(jsAlloca(NTypedArrayTypes)); jsStrings = jsAlloca(NJSStrings); -#ifdef V4_USE_VALGRIND - VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, 2*JSStackLimit); -#endif - // set up stack limits jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value); diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index d7fd44e1d6..ca7efb1e79 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -82,7 +82,7 @@ struct Scope { memset(mark, 0, (engine->jsStackTop - mark)*sizeof(Value)); #endif #ifdef V4_USE_VALGRIND - VALGRIND_MAKE_MEM_UNDEFINED(mark, engine->jsStackLimit - mark); + VALGRIND_MAKE_MEM_UNDEFINED(mark, (engine->jsStackLimit - mark) * sizeof(Value)); #endif engine->jsStackTop = mark; } -- cgit v1.2.3 From 8b99e2019a860d31e49e0fbe9bcfd9981dc2b768 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 5 May 2016 14:38:16 +0300 Subject: Imports: de-duplicate some expensive calls QMetaMethod::parameterNames() contains internal loop, and we had quadratic behavior. Since operator[] was used with temp object, also we had detach()ing. QUrl::path() contains memory allocation. To avoid these issues cache results of functions. Change-Id: Ie4c3f0573a000342aff44de0dd4f744a146bb935 Reviewed-by: Simon Hausmann --- src/imports/folderlistmodel/qquickfolderlistmodel.cpp | 5 +++-- src/imports/statemachine/signaltransition.cpp | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp index 66af37c40c..1c94fddecf 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp @@ -491,10 +491,11 @@ QUrl QQuickFolderListModel::parentFolder() const return QUrl(); localFile = dir.path(); } else { - const int pos = d->currentDir.path().lastIndexOf(QLatin1Char('/')); + const QString path = d->currentDir.path(); + const int pos = path.lastIndexOf(QLatin1Char('/')); if (pos <= 0) return QUrl(); - localFile = d->currentDir.path().left(pos); + localFile = path.left(pos); } return QUrl::fromLocalFile(localFile); } diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index 47efc9ec15..508e1e3685 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -78,8 +78,9 @@ bool SignalTransition::eventTest(QEvent *event) // Set arguments as context properties int count = e->arguments().count(); QMetaMethod metaMethod = e->sender()->metaObject()->method(e->signalIndex()); + const auto parameterNames = metaMethod.parameterNames(); for (int i = 0; i < count; i++) - context.setContextProperty(metaMethod.parameterNames()[i], QVariant::fromValue(e->arguments().at(i))); + context.setContextProperty(parameterNames[i], QVariant::fromValue(e->arguments().at(i))); QQmlExpression expr(m_guard, &context, this); QVariant result = expr.evaluate(); -- cgit v1.2.3 From 72ee8417fa2d7789e8ccd09a54db8af9b0f1c366 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Thu, 19 May 2016 17:57:44 +0200 Subject: RegAllocInfo: Initialize member _currentStmt. As pointed out by Coverity (CID 22368). Change-Id: I67bdda3f747b68a0197a4cb2e2aa750aa322b6ba Reviewed-by: Simon Hausmann --- src/qml/jit/qv4regalloc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index d1d97c8f84..82ae60a8f0 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -833,6 +833,7 @@ public: , _assignedSpillSlots(assignedSpillSlots) , _intRegs(intRegs) , _fpRegs(fpRegs) + , _currentStmt(0) { _unprocessed = unprocessed; _liveAtStart.reserve(function->basicBlockCount()); -- cgit v1.2.3 From 7e84a8631cca6f81428ae4b2621e6858a8de9357 Mon Sep 17 00:00:00 2001 From: Milla Pohjanheimo Date: Tue, 31 May 2016 11:54:55 +0300 Subject: Blacklisting tst_QQuickText::fontSizeMode on OpenSUSE 42.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To get QtWayland in the CI for OpenSUSE 42.1, we need to blacklist this test. Task-number:QTBUG-53512 Change-Id: Ib150088e3843bab87f845d3502e8e2712fb8ff4b Reviewed-by: Tony Sarajärvi --- tests/auto/quick/qquicktext/BLACKLIST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/quick/qquicktext/BLACKLIST b/tests/auto/quick/qquicktext/BLACKLIST index 223d8feb67..f400af1d10 100644 --- a/tests/auto/quick/qquicktext/BLACKLIST +++ b/tests/auto/quick/qquicktext/BLACKLIST @@ -4,3 +4,5 @@ * [lineLaidOutRelayout] msvc-2015 +[fontSizeMode] +opensuse-42.1 -- cgit v1.2.3 From 2bb309af8b2e32bd34644bed363f5880c50221af Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 29 May 2016 19:21:54 +0200 Subject: Moved resolved type references over to QV4::CompiledData::CompilationUnit Change-Id: I99bb37bf4d4aa4aedd8e02a0fb4afb4a908573a6 Reviewed-by: Erik Verbruggen --- src/qml/compiler/qqmltypecompiler.cpp | 42 +++++++++--------- src/qml/compiler/qqmltypecompiler_p.h | 15 ++++--- src/qml/compiler/qv4compileddata.cpp | 64 ++++++++++++++++++++++++++++ src/qml/compiler/qv4compileddata_p.h | 30 +++++++++++++ src/qml/qml/qqmlcompileddata.cpp | 59 ------------------------- src/qml/qml/qqmlcompiler_p.h | 27 ------------ src/qml/qml/qqmlobjectcreator.cpp | 8 ++-- src/qml/qml/qqmlobjectcreator_p.h | 2 +- tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 5 +++ 9 files changed, 133 insertions(+), 119 deletions(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 19beb13e45..4a621f6d19 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -79,7 +79,7 @@ bool QQmlTypeCompiler::compile() const QHash &resolvedTypes = typeData->resolvedTypeRefs(); for (QHash::ConstIterator resolvedType = resolvedTypes.constBegin(), end = resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) { - QScopedPointer ref(new QQmlCompiledData::TypeReference); + QScopedPointer ref(new QV4::CompiledData::CompilationUnit::ResolvedTypeReference); QQmlType *qmlType = resolvedType->type; if (resolvedType->typeData) { if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) { @@ -125,12 +125,12 @@ bool QQmlTypeCompiler::compile() ref->majorVersion = resolvedType->majorVersion; ref->minorVersion = resolvedType->minorVersion; ref->doDynamicTypeCheck(); - compiledData->resolvedTypes.insert(resolvedType.key(), ref.take()); + m_resolvedTypes.insert(resolvedType.key(), ref.take()); } // Build property caches and VME meta object data - for (QHash::ConstIterator it = compiledData->resolvedTypes.constBegin(), end = compiledData->resolvedTypes.constEnd(); + for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; ++it) { QQmlCustomParser *customParser = (*it)->type ? (*it)->type->customParser() : 0; if (customParser) @@ -244,13 +244,14 @@ bool QQmlTypeCompiler::compile() compiledData->compilationUnit->propertyCaches = m_propertyCaches; compiledData->compilationUnit->importCache = importCache; compiledData->compilationUnit->dependentScripts = dependentScripts; + compiledData->compilationUnit->resolvedTypes = m_resolvedTypes; // Add to type registry of composites if (compiledData->compilationUnit->isCompositeType()) engine->registerInternalCompositeType(compiledData); else { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject); - QQmlCompiledData::TypeReference *typeRef = compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex); + auto *typeRef = m_resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); if (typeRef->component) { compiledData->metaTypeId = typeRef->component->metaTypeId; @@ -273,7 +274,7 @@ bool QQmlTypeCompiler::compile() for (quint32 i = 0; i < qmlUnit->nObjects; ++i) { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); bindingCount += obj->nBindings; - if (QQmlCompiledData::TypeReference *typeRef = compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex)) { + if (auto *typeRef = m_resolvedTypes.value(obj->inheritedTypeNameIndex)) { if (QQmlType *qmlType = typeRef->type) { if (qmlType->parserStatusCast() != -1) ++parserStatusCount; @@ -328,9 +329,9 @@ const QQmlImports *QQmlTypeCompiler::imports() const return &typeData->imports(); } -QHash *QQmlTypeCompiler::resolvedTypes() +QHash *QQmlTypeCompiler::resolvedTypes() { - return &compiledData->resolvedTypes; + return &m_resolvedTypes; } QList *QQmlTypeCompiler::qmlObjects() @@ -473,7 +474,7 @@ bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int r } if (obj->inheritedTypeNameIndex != 0) { - QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); if (typeRef->isFullyDynamicType) { @@ -494,7 +495,7 @@ bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int r baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); Q_ASSERT(baseTypeCache); } else if (instantiatingBinding && instantiatingBinding->isAttachedProperty()) { - QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(instantiatingBinding->propertyNameIndex); + auto *typeRef = resolvedTypes->value(instantiatingBinding->propertyNameIndex); Q_ASSERT(typeRef); QQmlType *qmltype = typeRef->type; if (!qmltype) { @@ -551,7 +552,7 @@ bool QQmlPropertyCacheCreator::ensureVMEMetaObject(int objectIndex) if (willCreateVMEMetaObject) return true; const QmlIR::Object *obj = qmlObjects.at(objectIndex); - QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); return createMetaObject(objectIndex, obj, baseTypeCache); @@ -876,7 +877,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio // Attached property? if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); - QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(binding->propertyNameIndex); + auto *typeRef = resolvedTypes.value(binding->propertyNameIndex); QQmlType *type = typeRef ? typeRef->type : 0; if (!type) { if (imports->resolveType(propertyName, &type, 0, 0, 0)) { @@ -949,7 +950,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio const QString &originalPropertyName = stringAt(binding->propertyNameIndex); - QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); + auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); const QQmlType *type = typeRef ? typeRef->type : 0; if (type) { COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type->module()).arg(type->majorVersion()).arg(type->minorVersion())); @@ -1153,7 +1154,7 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, int value = 0; bool ok = false; - QQmlCompiledData::TypeReference *tr = resolvedTypes->value(obj->inheritedTypeNameIndex); + auto *tr = resolvedTypes->value(obj->inheritedTypeNameIndex); if (type && tr && tr->type == type) { QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex); @@ -1330,7 +1331,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI continue; const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex); - QQmlCompiledData::TypeReference *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex); + auto *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex); Q_ASSERT(tr); if (QQmlType *targetType = tr->type) { if (targetType->metaObject() == &QQmlComponent::staticMetaObject) @@ -1370,7 +1371,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; if (!resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) { - QQmlCompiledData::TypeReference *typeRef = new QQmlCompiledData::TypeReference; + auto typeRef = new QV4::CompiledData::CompilationUnit::ResolvedTypeReference; typeRef->type = componentType; typeRef->majorVersion = componentType->majorVersion(); typeRef->minorVersion = componentType->minorVersion(); @@ -1413,7 +1414,7 @@ bool QQmlComponentAndAliasResolver::resolve() bool isExplicitComponent = false; if (obj->inheritedTypeNameIndex) { - QQmlCompiledData::TypeReference *tref = resolvedTypes->value(obj->inheritedTypeNameIndex); + auto *tref = resolvedTypes->value(obj->inheritedTypeNameIndex); Q_ASSERT(tref); if (tref->type && tref->type->metaObject() == &QQmlComponent::staticMetaObject) isExplicitComponent = true; @@ -1570,7 +1571,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases() if (property.isEmpty()) { const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); - QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex); + auto *typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex); Q_ASSERT(typeRef); if (typeRef->type) @@ -1915,7 +1916,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD if (notInRevision) { QString typeName = stringAt(obj->inheritedTypeNameIndex); - QQmlCompiledData::TypeReference *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex); + auto *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex); if (objectType && objectType->type) { COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); } else { @@ -2368,8 +2369,7 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co QQmlType *qmlType = 0; const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex); - QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex); - if (typeRef) { + if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) { QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); const QMetaObject *mo = cache->firstCppMetaObject(); while (mo && !qmlType) { @@ -2485,7 +2485,7 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject) m.idIndex = obj->id; m.type = propertyCaches.at(objectIndex).data(); - QQmlCompiledData::TypeReference *tref = resolvedTypes.value(obj->inheritedTypeNameIndex); + auto *tref = resolvedTypes.value(obj->inheritedTypeNameIndex); if (tref && tref->isFullyDynamicType) m.type = 0; diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 5abfc7c18d..121be874fe 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -96,7 +96,7 @@ public: QUrl url() const { return typeData->finalUrl(); } QQmlEnginePrivate *enginePrivate() const { return engine; } const QQmlImports *imports() const; - QHash *resolvedTypes(); + QHash *resolvedTypes(); QList *qmlObjects(); int rootObjectIndex() const; void setPropertyCaches(const QQmlPropertyCacheVector &caches); @@ -121,6 +121,7 @@ private: QmlIR::Document *document; // index is string index of type name (use obj->inheritedTypeNameIndex) QHash customParsers; + QHash m_resolvedTypes; // index in first hash is component index, vector inside contains object indices of objects with id property QVector m_componentRoots; @@ -156,7 +157,7 @@ protected: QQmlEnginePrivate *enginePrivate; const QList &qmlObjects; const QQmlImports *imports; - QHash *resolvedTypes; + QHash *resolvedTypes; QQmlPropertyCacheVector propertyCaches; }; @@ -179,7 +180,7 @@ private: const QList &qmlObjects; const QQmlImports *imports; const QHash &customParsers; - const QHash &resolvedTypes; + const QHash &resolvedTypes; const QSet &illegalNames; const QQmlPropertyCacheVector &propertyCaches; }; @@ -210,7 +211,7 @@ private: const QList &qmlObjects; const QQmlPropertyCacheVector propertyCaches; const QQmlImports *imports; - QHash *resolvedTypes; + QHash *resolvedTypes; }; class QQmlCustomParserScriptIndexer: public QQmlCompilePass @@ -277,7 +278,7 @@ protected: QHash _idToObjectIndex; QList _objectsWithAliases; - QHash *resolvedTypes; + QHash *resolvedTypes; QQmlPropertyCacheVector propertyCaches; }; @@ -318,7 +319,7 @@ private: QQmlEnginePrivate *enginePrivate; const QV4::CompiledData::Unit *qmlUnit; - const QHash &resolvedTypes; + const QHash &resolvedTypes; const QHash &customParsers; const QQmlPropertyCacheVector &propertyCaches; @@ -338,7 +339,7 @@ private: bool compileComponent(int componentRoot); bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); - const QHash &resolvedTypes; + const QHash &resolvedTypes; const QHash &customParsers; const QList &qmlObjects; const QQmlPropertyCacheVector &propertyCaches; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index dc29d1b941..2b437254c0 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -48,6 +48,9 @@ #include #include #include +#include +#include +#include #endif #include #include @@ -181,6 +184,16 @@ void CompilationUnit::unlink() importCache = nullptr; + for (auto resolvedType = resolvedTypes.begin(), end = resolvedTypes.end(); + resolvedType != end; ++resolvedType) { + if ((*resolvedType)->component) + (*resolvedType)->component->release(); + if ((*resolvedType)->typePropertyCache) + (*resolvedType)->typePropertyCache->release(); + } + qDeleteAll(resolvedTypes); + resolvedTypes.clear(); + engine = 0; free(runtimeStrings); runtimeStrings = 0; @@ -319,6 +332,57 @@ QString Binding::valueAsScriptString(const Unit *unit) const return valueAsString(unit); } +#ifndef V4_BOOTSTRAP +/*! +Returns the property cache, if one alread exists. The cache is not referenced. +*/ +QQmlPropertyCache *CompilationUnit::ResolvedTypeReference::propertyCache() const +{ + if (type) + return typePropertyCache; + else + return component->compilationUnit->rootPropertyCache(); +} + +/*! +Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. +*/ +QQmlPropertyCache *CompilationUnit::ResolvedTypeReference::createPropertyCache(QQmlEngine *engine) +{ + if (typePropertyCache) { + return typePropertyCache; + } else if (type) { + typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject()); + typePropertyCache->addref(); + return typePropertyCache; + } else { + return component->compilationUnit->rootPropertyCache(); + } +} + +template +bool qtTypeInherits(const QMetaObject *mo) { + while (mo) { + if (mo == &T::staticMetaObject) + return true; + mo = mo->superClass(); + } + return false; +} + +void CompilationUnit::ResolvedTypeReference::doDynamicTypeCheck() +{ + const QMetaObject *mo = 0; + if (typePropertyCache) + mo = typePropertyCache->firstCppMetaObject(); + else if (type) + mo = type->metaObject(); + else if (component) + mo = component->compilationUnit->rootPropertyCache()->firstCppMetaObject(); + isFullyDynamicType = qtTypeInherits(mo); +} +#endif + } } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 8beb932fc8..0e2e2306d8 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -72,6 +72,9 @@ class QQmlPropertyCache; class QQmlPropertyData; class QQmlTypeNameCache; class QQmlScriptData; +class QQmlType; +class QQmlCompiledData; +class QQmlEngine; // The vector is indexed by QV4::CompiledData::Object index and the flag // indicates whether instantiation of the object requires a VME meta-object. @@ -694,6 +697,33 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QVector dependentScripts; + struct ResolvedTypeReference + { + ResolvedTypeReference() + : type(0), typePropertyCache(0), component(0) + , majorVersion(0) + , minorVersion(0) + , isFullyDynamicType(false) + {} + + QQmlType *type; + QQmlPropertyCache *typePropertyCache; + QQmlCompiledData *component; + + int majorVersion; + int minorVersion; + // Types such as QQmlPropertyMap can add properties dynamically at run-time and + // therefore cannot have a property cache installed when instantiated. + bool isFullyDynamicType; + + QQmlPropertyCache *propertyCache() const; + QQmlPropertyCache *createPropertyCache(QQmlEngine *); + + void doDynamicTypeCheck(); + }; + // map from name index + QHash resolvedTypes; + QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); void unlink(); diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index b25792054c..fad13bb1e3 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -74,71 +74,12 @@ QQmlCompiledData::~QQmlCompiledData() QQmlEnginePrivate::get(engine)->unregisterInternalCompositeType(this); clear(); - - for (QHash::Iterator resolvedType = resolvedTypes.begin(), end = resolvedTypes.end(); - resolvedType != end; ++resolvedType) { - if ((*resolvedType)->component) - (*resolvedType)->component->release(); - if ((*resolvedType)->typePropertyCache) - (*resolvedType)->typePropertyCache->release(); - } - qDeleteAll(resolvedTypes); - resolvedTypes.clear(); } void QQmlCompiledData::clear() { } -/*! -Returns the property cache, if one alread exists. The cache is not referenced. -*/ -QQmlPropertyCache *QQmlCompiledData::TypeReference::propertyCache() const -{ - if (type) - return typePropertyCache; - else - return component->compilationUnit->rootPropertyCache(); -} - -/*! -Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. -*/ -QQmlPropertyCache *QQmlCompiledData::TypeReference::createPropertyCache(QQmlEngine *engine) -{ - if (typePropertyCache) { - return typePropertyCache; - } else if (type) { - typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject()); - typePropertyCache->addref(); - return typePropertyCache; - } else { - return component->compilationUnit->rootPropertyCache(); - } -} - -template -bool qtTypeInherits(const QMetaObject *mo) { - while (mo) { - if (mo == &T::staticMetaObject) - return true; - mo = mo->superClass(); - } - return false; -} - -void QQmlCompiledData::TypeReference::doDynamicTypeCheck() -{ - const QMetaObject *mo = 0; - if (typePropertyCache) - mo = typePropertyCache->firstCppMetaObject(); - else if (type) - mo = type->metaObject(); - else if (component) - mo = component->compilationUnit->rootPropertyCache()->firstCppMetaObject(); - isFullyDynamicType = qtTypeInherits(mo); -} - void QQmlCompiledData::initialize(QQmlEngine *engine) { Q_ASSERT(!hasEngine()); diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index 2c9e04d5e0..39fa84df3d 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -93,33 +93,6 @@ public: int listMetaTypeId; bool isRegisteredWithEngine; - struct TypeReference - { - TypeReference() - : type(0), typePropertyCache(0), component(0) - , majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} - - QQmlType *type; - QQmlPropertyCache *typePropertyCache; - QQmlCompiledData *component; - - int majorVersion; - int minorVersion; - // Types such as QQmlPropertyMap can add properties dynamically at run-time and - // therefore cannot have a property cache installed when instantiated. - bool isFullyDynamicType; - - QQmlPropertyCache *propertyCache() const; - QQmlPropertyCache *createPropertyCache(QQmlEngine *); - - void doDynamicTypeCheck(); - }; - // map from name index - QHash resolvedTypes; - QQmlRefPointer compilationUnit; bool isInitialized() const { return hasEngine(); } diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index dcd35bccad..e2e1c6878e 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -72,7 +72,7 @@ struct ActiveOCRestorer QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlContextData *creationContext, void *activeVMEDataForRootContext) : phase(Startup) , compiledData(compiledData) - , resolvedTypes(compiledData->resolvedTypes) + , resolvedTypes(compiledData->compilationUnit->resolvedTypes) , propertyCaches(compiledData->compilationUnit->propertyCaches) , activeVMEDataForRootContext(activeVMEDataForRootContext) { @@ -96,7 +96,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlObjectCreatorSharedState *inheritedSharedState) : phase(Startup) , compiledData(compiledData) - , resolvedTypes(compiledData->resolvedTypes) + , resolvedTypes(compiledData->compilationUnit->resolvedTypes) , propertyCaches(compiledData->compilationUnit->propertyCaches) , activeVMEDataForRootContext(0) { @@ -708,7 +708,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); - QQmlCompiledData::TypeReference *tr = resolvedTypes.value(binding->propertyNameIndex); + QV4::CompiledData::CompilationUnit::ResolvedTypeReference *tr = resolvedTypes.value(binding->propertyNameIndex); Q_ASSERT(tr); QQmlType *attachedType = tr->type; if (!attachedType) { @@ -1042,7 +1042,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo instance = component; ddata = QQmlData::get(instance, /*create*/true); } else { - QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); + QV4::CompiledData::CompilationUnit::ResolvedTypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); installPropertyCache = !typeRef->isFullyDynamicType; QQmlType *type = typeRef->type; diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index f51cf3a2ca..d18e6696e4 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -140,7 +140,7 @@ private: const QV4::CompiledData::Unit *qmlUnit; QQmlGuardedContextData parentContext; QQmlContextData *context; - const QHash &resolvedTypes; + const QHash &resolvedTypes; const QQmlPropertyCacheVector &propertyCaches; QExplicitlySharedDataPointer sharedState; bool topLevelCreator; diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 3af1cf46b3..74e54b64e9 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -317,6 +317,11 @@ public: QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); QCoreApplication::processEvents(); + // There might be JS function objects around that hold a last ref to the compilation unit that's + // keeping the type compilation data (CompilationUnit) around. Let's collect them as well so that + // trim works well. + engine->collectGarbage(); + engine->trimComponentCache(); } -- cgit v1.2.3 From 06467a192b7b107987b2d2d030a32a36ffbece5f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 29 May 2016 18:58:07 +0200 Subject: Moved meta-type id members to QV4::CompiledData::CompilationUnit Change-Id: I808d0a36094e873b69cf24a5b0113e741ff2a25d Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder.cpp | 2 +- src/qml/compiler/qqmltypecompiler.cpp | 20 ++++++++++---------- src/qml/compiler/qv4compileddata.cpp | 10 ++++++++++ src/qml/compiler/qv4compileddata_p.h | 5 +++++ src/qml/qml/qqmlcompileddata.cpp | 5 +---- src/qml/qml/qqmlcompiler_p.h | 4 ---- src/qml/qml/qqmlengine.cpp | 18 +++++++++--------- src/qml/qml/qqmlengine_p.h | 2 +- 8 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index c9a4e21ddd..43410785b1 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1698,7 +1698,7 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, if (tdata->isComplete()) { auto newResolver = resolver->owner->New(); newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compiledData()->metaTypeId)); + initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compiledData()->compilationUnit->metaTypeId)); newResolver->flags |= AllPropertiesAreFinal; return newResolver->resolveMember(qmlEngine, newResolver, member); } diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 4a621f6d19..0ad683dc84 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -254,11 +254,11 @@ bool QQmlTypeCompiler::compile() auto *typeRef = m_resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); if (typeRef->component) { - compiledData->metaTypeId = typeRef->component->metaTypeId; - compiledData->listMetaTypeId = typeRef->component->listMetaTypeId; + compiledData->compilationUnit->metaTypeId = typeRef->component->compilationUnit->metaTypeId; + compiledData->compilationUnit->listMetaTypeId = typeRef->component->compilationUnit->listMetaTypeId; } else { - compiledData->metaTypeId = typeRef->type->typeId(); - compiledData->listMetaTypeId = typeRef->type->qListTypeId(); + compiledData->compilationUnit->metaTypeId = typeRef->type->typeId(); + compiledData->compilationUnit->listMetaTypeId = typeRef->type->qListTypeId(); } } @@ -507,7 +507,7 @@ bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int r Q_ASSERT(tdata->isComplete()); QQmlCompiledData *data = tdata->compiledData(); - qmltype = QQmlMetaType::qmlType(data->metaTypeId); + qmltype = QQmlMetaType::qmlType(data->compilationUnit->metaTypeId); tdata->release(); } @@ -717,7 +717,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob QQmlCompiledData *data = tdata->compiledData(); - paramTypes[i + 1] = data->metaTypeId; + paramTypes[i + 1] = data->compilationUnit->metaTypeId; tdata->release(); } else { @@ -801,9 +801,9 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob QQmlCompiledData *data = tdata->compiledData(); if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = data->metaTypeId; + propertyType = data->compilationUnit->metaTypeId; } else { - propertyType = data->listMetaTypeId; + propertyType = data->compilationUnit->listMetaTypeId; } tdata->release(); @@ -887,7 +887,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio Q_ASSERT(tdata->isComplete()); QQmlCompiledData *data = tdata->compiledData(); - type = QQmlMetaType::qmlType(data->metaTypeId); + type = QQmlMetaType::qmlType(data->compilationUnit->metaTypeId); tdata->release(); } @@ -1577,7 +1577,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases() if (typeRef->type) type = typeRef->type->typeId(); else - type = typeRef->component->metaTypeId; + type = typeRef->component->compilationUnit->metaTypeId; alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; propertyFlags |= QQmlPropertyData::IsQObjectDerived; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 2b437254c0..b2cd7b5953 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -74,6 +74,9 @@ CompilationUnit::CompilationUnit() , totalBindingsCount(0) , totalParserStatusCount(0) , totalObjectCount(0) + , metaTypeId(-1) + , listMetaTypeId(-1) + , isRegisteredWithEngine(false) {} CompilationUnit::~CompilationUnit() @@ -173,6 +176,13 @@ void CompilationUnit::unlink() if (engine) engine->compilationUnits.erase(engine->compilationUnits.find(this)); + if (isRegisteredWithEngine) { + Q_ASSERT(data && quint32(propertyCaches.count()) > data->indexOfRootObject && !propertyCaches.at(data->indexOfRootObject).isNull()); + QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(propertyCaches.at(data->indexOfRootObject)->engine); + qmlEngine->unregisterInternalCompositeType(this); + isRegisteredWithEngine = false; + } + for (int ii = 0; ii < propertyCaches.count(); ++ii) if (propertyCaches.at(ii).data()) propertyCaches.at(ii)->release(); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 0e2e2306d8..cb4941d147 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -724,6 +724,11 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount // map from name index QHash resolvedTypes; + int metaTypeId; + int listMetaTypeId; + bool isRegisteredWithEngine; + + QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); void unlink(); diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index fad13bb1e3..bd867a9b90 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine) -: engine(engine), metaTypeId(-1), listMetaTypeId(-1), isRegisteredWithEngine(false) +: engine(engine) { Q_ASSERT(engine); } @@ -70,9 +70,6 @@ void QQmlCompiledData::destroy() QQmlCompiledData::~QQmlCompiledData() { - if (isRegisteredWithEngine) - QQmlEnginePrivate::get(engine)->unregisterInternalCompositeType(this); - clear(); } diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index 39fa84df3d..4f27e3ef44 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -89,10 +89,6 @@ public: QQmlEngine *engine; - int metaTypeId; - int listMetaTypeId; - bool isRegisteredWithEngine; - QQmlRefPointer compilationUnit; bool isInitialized() const { return hasEngine(); } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index fd1795499e..7749c4f563 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -641,12 +641,12 @@ QQmlEnginePrivate::~QQmlEnginePrivate() for (TypePropertyCacheIt iter = typePropertyCache.cbegin(), end = typePropertyCache.cend(); iter != end; ++iter) (*iter)->release(); for (CompositeTypesIt iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) { - iter.value()->isRegisteredWithEngine = false; + iter.value()->compilationUnit->isRegisteredWithEngine = false; // since unregisterInternalCompositeType() will not be called in this // case, we have to clean up the type registration manually - QMetaType::unregisterType(iter.value()->metaTypeId); - QMetaType::unregisterType(iter.value()->listMetaTypeId); + QMetaType::unregisterType(iter.value()->compilationUnit->metaTypeId); + QMetaType::unregisterType(iter.value()->compilationUnit->listMetaTypeId); } delete profiler; } @@ -2292,9 +2292,9 @@ void QQmlEnginePrivate::registerInternalCompositeType(QQmlCompiledData *data) static_cast >(QtPrivate::QMetaTypeTypeFlags >::Flags), static_cast(0)); - data->metaTypeId = ptr_type; - data->listMetaTypeId = lst_type; - data->isRegisteredWithEngine = true; + data->compilationUnit->metaTypeId = ptr_type; + data->compilationUnit->listMetaTypeId = lst_type; + data->compilationUnit->isRegisteredWithEngine = true; Locker locker(this); m_qmlLists.insert(lst_type, ptr_type); @@ -2303,10 +2303,10 @@ void QQmlEnginePrivate::registerInternalCompositeType(QQmlCompiledData *data) m_compositeTypes.insert(ptr_type, data); } -void QQmlEnginePrivate::unregisterInternalCompositeType(QQmlCompiledData *data) +void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) { - int ptr_type = data->metaTypeId; - int lst_type = data->listMetaTypeId; + int ptr_type = compilationUnit->metaTypeId; + int lst_type = compilationUnit->listMetaTypeId; Locker locker(this); m_qmlLists.remove(lst_type); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 18bee387dd..13a4280203 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -217,7 +217,7 @@ public: QQmlPropertyCache *propertyCacheForType(int); QQmlPropertyCache *rawPropertyCacheForType(int); void registerInternalCompositeType(QQmlCompiledData *); - void unregisterInternalCompositeType(QQmlCompiledData *); + void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; -- cgit v1.2.3 From 12462cf55a8edeace4570498ec4b62584c56bfb0 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 15 Apr 2016 11:44:16 +0200 Subject: V4: Replace foreach loops with range-based for loops. Change-Id: I8b3b33af1f01f1f2559e82e210375935b5fee00e Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4codegen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index d08d2aafa2..a5d4c6f122 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1981,7 +1981,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, } } else { if (!_env->isStrict) { - foreach (const QString &inheritedLocal, inheritedLocals) { + for (const QString &inheritedLocal : qAsConst(inheritedLocals)) { function->LOCAL(inheritedLocal); unsigned tempIndex = entryBlock->newTemp(); Environment::Member member = { Environment::UndefinedMember, @@ -2023,7 +2023,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _function->RECEIVE(it->name.toString()); } - foreach (const Environment::Member &member, _env->members) { + for (const Environment::Member &member : qAsConst(_env->members)) { if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body ? member.function->body->elements : 0); @@ -2857,7 +2857,7 @@ QList Codegen::qmlErrors() const qmlErrors.reserve(_errors.size()); QUrl url(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName)); - foreach (const QQmlJS::DiagnosticMessage &msg, _errors) { + for (const QQmlJS::DiagnosticMessage &msg: qAsConst(_errors)) { QQmlError e; e.setUrl(url); e.setLine(msg.loc.startLine); -- cgit v1.2.3 From 89aa39faf2f44120eb8e22a3f1342e9287ce7305 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 1 Jun 2016 10:05:19 +0200 Subject: Add \since 5.8 to Binding::delayed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It wasn't added in 1148d3ace. Change-Id: I2d7495c77bb924500b302f60fb6ab9bf71578967 Reviewed-by: Topi Reiniö --- src/qml/types/qqmlbind.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index 3102da3f20..a545fe57ca 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -289,6 +289,7 @@ void QQmlBind::setValue(const QVariant &v) /*! \qmlproperty bool QtQml::Binding::delayed + \since 5.8 This property holds whether the binding should be delayed. -- cgit v1.2.3 From ddb60df2634ae97ae85ea5b6021283be098efed4 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Fri, 20 May 2016 14:57:25 +0200 Subject: QQuickTextNodeEngine: early out if no document layout. As Coverity (CID 22302) points out, qobject_cast<> can return NULL; if we don't get a document layout, then we have no frame decorations to add to it. Change-Id: I2cd428456b3a3b24a28a5dd18ce948a8e95d21cb Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/items/qquicktextnodeengine.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index 18cb1d0083..417e2483df 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -636,9 +636,12 @@ void QQuickTextNodeEngine::addBorder(const QRectF &rect, qreal border, void QQuickTextNodeEngine::addFrameDecorations(QTextDocument *document, QTextFrame *frame) { QTextDocumentLayout *documentLayout = qobject_cast(document->documentLayout()); - QTextFrameFormat frameFormat = frame->format().toFrameFormat(); + if (Q_UNLIKELY(!documentLayout)) + return; + QTextFrameFormat frameFormat = frame->format().toFrameFormat(); QTextTable *table = qobject_cast(frame); + QRectF boundingRect = table == 0 ? documentLayout->frameBoundingRect(frame) : documentLayout->tableBoundingRect(table); -- cgit v1.2.3 From 795a1bf1171f2bd8288ced2a2c46590eff32073d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 1 Jun 2016 08:25:19 +0200 Subject: Remove "external" usages of QQmlCompiledData::engine This removes the last "user" of QQmlCompiledData apart from its circular usage. Change-Id: I5383c9e7bba03617a1145cd43f52051848962013 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlcomponent.cpp | 1 + src/qml/qml/qqmlincubator.cpp | 12 +++++------- src/qml/qml/qqmlincubator_p.h | 1 + src/qml/types/qqmldelegatemodel.cpp | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index eb4f590222..16c41a8950 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1051,6 +1051,7 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, p->compiledData = d->cc; p->compiledData->addref(); + p->enginePriv = enginePriv; p->creator.reset(new QQmlObjectCreator(contextData, d->cc, d->creationContext, p.data())); p->subComponentToCreate = d->start; diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index eba93bfb1a..d0991f51fe 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -132,7 +132,7 @@ QQmlIncubationController *QQmlEngine::incubationController() const QQmlIncubatorPrivate::QQmlIncubatorPrivate(QQmlIncubator *q, QQmlIncubator::IncubationMode m) : q(q), status(QQmlIncubator::Null), mode(m), isAsynchronous(false), progress(Execute), - result(0), compiledData(0), waitingOnMe(0) + result(0), enginePriv(0), compiledData(0), waitingOnMe(0) { } @@ -146,7 +146,6 @@ void QQmlIncubatorPrivate::clear() if (next.isInList()) { next.remove(); Q_ASSERT(compiledData); - QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(compiledData->engine); compiledData->release(); compiledData = 0; enginePriv->incubatorCount--; @@ -157,6 +156,7 @@ void QQmlIncubatorPrivate::clear() compiledData->release(); compiledData = 0; } + enginePriv = 0; if (!rootContext.isNull()) { rootContext->activeVMEData = 0; rootContext = 0; @@ -286,9 +286,8 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) QExplicitlySharedDataPointer protectThis(this); QRecursionWatcher watcher(this); - - QQmlEngine *engine = compiledData->engine; - QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); + // get a copy of the engine pointer as it might get reset; + QQmlEnginePrivate *enginePriv = this->enginePriv; if (!vmeGuard.isOK()) { QQmlError error; @@ -563,10 +562,9 @@ void QQmlIncubator::clear() if (s == Null) return; - QQmlEnginePrivate *enginePriv = 0; + QQmlEnginePrivate *enginePriv = d->enginePriv; if (s == Loading) { Q_ASSERT(d->compiledData); - enginePriv = QQmlEnginePrivate::get(d->compiledData->engine); if (d->result) d->result->deleteLater(); d->result = 0; } diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index a12ff9c5e2..974a353f77 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -85,6 +85,7 @@ public: QPointer result; QQmlGuardedContextData rootContext; + QQmlEnginePrivate *enginePriv; QQmlCompiledData *compiledData; QScopedPointer creator; int subComponentToCreate; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index aee9fcd0a2..05408b525a 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -1921,6 +1921,7 @@ void QQmlDelegateModelItem::incubateObject( incubatorPriv->compiledData = componentPriv->cc; incubatorPriv->compiledData->addref(); + incubatorPriv->enginePriv = enginePriv; incubatorPriv->creator.reset(new QQmlObjectCreator(context, componentPriv->cc, componentPriv->creationContext)); incubatorPriv->subComponentToCreate = componentPriv->start; -- cgit v1.2.3 From 6e86846c9a1438a2ed359292ffa0d4ae70593cd2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 1 Jun 2016 10:22:18 +0200 Subject: Remove some dead code Change-Id: I1f3baa11e7c7860d6052182a1056f5b13d4ce593 Reviewed-by: Robin Burchell Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index b2cd7b5953..7a2404f40c 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -159,12 +159,6 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) linkBackendToEngine(engine); -#if 0 - runtimeFunctionsSortedByAddress.resize(runtimeFunctions.size()); - memcpy(runtimeFunctionsSortedByAddress.data(), runtimeFunctions.data(), runtimeFunctions.size() * sizeof(QV4::Function*)); - std::sort(runtimeFunctionsSortedByAddress.begin(), runtimeFunctionsSortedByAddress.end(), functionSortHelper); -#endif - if (data->indexOfRootFunction != -1) return runtimeFunctions[data->indexOfRootFunction]; else -- cgit v1.2.3 From 1823c1c6d75261cf0b3f247e07ac3d0c7b31f1c0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 1 Jun 2016 10:32:20 +0200 Subject: Remove inheritance of QQmlCompiledData from QQmlCleanUp The clear() re-implementation from QQmlCleanUp was empty, so this added no functionality. Commit 19c6f620dd35916466c36234231d798f79732ab0 removed the last usage of it. Change-Id: I499a6daeb1f74cc8bad1cacc5c367fde1e6eee75 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlcompileddata.cpp | 16 +--------------- src/qml/qml/qqmlcompiler_p.h | 6 +----- src/qml/qml/qqmlobjectcreator.cpp | 4 ++-- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index bd867a9b90..5344c738d8 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -62,7 +62,7 @@ QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine) void QQmlCompiledData::destroy() { - if (engine && hasEngine()) + if (engine && compilationUnit && compilationUnit->engine) QQmlEnginePrivate::deleteInEngineThread(engine, this); else delete this; @@ -70,20 +70,6 @@ void QQmlCompiledData::destroy() QQmlCompiledData::~QQmlCompiledData() { - clear(); -} - -void QQmlCompiledData::clear() -{ -} - -void QQmlCompiledData::initialize(QQmlEngine *engine) -{ - Q_ASSERT(!hasEngine()); - QQmlCleanup::addToEngine(engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - if (compilationUnit && !compilationUnit->engine) - compilationUnit->linkToEngine(v4); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index 4f27e3ef44..35beba1562 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -81,7 +81,7 @@ class QQmlContext; class QQmlContextData; // ### Merge with QV4::CompiledData::CompilationUnit -class Q_AUTOTEST_EXPORT QQmlCompiledData : public QQmlRefCount, public QQmlCleanup +class Q_AUTOTEST_EXPORT QQmlCompiledData : public QQmlRefCount { public: QQmlCompiledData(QQmlEngine *engine); @@ -91,12 +91,8 @@ public: QQmlRefPointer compilationUnit; - bool isInitialized() const { return hasEngine(); } - void initialize(QQmlEngine *); - protected: virtual void destroy(); // From QQmlRefCount - virtual void clear(); // From QQmlCleanup private: QQmlCompiledData(const QQmlCompiledData &other); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index e2e1c6878e..546bc1d81e 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -112,8 +112,8 @@ void QQmlObjectCreator::init(QQmlContextData *providedParentContext) engine = parentContext->engine; v4 = QV8Engine::getV4(engine); - if (!compiledData->isInitialized()) - compiledData->initialize(engine); + if (compiledData->compilationUnit && !compiledData->compilationUnit->engine) + compiledData->compilationUnit->linkToEngine(v4); qmlUnit = compiledData->compilationUnit->data; context = 0; -- cgit v1.2.3 From 3b14f29b8e5b889c7682eaca3e750493a05bcdb8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 1 Jun 2016 10:55:45 +0200 Subject: Ensure that CompilationUnit instances are deleted in the engine thread When using CompilationUnit with a QML engine, ensure that they are deleted in the same thread as the QML engine. Only the QML engine has a secondary thread (not a plain QJSEngine) and there it may happen that the last refcount drops within the loader thread. For example when the trimCache() is called within the loader. The destruction of the CompilationUnit however is not safe to perform in a secondary thread. Change-Id: Ia0105a8885ec97b0b2159e32e637adbd4e99f016 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 11 +++++++++++ src/qml/compiler/qv4compileddata_p.h | 1 + src/qml/qml/qqmlcompileddata.cpp | 8 -------- src/qml/qml/qqmlcompiler_p.h | 3 --- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 7a2404f40c..d4ed899d3e 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -222,6 +222,17 @@ void CompilationUnit::markObjects(QV4::ExecutionEngine *e) } } +void CompilationUnit::destroy() +{ + QQmlEngine *qmlEngine = 0; + if (engine) + qmlEngine = engine->qmlEngine(); + if (qmlEngine) + QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); + else + delete this; +} + IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex) { auto it = namedObjectsPerComponentCache.find(componentObjectIndex); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index cb4941d147..117bad0c8b 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -734,6 +734,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount void markObjects(QV4::ExecutionEngine *e); + void destroy() Q_DECL_OVERRIDE; protected: virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; #endif // V4_BOOTSTRAP diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index 5344c738d8..1a9919c1be 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -60,14 +60,6 @@ QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine) Q_ASSERT(engine); } -void QQmlCompiledData::destroy() -{ - if (engine && compilationUnit && compilationUnit->engine) - QQmlEnginePrivate::deleteInEngineThread(engine, this); - else - delete this; -} - QQmlCompiledData::~QQmlCompiledData() { } diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index 35beba1562..2846ae4084 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -91,9 +91,6 @@ public: QQmlRefPointer compilationUnit; -protected: - virtual void destroy(); // From QQmlRefCount - private: QQmlCompiledData(const QQmlCompiledData &other); QQmlCompiledData &operator=(const QQmlCompiledData &other); -- cgit v1.2.3 From 1f3f6d3e7d81dc11658a46003e86e921edb35bdf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 1 Jun 2016 12:22:11 +0200 Subject: Remove QQmlCompiledData in favor of QV4::CompiledData::CompilationUnit QQmlCompiledData used to contain the binary data for instantiating QML types in the QML VME. Nowadays the QML type compiler as well as the JavaScript compiler create a QV4::CompiledData::CompilationUnit. Change-Id: I155f62a5ecfb55a3fe230520231b6d8fd5b28ac9 Reviewed-by: Robin Burchell --- src/imports/statemachine/signaltransition.h | 1 - src/qml/compiler/qqmlirbuilder.cpp | 3 +- src/qml/compiler/qqmltypecompiler.cpp | 103 +++++++++++---------- src/qml/compiler/qqmltypecompiler_p.h | 8 +- src/qml/compiler/qv4compileddata.cpp | 13 ++- src/qml/compiler/qv4compileddata_p.h | 5 +- src/qml/debugger/qqmlprofiler_p.h | 7 +- src/qml/qml/qml.pri | 2 - src/qml/qml/qqmlbinding.cpp | 1 - src/qml/qml/qqmlboundsignal.cpp | 1 - src/qml/qml/qqmlcompileddata.cpp | 67 -------------- src/qml/qml/qqmlcompiler_p.h | 101 -------------------- src/qml/qml/qqmlcomponent.cpp | 38 ++++---- src/qml/qml/qqmlcomponent.h | 9 +- src/qml/qml/qqmlcomponent_p.h | 5 +- src/qml/qml/qqmlcustomparser.cpp | 1 - src/qml/qml/qqmlcustomparser_p.h | 1 - src/qml/qml/qqmldata_p.h | 12 ++- src/qml/qml/qqmlengine.cpp | 50 +++++----- src/qml/qml/qqmlengine_p.h | 4 +- src/qml/qml/qqmlexpression.cpp | 2 +- src/qml/qml/qqmlincubator.cpp | 27 +++--- src/qml/qml/qqmlincubator_p.h | 3 +- src/qml/qml/qqmlmetatype.cpp | 5 +- src/qml/qml/qqmlobjectcreator.cpp | 90 +++++++++--------- src/qml/qml/qqmlobjectcreator_p.h | 7 +- src/qml/qml/qqmlproperty.cpp | 1 - src/qml/qml/qqmltypeloader.cpp | 14 ++- src/qml/qml/qqmltypeloader_p.h | 5 +- src/qml/qml/qqmlvme.cpp | 1 - src/qml/qml/qqmlvme_p.h | 4 +- src/qml/qml/qqmlvmemetaobject_p.h | 3 +- src/qml/types/qqmlconnections.cpp | 1 - src/qml/types/qqmldelegatemodel.cpp | 7 +- src/qml/types/qqmllistmodel.cpp | 1 - src/quick/util/qquickpropertychanges.cpp | 1 - tests/auto/qml/qqmllanguage/testtypes.cpp | 2 - tests/auto/qml/qqmllanguage/testtypes.h | 1 - tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 2 +- .../qml/qqmltranslation/tst_qqmltranslation.cpp | 13 ++- .../auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp | 3 +- 41 files changed, 217 insertions(+), 408 deletions(-) delete mode 100644 src/qml/qml/qqmlcompileddata.cpp delete mode 100644 src/qml/qml/qqmlcompiler_p.h diff --git a/src/imports/statemachine/signaltransition.h b/src/imports/statemachine/signaltransition.h index 4a246e165f..c6512e2b19 100644 --- a/src/imports/statemachine/signaltransition.h +++ b/src/imports/statemachine/signaltransition.h @@ -48,7 +48,6 @@ #include #include #include -#include QT_BEGIN_NAMESPACE diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 43410785b1..186cd5aac9 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #endif #ifdef CONST @@ -1698,7 +1697,7 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, if (tdata->isComplete()) { auto newResolver = resolver->owner->New(); newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compiledData()->compilationUnit->metaTypeId)); + initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compilationUnit()->metaTypeId)); newResolver->flags |= AllPropertiesAreFinal; return newResolver->resolveMember(qmlEngine, newResolver, member); } diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 0ad683dc84..cfb5df5a60 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -55,15 +55,14 @@ QT_BEGIN_NAMESPACE -QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QmlIR::Document *parsedQML) +QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *parsedQML) : engine(engine) - , compiledData(compiledData) , typeData(typeData) , document(parsedQML) { } -bool QQmlTypeCompiler::compile() +QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() { importCache = new QQmlTypeNameCache; @@ -89,10 +88,10 @@ bool QQmlTypeCompiler::compile() error.setColumn(resolvedType->location.column); error.setLine(resolvedType->location.line); recordError(error); - return false; + return nullptr; } - ref->component = resolvedType->typeData->compiledData(); - ref->component->addref(); + ref->compilationUnit = resolvedType->typeData->compilationUnit(); + ref->compilationUnit->addref(); } else if (qmlType) { ref->type = qmlType; Q_ASSERT(ref->type); @@ -106,7 +105,7 @@ bool QQmlTypeCompiler::compile() error.setColumn(resolvedType->location.column); error.setLine(resolvedType->location.line); recordError(error); - return false; + return nullptr; } if (ref->type->containsRevisionedAttributes()) { @@ -117,7 +116,7 @@ bool QQmlTypeCompiler::compile() cacheError.setColumn(resolvedType->location.column); cacheError.setLine(resolvedType->location.line); recordError(cacheError); - return false; + return nullptr; } ref->typePropertyCache->addref(); } @@ -140,7 +139,7 @@ bool QQmlTypeCompiler::compile() { QQmlPropertyCacheCreator propertyCacheBuilder(this); if (!propertyCacheBuilder.buildMetaObjects()) - return false; + return nullptr; } { @@ -151,13 +150,13 @@ bool QQmlTypeCompiler::compile() { SignalHandlerConverter converter(this); if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations()) - return false; + return nullptr; } { QQmlEnumTypeResolver enumResolver(this); if (!enumResolver.resolveEnumBindings()) - return false; + return nullptr; } { @@ -198,13 +197,13 @@ bool QQmlTypeCompiler::compile() // Scan for components, determine their scopes and resolve aliases within the scope. QQmlComponentAndAliasResolver resolver(this); if (!resolver.resolve()) - return false; + return nullptr; } { QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this); if (!deferredAndCustomParserBindingScanner.scanObject()) - return false; + return nullptr; } // Compile JS binding expressions and signal handlers @@ -219,7 +218,7 @@ bool QQmlTypeCompiler::compile() QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, importCache, &document->jsGenerator.stringTable); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) - return false; + return nullptr; QQmlJavaScriptBindingExpressionSimplificationPass pass(this); pass.reduceTranslationBindings(); @@ -240,32 +239,33 @@ bool QQmlTypeCompiler::compile() // The js unit owns the data and will free the qml unit. document->javaScriptCompilationUnit->data = qmlUnit; - compiledData->compilationUnit = document->javaScriptCompilationUnit; - compiledData->compilationUnit->propertyCaches = m_propertyCaches; - compiledData->compilationUnit->importCache = importCache; - compiledData->compilationUnit->dependentScripts = dependentScripts; - compiledData->compilationUnit->resolvedTypes = m_resolvedTypes; + QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit; + compilationUnit = document->javaScriptCompilationUnit; + compilationUnit->propertyCaches = m_propertyCaches; + compilationUnit->importCache = importCache; + compilationUnit->dependentScripts = dependentScripts; + compilationUnit->resolvedTypes = m_resolvedTypes; // Add to type registry of composites - if (compiledData->compilationUnit->isCompositeType()) - engine->registerInternalCompositeType(compiledData); + if (compilationUnit->isCompositeType()) + engine->registerInternalCompositeType(compilationUnit); else { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject); auto *typeRef = m_resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); - if (typeRef->component) { - compiledData->compilationUnit->metaTypeId = typeRef->component->compilationUnit->metaTypeId; - compiledData->compilationUnit->listMetaTypeId = typeRef->component->compilationUnit->listMetaTypeId; + if (typeRef->compilationUnit) { + compilationUnit->metaTypeId = typeRef->compilationUnit->metaTypeId; + compilationUnit->listMetaTypeId = typeRef->compilationUnit->listMetaTypeId; } else { - compiledData->compilationUnit->metaTypeId = typeRef->type->typeId(); - compiledData->compilationUnit->listMetaTypeId = typeRef->type->qListTypeId(); + compilationUnit->metaTypeId = typeRef->type->typeId(); + compilationUnit->listMetaTypeId = typeRef->type->qListTypeId(); } } // Sanity check property bindings QQmlPropertyValidator validator(this); if (!validator.validate()) - return false; + return nullptr; // Collect some data for instantiation later. int bindingCount = 0; @@ -280,21 +280,24 @@ bool QQmlTypeCompiler::compile() ++parserStatusCount; } ++objectCount; - if (typeRef->component) { - bindingCount += typeRef->component->compilationUnit->totalBindingsCount; - parserStatusCount += typeRef->component->compilationUnit->totalParserStatusCount; - objectCount += typeRef->component->compilationUnit->totalObjectCount; + if (typeRef->compilationUnit) { + bindingCount += typeRef->compilationUnit->totalBindingsCount; + parserStatusCount += typeRef->compilationUnit->totalParserStatusCount; + objectCount += typeRef->compilationUnit->totalObjectCount; } } } - compiledData->compilationUnit->totalBindingsCount = bindingCount; - compiledData->compilationUnit->totalParserStatusCount = parserStatusCount; - compiledData->compilationUnit->totalObjectCount = objectCount; + compilationUnit->totalBindingsCount = bindingCount; + compilationUnit->totalParserStatusCount = parserStatusCount; + compilationUnit->totalObjectCount = objectCount; - Q_ASSERT(compiledData->compilationUnit->propertyCaches.count() == static_cast(compiledData->compilationUnit->data->nObjects)); + Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast(compilationUnit->data->nObjects)); - return errors.isEmpty(); + if (errors.isEmpty()) + return compilationUnit; + else + return nullptr; } void QQmlTypeCompiler::recordError(const QQmlError &error) @@ -321,7 +324,7 @@ QV4::IR::Module *QQmlTypeCompiler::jsIRModule() const const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const { - return compiledData->compilationUnit->data; + return document->javaScriptCompilationUnit->data; } const QQmlImports *QQmlTypeCompiler::imports() const @@ -372,7 +375,7 @@ const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const void QQmlTypeCompiler::setBindingPropertyDataPerObject(const QVector &propertyData) { - compiledData->compilationUnit->bindingPropertyDataPerObject = propertyData; + document->javaScriptCompilationUnit->bindingPropertyDataPerObject = propertyData; } QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const @@ -506,8 +509,8 @@ bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int r Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); - QQmlCompiledData *data = tdata->compiledData(); - qmltype = QQmlMetaType::qmlType(data->compilationUnit->metaTypeId); + auto compilationUnit = tdata->compilationUnit(); + qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); tdata->release(); } @@ -715,9 +718,9 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); - QQmlCompiledData *data = tdata->compiledData(); + auto compilationUnit = tdata->compilationUnit(); - paramTypes[i + 1] = data->compilationUnit->metaTypeId; + paramTypes[i + 1] = compilationUnit->metaTypeId; tdata->release(); } else { @@ -798,12 +801,12 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); - QQmlCompiledData *data = tdata->compiledData(); + auto compilationUnit = tdata->compilationUnit(); if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = data->compilationUnit->metaTypeId; + propertyType = compilationUnit->metaTypeId; } else { - propertyType = data->compilationUnit->listMetaTypeId; + propertyType = compilationUnit->listMetaTypeId; } tdata->release(); @@ -886,8 +889,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); - QQmlCompiledData *data = tdata->compiledData(); - type = QQmlMetaType::qmlType(data->compilationUnit->metaTypeId); + auto compilationUnit = tdata->compilationUnit(); + type = QQmlMetaType::qmlType(compilationUnit->metaTypeId); tdata->release(); } @@ -1336,8 +1339,8 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI if (QQmlType *targetType = tr->type) { if (targetType->metaObject() == &QQmlComponent::staticMetaObject) continue; - } else if (tr->component) { - if (tr->component->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject) + } else if (tr->compilationUnit) { + if (tr->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject) continue; } @@ -1577,7 +1580,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases() if (typeRef->type) type = typeRef->type->typeId(); else - type = typeRef->component->compilationUnit->metaTypeId; + type = typeRef->compilationUnit->metaTypeId; alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; propertyFlags |= QQmlPropertyData::IsQObjectDerived; diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 121be874fe..b55106ac7b 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -53,13 +53,12 @@ #include #include #include -#include +#include #include QT_BEGIN_NAMESPACE class QQmlEnginePrivate; -class QQmlCompiledData; class QQmlError; class QQmlTypeData; class QQmlImports; @@ -79,9 +78,9 @@ struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QmlIR::Document *document); + QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document); - bool compile(); + QV4::CompiledData::CompilationUnit *compile(); QList compilationErrors() const { return errors; } void recordError(const QQmlError &error); @@ -115,7 +114,6 @@ public: private: QList errors; QQmlEnginePrivate *engine; - QQmlCompiledData *compiledData; QQmlTypeData *typeData; QQmlRefPointer importCache; QmlIR::Document *document; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index d4ed899d3e..0f81e8b0d9 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #endif #include @@ -190,8 +189,8 @@ void CompilationUnit::unlink() for (auto resolvedType = resolvedTypes.begin(), end = resolvedTypes.end(); resolvedType != end; ++resolvedType) { - if ((*resolvedType)->component) - (*resolvedType)->component->release(); + if ((*resolvedType)->compilationUnit) + (*resolvedType)->compilationUnit->release(); if ((*resolvedType)->typePropertyCache) (*resolvedType)->typePropertyCache->release(); } @@ -356,7 +355,7 @@ QQmlPropertyCache *CompilationUnit::ResolvedTypeReference::propertyCache() const if (type) return typePropertyCache; else - return component->compilationUnit->rootPropertyCache(); + return compilationUnit->rootPropertyCache(); } /*! @@ -371,7 +370,7 @@ QQmlPropertyCache *CompilationUnit::ResolvedTypeReference::createPropertyCache(Q typePropertyCache->addref(); return typePropertyCache; } else { - return component->compilationUnit->rootPropertyCache(); + return compilationUnit->rootPropertyCache(); } } @@ -392,8 +391,8 @@ void CompilationUnit::ResolvedTypeReference::doDynamicTypeCheck() mo = typePropertyCache->firstCppMetaObject(); else if (type) mo = type->metaObject(); - else if (component) - mo = component->compilationUnit->rootPropertyCache()->firstCppMetaObject(); + else if (compilationUnit) + mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); isFullyDynamicType = qtTypeInherits(mo); } #endif diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 117bad0c8b..26a27d79c8 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -73,7 +73,6 @@ class QQmlPropertyData; class QQmlTypeNameCache; class QQmlScriptData; class QQmlType; -class QQmlCompiledData; class QQmlEngine; // The vector is indexed by QV4::CompiledData::Object index and the flag @@ -700,7 +699,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount struct ResolvedTypeReference { ResolvedTypeReference() - : type(0), typePropertyCache(0), component(0) + : type(0), typePropertyCache(0), compilationUnit(0) , majorVersion(0) , minorVersion(0) , isFullyDynamicType(false) @@ -708,7 +707,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QQmlType *type; QQmlPropertyCache *typePropertyCache; - QQmlCompiledData *component; + QV4::CompiledData::CompilationUnit *compilationUnit; int majorVersion; int minorVersion; diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 60851ac444..707901063c 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -55,7 +55,6 @@ #include #include #include -#include #include "qqmlprofilerdefinitions_p.h" #include "qqmlabstractprofileradapter_p.h" @@ -154,7 +153,7 @@ public: ref(new BindingRefCount(binding), QQmlRefPointer::Adopt), sent(false) {} - RefLocation(QQmlCompiledData *ref, const QUrl &url, const QV4::CompiledData::Object *obj, + RefLocation(QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QV4::CompiledData::Object *obj, const QString &type) : Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url), locationType(Creating), ref(ref), sent(false) @@ -226,7 +225,7 @@ public: Creating, id(obj))); } - void updateCreating(const QV4::CompiledData::Object *obj, QQmlCompiledData *ref, + void updateCreating(const QV4::CompiledData::Object *obj, QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QString &type) { quintptr locationId(id(obj)); @@ -367,7 +366,7 @@ public: Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, endRange()); } - void update(QQmlCompiledData *ref, const QV4::CompiledData::Object *obj, + void update(QV4::CompiledData::CompilationUnit *ref, const QV4::CompiledData::Object *obj, const QString &typeName, const QUrl &url) { profiler->updateCreating(obj, ref, url, typeName); diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 91d883c29f..75e1eb9345 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -12,7 +12,6 @@ SOURCES += \ $$PWD/qqmlpropertyvalueinterceptor.cpp \ $$PWD/qqmlproxymetaobject.cpp \ $$PWD/qqmlvme.cpp \ - $$PWD/qqmlcompileddata.cpp \ $$PWD/qqmlboundsignal.cpp \ $$PWD/qqmlmetatype.cpp \ $$PWD/qqmlstringconverters.cpp \ @@ -71,7 +70,6 @@ HEADERS += \ $$PWD/qqmlparserstatus.h \ $$PWD/qqmlproxymetaobject_p.h \ $$PWD/qqmlvme_p.h \ - $$PWD/qqmlcompiler_p.h \ $$PWD/qqmlengine_p.h \ $$PWD/qqmlexpression_p.h \ $$PWD/qqmlprivate.h \ diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 1249e1b6c8..eacb928901 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -42,7 +42,6 @@ #include "qqml.h" #include "qqmlcontext.h" #include "qqmlinfo.h" -#include "qqmlcompiler_p.h" #include "qqmldata_p.h" #include #include diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index f423a7d452..7d6e1ffa1a 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -51,7 +51,6 @@ #include #include #include -#include #include "qqmlinfo.h" #include diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp deleted file mode 100644 index 1a9919c1be..0000000000 --- a/src/qml/qml/qqmlcompileddata.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qqmlcompiler_p.h" -#include "qqmlengine.h" -#include "qqmlcomponent.h" -#include "qqmlcomponent_p.h" -#include "qqmlcontext.h" -#include "qqmlcontext_p.h" -#include "qqmlpropertymap.h" -#ifdef QML_THREADED_VME_INTERPRETER -#include "qqmlvme_p.h" -#endif - -#include - -#include - -QT_BEGIN_NAMESPACE - -QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine) -: engine(engine) -{ - Q_ASSERT(engine); -} - -QQmlCompiledData::~QQmlCompiledData() -{ -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h deleted file mode 100644 index 2846ae4084..0000000000 --- a/src/qml/qml/qqmlcompiler_p.h +++ /dev/null @@ -1,101 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#ifndef QQMLCOMPILER_P_H -#define QQMLCOMPILER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqml.h" -#include "qqmlerror.h" -#include "qqmlengine_p.h" -#include -#include "qqmlpropertycache_p.h" -#include "qqmltypenamecache_p.h" -#include "qqmltypeloader_p.h" -#include "private/qv4identifier_p.h" -#include -#include "qqmlcustomparser_p.h" - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace CompiledData { -struct CompilationUnit; -struct Unit; -} -} - -class QQmlEngine; -class QQmlComponent; -class QQmlContext; -class QQmlContextData; - -// ### Merge with QV4::CompiledData::CompilationUnit -class Q_AUTOTEST_EXPORT QQmlCompiledData : public QQmlRefCount -{ -public: - QQmlCompiledData(QQmlEngine *engine); - virtual ~QQmlCompiledData(); - - QQmlEngine *engine; - - QQmlRefPointer compilationUnit; - -private: - QQmlCompiledData(const QQmlCompiledData &other); - QQmlCompiledData &operator=(const QQmlCompiledData &other); -}; - -QT_END_NAMESPACE - -#endif // QQMLCOMPILER_P_H diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 16c41a8950..282f915141 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -41,7 +41,6 @@ #include "qqmlcomponent_p.h" #include "qqmlcomponentattached_p.h" -#include "qqmlcompiler_p.h" #include "qqmlcontext_p.h" #include "qqmlengine_p.h" #include "qqmlvme_p.h" @@ -335,14 +334,13 @@ void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p) void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data) { url = data->finalUrl(); - QQmlCompiledData *c = data->compiledData(); + compilationUnit = data->compilationUnit(); - if (!c) { + if (!compilationUnit) { Q_ASSERT(data->isError()); state.errors = data->errors(); } else { - cc = c; - cc->addref(); + compilationUnit->addref(); } data->release(); @@ -356,9 +354,9 @@ void QQmlComponentPrivate::clear() typeData = 0; } - if (cc) { - cc->release(); - cc = 0; + if (compilationUnit) { + compilationUnit->release(); + compilationUnit = 0; } } @@ -393,8 +391,8 @@ QQmlComponent::~QQmlComponent() d->typeData->unregisterCallback(d); d->typeData->release(); } - if (d->cc) - d->cc->release(); + if (d->compilationUnit) + d->compilationUnit->release(); } /*! @@ -422,7 +420,7 @@ QQmlComponent::Status QQmlComponent::status() const return Loading; else if (!d->state.errors.isEmpty()) return Error; - else if (d->engine && d->cc) + else if (d->engine && d->compilationUnit) return Ready; else return Null; @@ -564,14 +562,14 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, /*! \internal */ -QQmlComponent::QQmlComponent(QQmlEngine *engine, QQmlCompiledData *cc, int start, QObject *parent) +QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *compilationUnit, int start, QObject *parent) : QQmlComponent(engine, parent) { Q_D(QQmlComponent); - d->cc = cc; - cc->addref(); + d->compilationUnit = compilationUnit; + compilationUnit->addref(); d->start = start; - d->url = cc->compilationUnit->url(); + d->url = compilationUnit->url(); d->progress = 1.0; } @@ -864,7 +862,7 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) enginePriv->referenceScarceResources(); QObject *rv = 0; - state.creator.reset(new QQmlObjectCreator(context, cc, creationContext)); + state.creator.reset(new QQmlObjectCreator(context, compilationUnit, creationContext)); rv = state.creator->create(start); if (!rv) state.errors = state.creator->errors; @@ -905,7 +903,7 @@ void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv, Q_ASSERT(ddata->deferredData); QQmlData::DeferredData *deferredData = ddata->deferredData; QQmlContextData *creationContext = 0; - state->creator.reset(new QQmlObjectCreator(deferredData->context->parent, deferredData->compiledData, creationContext)); + state->creator.reset(new QQmlObjectCreator(deferredData->context->parent, deferredData->compilationUnit, creationContext)); if (!state->creator->populateDeferredProperties(object)) state->errors << state->creator->errors; } @@ -1049,10 +1047,10 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->engine); - p->compiledData = d->cc; - p->compiledData->addref(); + p->compilationUnit = d->compilationUnit; + p->compilationUnit->addref(); p->enginePriv = enginePriv; - p->creator.reset(new QQmlObjectCreator(contextData, d->cc, d->creationContext, p.data())); + p->creator.reset(new QQmlObjectCreator(contextData, d->compilationUnit, d->creationContext, p.data())); p->subComponentToCreate = d->start; enginePriv->incubate(incubator, forContextData); diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h index aefbf20aff..ca60f01eb5 100644 --- a/src/qml/qml/qqmlcomponent.h +++ b/src/qml/qml/qqmlcomponent.h @@ -55,10 +55,15 @@ class QQmlEngine; class QQmlComponent; class QQmlIncubator; class QQmlV4Function; -class QQmlCompiledData; class QQmlComponentPrivate; class QQmlComponentAttached; +namespace QV4 { +namespace CompiledData { +struct CompilationUnit; +} +} + class Q_QML_EXPORT QQmlComponent : public QObject { Q_OBJECT @@ -122,7 +127,7 @@ protected: Q_INVOKABLE void incubateObject(QQmlV4Function *); private: - QQmlComponent(QQmlEngine *, QQmlCompiledData *, int, QObject *parent); + QQmlComponent(QQmlEngine *, QV4::CompiledData::CompilationUnit *compilationUnit, int, QObject *parent); Q_DISABLE_COPY(QQmlComponent) friend class QQmlTypeData; diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 039b267433..bb6c6d1c05 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -71,7 +71,6 @@ QT_BEGIN_NAMESPACE class QQmlComponent; class QQmlEngine; -class QQmlCompiledData; class QQmlComponentAttached; class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public QQmlTypeData::TypeDataCallback @@ -80,7 +79,7 @@ class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public public: QQmlComponentPrivate() - : typeData(0), progress(0.), start(-1), cc(0), engine(0), creationContext(0), depthIncreased(false) {} + : typeData(0), progress(0.), start(-1), compilationUnit(0), engine(0), creationContext(0), depthIncreased(false) {} void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous); @@ -98,7 +97,7 @@ public: qreal progress; int start; - QQmlCompiledData *cc; + QV4::CompiledData::CompilationUnit *compilationUnit; struct ConstructionState { ConstructionState() diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index 134002cf33..e9f747a440 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -39,7 +39,6 @@ #include "qqmlcustomparser_p.h" -#include "qqmlcompiler_p.h" #include #include diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index 9aa052c1ad..d1ed61defe 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -60,7 +60,6 @@ QT_BEGIN_NAMESPACE -class QQmlCompiledData; class QQmlPropertyValidator; class QQmlEnginePrivate; diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index ad2456a68d..94a3b0f198 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -63,7 +63,6 @@ QT_BEGIN_NAMESPACE template class QHash; class QQmlEngine; class QQmlGuardImpl; -class QQmlCompiledData; class QQmlAbstractBinding; class QQmlBoundSignal; class QQmlContext; @@ -72,6 +71,13 @@ class QQmlContextData; class QQmlNotifier; class QQmlDataExtended; class QQmlNotifierEndpoint; + +namespace QV4 { +namespace CompiledData { +struct CompilationUnit; +} +} + // This class is structured in such a way, that simply zero'ing it is the // default state for elemental object allocations. This is crucial in the // workings of the QQmlInstruction::CreateSimpleObject instruction. @@ -179,10 +185,10 @@ public: struct DeferredData { unsigned int deferredIdx; - QQmlCompiledData *compiledData;//Not always the same as the other compiledData + QV4::CompiledData::CompilationUnit *compilationUnit;//Not always the same as the other compilation unit QQmlContextData *context;//Could be either context or outerContext }; - QQmlCompiledData *compiledData; + QV4::CompiledData::CompilationUnit *compilationUnit; DeferredData *deferredData; QV4::WeakValue jsWrapper; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 7749c4f563..3c7c37c1ea 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -42,7 +42,6 @@ #include "qqmlcomponentattached_p.h" #include "qqmlcontext_p.h" -#include "qqmlcompiler_p.h" #include "qqml.h" #include "qqmlcontext.h" #include "qqmlexpression.h" @@ -619,7 +618,6 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) QQmlEnginePrivate::~QQmlEnginePrivate() { typedef QHash, QQmlPropertyCache *>::const_iterator TypePropertyCacheIt; - typedef QHash::const_iterator CompositeTypesIt; if (inProgressCreations) qWarning() << QQmlEngine::tr("There are still \"%1\" items in the process of being created at engine destruction.").arg(inProgressCreations); @@ -640,13 +638,13 @@ QQmlEnginePrivate::~QQmlEnginePrivate() for (TypePropertyCacheIt iter = typePropertyCache.cbegin(), end = typePropertyCache.cend(); iter != end; ++iter) (*iter)->release(); - for (CompositeTypesIt iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) { - iter.value()->compilationUnit->isRegisteredWithEngine = false; + for (auto iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) { + iter.value()->isRegisteredWithEngine = false; // since unregisterInternalCompositeType() will not be called in this // case, we have to clean up the type registration manually - QMetaType::unregisterType(iter.value()->compilationUnit->metaTypeId); - QMetaType::unregisterType(iter.value()->compilationUnit->listMetaTypeId); + QMetaType::unregisterType(iter.value()->metaTypeId); + QMetaType::unregisterType(iter.value()->listMetaTypeId); } delete profiler; } @@ -682,7 +680,7 @@ QQmlData::QQmlData() hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(0), notifyList(0), context(0), outerContext(0), bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), - lineNumber(0), columnNumber(0), jsEngineId(0), compiledData(0), deferredData(0), + lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0), deferredData(0), propertyCache(0), guards(0), extendedData(0) { init(); @@ -1411,7 +1409,7 @@ void qmlExecuteDeferred(QObject *object) QQmlComponentPrivate::beginDeferred(ep, object, &state); // Release the reference for the deferral action (we still have one from construction) - data->deferredData->compiledData->release(); + data->deferredData->compilationUnit->release(); delete data->deferredData; data->deferredData = 0; @@ -1642,13 +1640,13 @@ void QQmlData::destroyed(QObject *object) if (bindings && !bindings->ref.deref()) delete bindings; - if (compiledData) { - compiledData->release(); - compiledData = 0; + if (compilationUnit) { + compilationUnit->release(); + compilationUnit = 0; } if (deferredData) { - deferredData->compiledData->release(); + deferredData->compilationUnit->release(); delete deferredData; deferredData = 0; } @@ -2225,9 +2223,9 @@ int QQmlEnginePrivate::listType(int t) const QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const { Locker locker(this); - QHash::ConstIterator iter = m_compositeTypes.constFind(t); + auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return QQmlMetaObject((*iter)->compilationUnit->rootPropertyCache()); + return QQmlMetaObject((*iter)->rootPropertyCache()); } else { QQmlType *type = QQmlMetaType::qmlType(t); return QQmlMetaObject(type?type->baseMetaObject():0); @@ -2237,9 +2235,9 @@ QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const { Locker locker(this); - QHash::ConstIterator iter = m_compositeTypes.constFind(t); + auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return QQmlMetaObject((*iter)->compilationUnit->rootPropertyCache()); + return QQmlMetaObject((*iter)->rootPropertyCache()); } else { QQmlType *type = QQmlMetaType::qmlType(t); return QQmlMetaObject(type?type->metaObject():0); @@ -2249,9 +2247,9 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) { Locker locker(this); - QHash::ConstIterator iter = m_compositeTypes.constFind(t); + auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return (*iter)->compilationUnit->rootPropertyCache(); + return (*iter)->rootPropertyCache(); } else { QQmlType *type = QQmlMetaType::qmlType(t); locker.unlock(); @@ -2262,9 +2260,9 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) { Locker locker(this); - QHash::ConstIterator iter = m_compositeTypes.constFind(t); + auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return (*iter)->compilationUnit->rootPropertyCache(); + return (*iter)->rootPropertyCache(); } else { QQmlType *type = QQmlMetaType::qmlType(t); locker.unlock(); @@ -2272,9 +2270,9 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) } } -void QQmlEnginePrivate::registerInternalCompositeType(QQmlCompiledData *data) +void QQmlEnginePrivate::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) { - QByteArray name = data->compilationUnit->rootPropertyCache()->className(); + QByteArray name = compilationUnit->rootPropertyCache()->className(); QByteArray ptr = name + '*'; QByteArray lst = "QQmlListProperty<" + name + '>'; @@ -2292,15 +2290,15 @@ void QQmlEnginePrivate::registerInternalCompositeType(QQmlCompiledData *data) static_cast >(QtPrivate::QMetaTypeTypeFlags >::Flags), static_cast(0)); - data->compilationUnit->metaTypeId = ptr_type; - data->compilationUnit->listMetaTypeId = lst_type; - data->compilationUnit->isRegisteredWithEngine = true; + compilationUnit->metaTypeId = ptr_type; + compilationUnit->listMetaTypeId = lst_type; + compilationUnit->isRegisteredWithEngine = true; Locker locker(this); m_qmlLists.insert(lst_type, ptr_type); // The QQmlCompiledData is not referenced here, but it is removed from this // hash in the QQmlCompiledData destructor - m_compositeTypes.insert(ptr_type, data); + m_compositeTypes.insert(ptr_type, compilationUnit); } void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 13a4280203..bd4b4e536e 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -216,7 +216,7 @@ public: QQmlMetaObject metaObjectForType(int) const; QQmlPropertyCache *propertyCacheForType(int); QQmlPropertyCache *rawPropertyCacheForType(int); - void registerInternalCompositeType(QQmlCompiledData *); + void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); bool isTypeLoaded(const QUrl &url) const; @@ -260,7 +260,7 @@ private: // the threaded loader. Only access them through their respective accessor methods. QHash, QQmlPropertyCache *> typePropertyCache; QHash m_qmlLists; - QHash m_compositeTypes; + QHash m_compositeTypes; static bool s_designerMode; // These members is protected by the full QQmlEnginePrivate::mutex mutex diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 50880e70ea..d48b6ad1d3 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -44,7 +44,7 @@ #include "qqmlengine_p.h" #include "qqmlcontext_p.h" #include "qqmlscriptstring_p.h" -#include "qqmlcompiler_p.h" +#include "qqmlbinding_p.h" #include #include diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index d0991f51fe..c52b5014ca 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -41,7 +41,6 @@ #include "qqmlcomponent.h" #include "qqmlincubator_p.h" -#include "qqmlcompiler_p.h" #include "qqmlexpression_p.h" #include "qqmlmemoryprofiler_p.h" #include "qqmlobjectcreator_p.h" @@ -132,7 +131,7 @@ QQmlIncubationController *QQmlEngine::incubationController() const QQmlIncubatorPrivate::QQmlIncubatorPrivate(QQmlIncubator *q, QQmlIncubator::IncubationMode m) : q(q), status(QQmlIncubator::Null), mode(m), isAsynchronous(false), progress(Execute), - result(0), enginePriv(0), compiledData(0), waitingOnMe(0) + result(0), enginePriv(0), compilationUnit(0), waitingOnMe(0) { } @@ -145,16 +144,16 @@ void QQmlIncubatorPrivate::clear() { if (next.isInList()) { next.remove(); - Q_ASSERT(compiledData); - compiledData->release(); - compiledData = 0; + Q_ASSERT(compilationUnit); + compilationUnit->release(); + compilationUnit = 0; enginePriv->incubatorCount--; QQmlIncubationController *controller = enginePriv->incubationController; if (controller) controller->incubatingObjectCountChanged(enginePriv->incubatorCount); - } else if (compiledData) { - compiledData->release(); - compiledData = 0; + } else if (compilationUnit) { + compilationUnit->release(); + compilationUnit = 0; } enginePriv = 0; if (!rootContext.isNull()) { @@ -278,10 +277,10 @@ void QQmlIncubatorPrivate::forceCompletion(QQmlInstantiationInterrupt &i) void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) { - if (!compiledData) + if (!compilationUnit) return; - QML_MEMORY_SCOPE_URL(compiledData->compilationUnit->url()); + QML_MEMORY_SCOPE_URL(compilationUnit->url()); QExplicitlySharedDataPointer protectThis(this); @@ -291,7 +290,7 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) if (!vmeGuard.isOK()) { QQmlError error; - error.setUrl(compiledData->compilationUnit->url()); + error.setUrl(compilationUnit->url()); error.setDescription(QQmlComponent::tr("Object destroyed during incubation")); errors << error; progress = QQmlIncubatorPrivate::Completed; @@ -564,14 +563,14 @@ void QQmlIncubator::clear() QQmlEnginePrivate *enginePriv = d->enginePriv; if (s == Loading) { - Q_ASSERT(d->compiledData); + Q_ASSERT(d->compilationUnit); if (d->result) d->result->deleteLater(); d->result = 0; } d->clear(); - Q_ASSERT(d->compiledData == 0); + Q_ASSERT(d->compilationUnit == 0); Q_ASSERT(d->waitingOnMe.data() == 0); Q_ASSERT(d->waitingFor.isEmpty()); @@ -711,7 +710,7 @@ QQmlIncubator::Status QQmlIncubatorPrivate::calculateStatus() const return QQmlIncubator::Error; else if (result && progress == QQmlIncubatorPrivate::Completed && waitingFor.isEmpty()) return QQmlIncubator::Ready; - else if (compiledData) + else if (compilationUnit) return QQmlIncubator::Loading; else return QQmlIncubator::Null; diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index 974a353f77..7c6bc15404 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -59,7 +59,6 @@ QT_BEGIN_NAMESPACE -class QQmlCompiledData; class QQmlIncubator; class QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator { @@ -86,7 +85,7 @@ public: QPointer result; QQmlGuardedContextData rootContext; QQmlEnginePrivate *enginePriv; - QQmlCompiledData *compiledData; + QV4::CompiledData::CompilationUnit *compilationUnit; QScopedPointer creator; int subComponentToCreate; QQmlVMEGuard vmeGuard; diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 1b3997baae..854ad959ab 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include #include @@ -493,8 +492,8 @@ QQmlType *QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const QQmlTypeData *td = engine->typeLoader.getType(sourceUrl()); if (!td || !td->isComplete()) return 0; - QQmlCompiledData *cd = td->compiledData(); - const QMetaObject *mo = cd->compilationUnit->rootPropertyCache()->firstCppMetaObject(); + QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit(); + const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); return QQmlMetaType::qmlType(mo); } diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 546bc1d81e..80e0fa3595 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -69,11 +69,11 @@ struct ActiveOCRestorer }; } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlContextData *creationContext, void *activeVMEDataForRootContext) +QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext, void *activeVMEDataForRootContext) : phase(Startup) - , compiledData(compiledData) - , resolvedTypes(compiledData->compilationUnit->resolvedTypes) - , propertyCaches(compiledData->compilationUnit->propertyCaches) + , compilationUnit(compilationUnit) + , resolvedTypes(compilationUnit->resolvedTypes) + , propertyCaches(compilationUnit->propertyCaches) , activeVMEDataForRootContext(activeVMEDataForRootContext) { init(parentContext); @@ -81,23 +81,23 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile sharedState = new QQmlObjectCreatorSharedState; topLevelCreator = true; sharedState->componentAttached = 0; - sharedState->allCreatedBindings.allocate(compiledData->compilationUnit->totalBindingsCount); - sharedState->allParserStatusCallbacks.allocate(compiledData->compilationUnit->totalParserStatusCount); - sharedState->allCreatedObjects.allocate(compiledData->compilationUnit->totalObjectCount); + sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount); + sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount); + sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount); sharedState->allJavaScriptObjects = 0; sharedState->creationContext = creationContext; sharedState->rootContext = 0; QQmlProfiler *profiler = QQmlEnginePrivate::get(engine)->profiler; Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler, - sharedState->profiler.init(profiler, compiledData->compilationUnit->totalParserStatusCount)); + sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount)); } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlObjectCreatorSharedState *inheritedSharedState) +QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) : phase(Startup) - , compiledData(compiledData) - , resolvedTypes(compiledData->compilationUnit->resolvedTypes) - , propertyCaches(compiledData->compilationUnit->propertyCaches) + , compilationUnit(compilationUnit) + , resolvedTypes(compilationUnit->resolvedTypes) + , propertyCaches(compilationUnit->propertyCaches) , activeVMEDataForRootContext(0) { init(parentContext); @@ -112,10 +112,10 @@ void QQmlObjectCreator::init(QQmlContextData *providedParentContext) engine = parentContext->engine; v4 = QV8Engine::getV4(engine); - if (compiledData->compilationUnit && !compiledData->compilationUnit->engine) - compiledData->compilationUnit->linkToEngine(v4); + if (compilationUnit && !compilationUnit->engine) + compilationUnit->linkToEngine(v4); - qmlUnit = compiledData->compilationUnit->data; + qmlUnit = compilationUnit->data; context = 0; _qobject = 0; _scopeObject = 0; @@ -166,9 +166,9 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI context = new QQmlContextData; context->isInternal = true; - context->imports = compiledData->compilationUnit->importCache; + context->imports = compilationUnit->importCache; context->imports->addref(); - context->initFromTypeCompilationUnit(compiledData->compilationUnit, subComponentIndex); + context->initFromTypeCompilationUnit(compilationUnit, subComponentIndex); context->setParent(parentContext); if (!sharedState->rootContext) { @@ -181,14 +181,14 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator); if (topLevelCreator) - sharedState->allJavaScriptObjects = scope.alloc(compiledData->compilationUnit->totalObjectCount); + sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount); - if (subComponentIndex == -1 && compiledData->compilationUnit->dependentScripts.count()) { - QV4::ScopedObject scripts(scope, v4->newArrayObject(compiledData->compilationUnit->dependentScripts.count())); + if (subComponentIndex == -1 && compilationUnit->dependentScripts.count()) { + QV4::ScopedObject scripts(scope, v4->newArrayObject(compilationUnit->dependentScripts.count())); context->importedScripts.set(v4, scripts); QV4::ScopedValue v(scope); - for (int i = 0; i < compiledData->compilationUnit->dependentScripts.count(); ++i) { - QQmlScriptData *s = compiledData->compilationUnit->dependentScripts.at(i); + for (int i = 0; i < compilationUnit->dependentScripts.count(); ++i) { + QQmlScriptData *s = compilationUnit->dependentScripts.at(i); scripts->putIndexed(i, (v = s->scriptValueForContext(context))); } } else if (sharedState->creationContext) { @@ -199,10 +199,10 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI if (instance) { QQmlData *ddata = QQmlData::get(instance); Q_ASSERT(ddata); - if (ddata->compiledData) - ddata->compiledData->release(); - ddata->compiledData = compiledData; - ddata->compiledData->addref(); + if (ddata->compilationUnit) + ddata->compilationUnit->release(); + ddata->compilationUnit = compilationUnit; + ddata->compilationUnit->addref(); } if (topLevelCreator) @@ -236,7 +236,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance) Q_ASSERT(topLevelCreator); Q_ASSERT(!sharedState->allJavaScriptObjects); - sharedState->allJavaScriptObjects = valueScope.alloc(compiledData->compilationUnit->totalObjectCount); + sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount); QV4::QmlContext *qmlContext = static_cast(valueScope.alloc(1)); @@ -366,7 +366,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QString string = binding->valueAsString(qmlUnit); // Encoded dir-separators defeat QUrl processing - decode them first string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); - QUrl value = string.isEmpty() ? QUrl() : compiledData->compilationUnit->url().resolved(QUrl(string)); + QUrl value = string.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(string)); // Apply URL interceptor if (engine->urlInterceptor()) value = engine->urlInterceptor()->intercept(value, QQmlAbstractUrlInterceptor::UrlString); @@ -561,7 +561,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } else if (property->propType == qMetaTypeId >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); QString urlString = binding->valueAsString(qmlUnit); - QUrl u = urlString.isEmpty() ? QUrl() : compiledData->compilationUnit->url().resolved(QUrl(urlString)); + QUrl u = urlString.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(urlString)); QList value; value.append(u); argv[0] = reinterpret_cast(&value); @@ -627,7 +627,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) QQmlListProperty savedList; qSwap(_currentList, savedList); - const QV4::CompiledData::BindingPropertyData &propertyData = compiledData->compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex); + const QV4::CompiledData::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex); if (_compiledObject->idNameIndex) { const QQmlPropertyData *idProperty = propertyData.last(); @@ -800,7 +800,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyPrivate::removeBinding(_bindingTarget, property->coreIndex); if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QV4::Function *runtimeFunction = compiledData->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; QV4::Scope scope(v4); QV4::ScopedContext qmlContext(scope, currentQmlContext()); @@ -983,7 +983,7 @@ void QQmlObjectCreator::setupFunctions() const quint32 *functionIdx = _compiledObject->functionOffsetTable(); for (quint32 i = 0; i < _compiledObject->nFunctions; ++i, ++functionIdx) { - QV4::Function *runtimeFunction = compiledData->compilationUnit->runtimeFunctions[*functionIdx]; + QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[*functionIdx]; const QString name = runtimeFunction->name()->toQString(); QQmlPropertyData *property = _propertyCache->property(name, _qobject, context); @@ -998,7 +998,7 @@ void QQmlObjectCreator::setupFunctions() void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location, const QString &description) { QQmlError error; - error.setUrl(compiledData->compilationUnit->url()); + error.setUrl(compilationUnit->url()); error.setLine(location.line); error.setColumn(location.column); error.setDescription(description); @@ -1035,9 +1035,9 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (obj->flags & QV4::CompiledData::Object::IsComponent) { isComponent = true; - QQmlComponent *component = new QQmlComponent(engine, compiledData, index, parent); + QQmlComponent *component = new QQmlComponent(engine, compilationUnit, index, parent); Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compiledData, obj, QStringLiteral(""), context->url())); + compilationUnit, obj, QStringLiteral(""), context->url())); QQmlComponentPrivate::get(component)->creationContext = context; instance = component; ddata = QQmlData::get(instance, /*create*/true); @@ -1048,7 +1048,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo QQmlType *type = typeRef->type; if (type) { Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compiledData, obj, type->qmlTypeName(), context->url())); + compilationUnit, obj, type->qmlTypeName(), context->url())); instance = type->create(); if (!instance) { recordError(obj->location, tr("Unable to create object of type %1").arg(stringAt(obj->inheritedTypeNameIndex))); @@ -1069,17 +1069,17 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo sharedState->allCreatedObjects.push(instance); } else { - Q_ASSERT(typeRef->component); + Q_ASSERT(typeRef->compilationUnit); Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compiledData, obj, typeRef->component->compilationUnit->fileName(), + compilationUnit, obj, typeRef->compilationUnit->fileName(), context->url())); - if (typeRef->component->compilationUnit->data->isSingleton()) + if (typeRef->compilationUnit->data->isSingleton()) { recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex))); return 0; } - QQmlObjectCreator subCreator(context, typeRef->component, sharedState.data()); + QQmlObjectCreator subCreator(context, typeRef->compilationUnit, sharedState.data()); instance = subCreator.create(); if (!instance) { errors += subCreator.errors; @@ -1127,7 +1127,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) { customParser->engine = QQmlEnginePrivate::get(engine); - customParser->imports = compiledData->compilationUnit->importCache; + customParser->imports = compilationUnit->importCache; QList bindings; const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); @@ -1137,7 +1137,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo bindings << binding; } } - customParser->applyBindings(instance, compiledData->compilationUnit.data(), bindings); + customParser->applyBindings(instance, compilationUnit, bindings); customParser->engine = 0; customParser->imports = (QQmlTypeNameCache*)0; @@ -1286,7 +1286,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * if (needVMEMetaObject) { Q_ASSERT(!cache.isNull()); // install on _object - vmeMetaObject = new QQmlVMEMetaObject(_qobject, cache, compiledData->compilationUnit, _compiledObjectIndex); + vmeMetaObject = new QQmlVMEMetaObject(_qobject, cache, compilationUnit, _compiledObjectIndex); if (_ddata->propertyCache) _ddata->propertyCache->release(); _ddata->propertyCache = cache; @@ -1304,8 +1304,8 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings) { QQmlData::DeferredData *deferData = new QQmlData::DeferredData; deferData->deferredIdx = _compiledObjectIndex; - deferData->compiledData = compiledData; - deferData->compiledData->addref(); + deferData->compilationUnit = compilationUnit; + deferData->compilationUnit->addref(); deferData->context = context; _ddata->deferredData = deferData; } diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index d18e6696e4..b4e706941b 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -53,7 +53,6 @@ #include #include #include -#include #include #include #include @@ -86,7 +85,7 @@ class QQmlObjectCreator { Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator) public: - QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlContextData *creationContext, void *activeVMEDataForRootContext = 0); + QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext, void *activeVMEDataForRootContext = 0); ~QQmlObjectCreator(); QObject *create(int subComponentIndex = -1, QObject *parent = 0, QQmlInstantiationInterrupt *interrupt = 0); @@ -104,7 +103,7 @@ public: QFiniteStack > &allCreatedObjects() const { return sharedState->allCreatedObjects; } private: - QQmlObjectCreator(QQmlContextData *contextData, QQmlCompiledData *compiledData, QQmlObjectCreatorSharedState *inheritedSharedState); + QQmlObjectCreator(QQmlContextData *contextData, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState); void init(QQmlContextData *parentContext); @@ -136,7 +135,7 @@ private: QQmlEngine *engine; QV4::ExecutionEngine *v4; - QQmlCompiledData *compiledData; + QV4::CompiledData::CompilationUnit *compilationUnit; const QV4::CompiledData::Unit *qmlUnit; QQmlGuardedContextData parentContext; QQmlContextData *context; diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index df2ff05de1..e04b1114ad 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -50,7 +50,6 @@ #include "qqmldata_p.h" #include "qqmlstringconverters_p.h" #include "qqmllist_p.h" -#include "qqmlcompiler_p.h" #include "qqmlvmemetaobject_p.h" #include "qqmlexpression_p.h" #include "qqmlvaluetypeproxybinding_p.h" diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 4ec3dfe6a5..b2f4c6749d 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -2047,7 +2046,7 @@ const QList &QQmlTypeData::compositeSingletons() co return m_compositeSingletons; } -QQmlCompiledData *QQmlTypeData::compiledData() const +QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const { return m_compiledData; } @@ -2304,13 +2303,12 @@ void QQmlTypeData::compile() { Q_ASSERT(m_compiledData == 0); - m_compiledData = new QQmlCompiledData(typeLoader()->engine()); - - QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), m_compiledData, this, m_document.data()); - if (!compiler.compile()) { + QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), this, m_document.data()); + m_compiledData = compiler.compile(); + if (m_compiledData) { + m_compiledData->addref(); + } else { setError(compiler.compilationErrors()); - m_compiledData->release(); - m_compiledData = 0; } } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 12ab98e425..7d54b9bdd1 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -77,7 +77,6 @@ class QQmlScriptData; class QQmlScriptBlob; class QQmlQmldirData; class QQmlTypeLoader; -class QQmlCompiledData; class QQmlComponentPrivate; class QQmlTypeData; class QQmlTypeLoader; @@ -428,7 +427,7 @@ public: const QSet &namespaces() const; const QList &compositeSingletons() const; - QQmlCompiledData *compiledData() const; + QV4::CompiledData::CompilationUnit *compilationUnit() const; // Used by QQmlComponent to get notifications struct TypeDataCallback { @@ -468,7 +467,7 @@ private: QHash m_resolvedTypes; bool m_typesResolved:1; - QQmlCompiledData *m_compiledData; + QV4::CompiledData::CompilationUnit *m_compiledData; QList m_callbacks; diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index 5f9fa69944..01c4f476d6 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -39,7 +39,6 @@ #include "qqmlvme_p.h" -#include "qqmlcompiler_p.h" #include "qqmlboundsignal_p.h" #include "qqmlstringconverters_p.h" #include diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h index ac9db5c046..99d63380ad 100644 --- a/src/qml/qml/qqmlvme_p.h +++ b/src/qml/qml/qqmlvme_p.h @@ -69,7 +69,6 @@ QT_BEGIN_NAMESPACE class QObject; class QJSValue; class QQmlScriptData; -class QQmlCompiledData; class QQmlContextData; namespace QQmlVMETypes { @@ -84,10 +83,9 @@ namespace QQmlVMETypes { struct State { enum Flag { Deferred = 0x00000001 }; - State() : flags(0), context(0), compiledData(0), instructionStream(0) {} + State() : flags(0), context(0), instructionStream(0) {} quint32 flags; QQmlContextData *context; - QQmlCompiledData *compiledData; const char *instructionStream; QBitField bindingSkipList; }; diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index 75e7ed6cb1..41f454eb62 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -64,12 +64,13 @@ #include #include "qqmlguard_p.h" -#include "qqmlcompiler_p.h" #include "qqmlcontext_p.h" +#include "qqmlpropertycache_p.h" #include #include +#include #include QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 6f7f13823e..84782114ac 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include #include diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 05408b525a..1585f3eda0 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -48,7 +48,6 @@ #include #include #include -#include #include #include @@ -1919,10 +1918,10 @@ void QQmlDelegateModelItem::incubateObject( QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component); - incubatorPriv->compiledData = componentPriv->cc; - incubatorPriv->compiledData->addref(); + incubatorPriv->compilationUnit = componentPriv->compilationUnit; + incubatorPriv->compilationUnit->addref(); incubatorPriv->enginePriv = enginePriv; - incubatorPriv->creator.reset(new QQmlObjectCreator(context, componentPriv->cc, componentPriv->creationContext)); + incubatorPriv->creator.reset(new QQmlObjectCreator(context, componentPriv->compilationUnit, componentPriv->creationContext)); incubatorPriv->subComponentToCreate = componentPriv->start; enginePriv->incubate(*incubationTask, forContext); diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 69066d1c69..8c676bcec0 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -42,7 +42,6 @@ #include #include #include -#include #include #include diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index bd556444de..df65e0822b 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index ccfcfc098e..908600784e 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -27,8 +27,6 @@ ****************************************************************************/ #include "testtypes.h" -#include - static QObject *myTypeObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 788ae42726..f41f13c561 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -41,7 +41,6 @@ #include #include #include -#include #include QVariant myCustomVariantTypeConverter(const QString &data); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 58a7c39760..fde861ce42 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2074,7 +2074,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() QQmlTypeData *td = eng->typeLoader.getType(url); Q_ASSERT(td); - QV4::CompiledData::Unit *qmlUnit = td->compiledData()->compilationUnit->data; + QV4::CompiledData::Unit *qmlUnit = td->compilationUnit()->data; Q_ASSERT(qmlUnit); const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject); QCOMPARE(qmlUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index bf255ba6a0..f26d638082 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include "../../shared/util.h" @@ -77,15 +76,15 @@ void tst_qqmltranslation::translation() QQmlContext *context = qmlContext(object); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine()); QQmlTypeData *typeData = engine->typeLoader.getType(context->baseUrl()); - QQmlCompiledData *cdata = typeData->compiledData(); - QVERIFY(cdata); + QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit(); + QVERIFY(compilationUnit); QSet compiledTranslations; compiledTranslations << QStringLiteral("basic") << QStringLiteral("disambiguation") << QStringLiteral("singular") << QStringLiteral("plural"); - const QV4::CompiledData::Unit *unit = cdata->compilationUnit->data; + const QV4::CompiledData::Unit *unit = compilationUnit->data; const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { @@ -137,10 +136,10 @@ void tst_qqmltranslation::idTranslation() QQmlContext *context = qmlContext(object); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine()); QQmlTypeData *typeData = engine->typeLoader.getType(context->baseUrl()); - QQmlCompiledData *cdata = typeData->compiledData(); - QVERIFY(cdata); + QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit(); + QVERIFY(compilationUnit); - const QV4::CompiledData::Unit *unit = cdata->compilationUnit->data; + const QV4::CompiledData::Unit *unit = compilationUnit->data; const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index ef1ea3a897..5e77d80554 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include "../../shared/util.h" class tst_QQMLTypeLoader : public QQmlDataTest @@ -82,7 +81,7 @@ void tst_QQMLTypeLoader::trimCache() QQmlTypeData *data = loader.getType(url); if (i % 5 == 0) // keep references to some of them so that they aren't trimmed - data->compiledData()->addref(); + data->compilationUnit()->addref(); data->release(); } -- cgit v1.2.3 From 32e0c5f96dac3f8afe187259929dd87012e1a464 Mon Sep 17 00:00:00 2001 From: Ralf Nolden Date: Wed, 1 Jun 2016 22:25:30 +0200 Subject: Compile Fix for OpenBSD in C++11 mode Add a __cplusplus condition to an already existing OpenBSD defined part to activate the code only on older gcc compilers. Change-Id: Ied8719dc35bf203ecbadd1099d711c027722121c Reviewed-by: Simon Hausmann --- src/3rdparty/masm/wtf/MathExtras.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty/masm/wtf/MathExtras.h b/src/3rdparty/masm/wtf/MathExtras.h index 9ded0ab736..28a189b6e6 100644 --- a/src/3rdparty/masm/wtf/MathExtras.h +++ b/src/3rdparty/masm/wtf/MathExtras.h @@ -106,7 +106,7 @@ inline bool isinf(double x) { return !finite(x) && !isnand(x); } #endif -#if OS(OPENBSD) +#if OS(OPENBSD) && __cplusplus < 201103L namespace std { -- cgit v1.2.3 From 38b11b7b52b418629ff2d245dad70edf20eb520c Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Wed, 1 Jun 2016 10:03:32 +0200 Subject: Removed insignificant of qmltest again Blacklisted or skipped broken and unstable test functions. Some are moved to tests/auto/qmltest-blacklist folder. Task-number: QTBUG-33723 Task-number: QTBUG-38290 Task-number: QTBUG-53778 Task-number: QTBUG-53779 Task-number: QTBUG-53780 Task-number: QTBUG-53781 Task-number: QTBUG-53782 Task-number: QTBUG-53785 Task-number: QTBUG-53793 Change-Id: I35594d0d054f4f5719f6549536a1fc5bd7e2518f Reviewed-by: Simon Hausmann --- tests/auto/qmltest-blacklist/animators/Box.qml | 62 +++++++ .../qmltest-blacklist/animators/tst_behavior.qml | 67 +++++++ .../auto/qmltest-blacklist/animators/tst_mixed.qml | 171 ++++++++++++++++++ .../animators/tst_mixedparallel.qml | 66 +++++++ .../animators/tst_mixedsequential.qml | 66 +++++++ .../animators/tst_multiwindow.qml | 95 ++++++++++ .../qmltest-blacklist/animators/tst_nested.qml | 84 +++++++++ tests/auto/qmltest-blacklist/animators/tst_on.qml | 71 ++++++++ .../qmltest-blacklist/animators/tst_opacity.qml | 67 +++++++ .../qmltest-blacklist/animators/tst_parallel.qml | 66 +++++++ .../qmltest-blacklist/animators/tst_restart.qml | 77 ++++++++ .../qmltest-blacklist/animators/tst_rotation.qml | 65 +++++++ .../auto/qmltest-blacklist/animators/tst_scale.qml | 66 +++++++ .../qmltest-blacklist/animators/tst_sequential.qml | 66 +++++++ .../animators/tst_targetdestroyed.qml | 77 ++++++++ .../animators/tst_transformorigin.qml | 152 ++++++++++++++++ .../qmltest-blacklist/animators/tst_transition.qml | 83 +++++++++ tests/auto/qmltest-blacklist/animators/tst_x.qml | 68 +++++++ tests/auto/qmltest-blacklist/animators/tst_y.qml | 68 +++++++ .../animators/tst_zeroduration.qml | 35 ++++ .../item/tst_layerInPositioner.qml | 199 +++++++++++++++++++++ .../itemgrabber/tst_itemgrabber.qml | 152 ++++++++++++++++ tests/auto/qmltest/BLACKLIST | 7 + tests/auto/qmltest/animators/Box.qml | 62 ------- tests/auto/qmltest/animators/tst_behavior.qml | 67 ------- tests/auto/qmltest/animators/tst_mixed.qml | 171 ------------------ tests/auto/qmltest/animators/tst_mixedparallel.qml | 66 ------- .../auto/qmltest/animators/tst_mixedsequential.qml | 66 ------- tests/auto/qmltest/animators/tst_multiwindow.qml | 95 ---------- tests/auto/qmltest/animators/tst_nested.qml | 84 --------- tests/auto/qmltest/animators/tst_on.qml | 71 -------- tests/auto/qmltest/animators/tst_opacity.qml | 67 ------- tests/auto/qmltest/animators/tst_parallel.qml | 66 ------- tests/auto/qmltest/animators/tst_restart.qml | 77 -------- tests/auto/qmltest/animators/tst_rotation.qml | 65 ------- tests/auto/qmltest/animators/tst_scale.qml | 66 ------- tests/auto/qmltest/animators/tst_sequential.qml | 66 ------- .../auto/qmltest/animators/tst_targetdestroyed.qml | 77 -------- .../auto/qmltest/animators/tst_transformorigin.qml | 152 ---------------- tests/auto/qmltest/animators/tst_transition.qml | 83 --------- tests/auto/qmltest/animators/tst_x.qml | 68 ------- tests/auto/qmltest/animators/tst_y.qml | 68 ------- tests/auto/qmltest/animators/tst_zeroduration.qml | 35 ---- tests/auto/qmltest/item/tst_layerInPositioner.qml | 199 --------------------- tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml | 152 ---------------- tests/auto/qmltest/listview/tst_listview.qml | 2 + tests/auto/qmltest/qmltest.pro | 4 +- tests/auto/qmltest/window/tst_clickwindow.qml | 2 + 48 files changed, 1936 insertions(+), 1925 deletions(-) create mode 100644 tests/auto/qmltest-blacklist/animators/Box.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_behavior.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_mixed.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_mixedparallel.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_mixedsequential.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_multiwindow.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_nested.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_on.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_opacity.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_parallel.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_restart.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_rotation.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_scale.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_sequential.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_targetdestroyed.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_transformorigin.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_transition.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_x.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_y.qml create mode 100644 tests/auto/qmltest-blacklist/animators/tst_zeroduration.qml create mode 100644 tests/auto/qmltest-blacklist/item/tst_layerInPositioner.qml create mode 100644 tests/auto/qmltest-blacklist/itemgrabber/tst_itemgrabber.qml delete mode 100644 tests/auto/qmltest/animators/Box.qml delete mode 100644 tests/auto/qmltest/animators/tst_behavior.qml delete mode 100644 tests/auto/qmltest/animators/tst_mixed.qml delete mode 100644 tests/auto/qmltest/animators/tst_mixedparallel.qml delete mode 100644 tests/auto/qmltest/animators/tst_mixedsequential.qml delete mode 100644 tests/auto/qmltest/animators/tst_multiwindow.qml delete mode 100644 tests/auto/qmltest/animators/tst_nested.qml delete mode 100644 tests/auto/qmltest/animators/tst_on.qml delete mode 100644 tests/auto/qmltest/animators/tst_opacity.qml delete mode 100644 tests/auto/qmltest/animators/tst_parallel.qml delete mode 100644 tests/auto/qmltest/animators/tst_restart.qml delete mode 100644 tests/auto/qmltest/animators/tst_rotation.qml delete mode 100644 tests/auto/qmltest/animators/tst_scale.qml delete mode 100644 tests/auto/qmltest/animators/tst_sequential.qml delete mode 100644 tests/auto/qmltest/animators/tst_targetdestroyed.qml delete mode 100644 tests/auto/qmltest/animators/tst_transformorigin.qml delete mode 100644 tests/auto/qmltest/animators/tst_transition.qml delete mode 100644 tests/auto/qmltest/animators/tst_x.qml delete mode 100644 tests/auto/qmltest/animators/tst_y.qml delete mode 100644 tests/auto/qmltest/animators/tst_zeroduration.qml delete mode 100644 tests/auto/qmltest/item/tst_layerInPositioner.qml delete mode 100644 tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml diff --git a/tests/auto/qmltest-blacklist/animators/Box.qml b/tests/auto/qmltest-blacklist/animators/Box.qml new file mode 100644 index 0000000000..46218bed3f --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/Box.qml @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 + +Rectangle { + id: box + gradient: Gradient { + GradientStop { position: 0.1; color: "red" } + GradientStop { position: 0.9; color: "blue" } + } + width: 100 + height: 100 + anchors.centerIn: parent + antialiasing: true + + property int rotationChangeCounter: 0 + onRotationChanged: ++rotationChangeCounter; + + property int scaleChangeCounter: 0 + onScaleChanged: ++scaleChangeCounter; + + property int opacityChangeCounter: 0 + onOpacityChanged: ++opacityChangeCounter + + property int xChangeCounter: 0; + onXChanged: ++xChangeCounter; + + property int yChangeCounter: 0; + onYChanged: ++yChangeCounter; + +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_behavior.qml b/tests/auto/qmltest-blacklist/animators/tst_behavior.qml new file mode 100644 index 0000000000..91145909d0 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_behavior.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testcase + name: "animators-behavior" + when: box.scale == 2 + function test_endresult() { + compare(box.scaleChangeCounter, 1); + compare(box.scale, 2); + var image = grabImage(root); + + verify(image.pixel(0, 0) == Qt.rgba(1, 0, 0)); + verify(image.pixel(199, 199) == Qt.rgba(0, 0, 1)); + } + } + + Box { + id: box + Behavior on scale { ScaleAnimator { id: animation; duration: 100; } } + } + + Timer { + interval: 100; + repeat: false + running: true + onTriggered: box.scale = 2 + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_mixed.qml b/tests/auto/qmltest-blacklist/animators/tst_mixed.qml new file mode 100644 index 0000000000..a738baba25 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_mixed.qml @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testCase + name: "animators-mixed" + when: !rootAnimation.running + function test_endresult() { + compare(box.rootStart, 2); + compare(box.rootEnd, 2); + + compare(parallelWithOneSequential.before, 4); + compare(parallelWithOneSequential.scaleUpdates, 4); + + compare(parallelWithTwoSequentialNormalEndsLast.beforeAnimator, 4); + compare(parallelWithTwoSequentialNormalEndsLast.scaleUpdates, 4); + compare(parallelWithTwoSequentialNormalEndsLast.afterAnimator, 4); + compare(parallelWithTwoSequentialNormalEndsLast.beforePause, 4); + compare(parallelWithTwoSequentialNormalEndsLast.afterPause, 4); + + compare(parallelWithTwoSequentialNormalEndsFirst.beforeAnimator, 4); + compare(parallelWithTwoSequentialNormalEndsFirst.scaleUpdates, 4); + compare(parallelWithTwoSequentialNormalEndsFirst.afterAnimator, 4); + compare(parallelWithTwoSequentialNormalEndsFirst.beforePause, 4); + compare(parallelWithTwoSequentialNormalEndsFirst.afterPause, 4); + + } + } + + Box { + id: box + + property int rootStart : 0 + property int rootEnd : 0; + + SequentialAnimation { + id: rootAnimation + + running: true + loops: 2 + + ScriptAction { script: box.rootStart++; } + + ParallelAnimation { + id: parallelWithOneSequential + property int before : 0; + property int scaleUpdates : 0; + loops: 2 + SequentialAnimation { + ScriptAction { script: { + parallelWithOneSequential.before++; + box.scale = 1; + box.scaleChangeCounter = 0; + } + } + ScaleAnimator { target: box; from: 1; to: 2; duration: 100; } + ScriptAction { script: { + parallelWithOneSequential.scaleUpdates += box.scaleChangeCounter; + } + } + } + } + + ParallelAnimation { + id: parallelWithTwoSequentialNormalEndsLast + property int beforeAnimator : 0; + property int scaleUpdates : 0; + property int afterAnimator : 0; + property int beforePause : 0; + property int afterPause : 0; + loops: 2 + SequentialAnimation { + ScriptAction { script: { + parallelWithTwoSequentialNormalEndsLast.beforeAnimator++; + box.scale = 1; + box.scaleChangeCounter = 0; + } + } + ScaleAnimator { target: box; from: 1; to: 2; duration: 100; } + ScriptAction { script: { + parallelWithTwoSequentialNormalEndsLast.scaleUpdates += box.scaleChangeCounter; + parallelWithTwoSequentialNormalEndsLast.afterAnimator++; + } + } + } + SequentialAnimation { + ScriptAction { script: { + parallelWithTwoSequentialNormalEndsLast.beforePause++ + } + } + PauseAnimation { duration: 200 } + ScriptAction { script: { + parallelWithTwoSequentialNormalEndsLast.afterPause++ + } + } + } + } + + ParallelAnimation { + id: parallelWithTwoSequentialNormalEndsFirst + property int beforeAnimator : 0; + property int scaleUpdates : 0; + property int afterAnimator : 0; + property int beforePause : 0; + property int afterPause : 0; + loops: 2 + SequentialAnimation { + ScriptAction { script: { + parallelWithTwoSequentialNormalEndsFirst.beforeAnimator++; + box.scale = 1; + box.scaleChangeCounter = 0; + } + } + ScaleAnimator { target: box; from: 1; to: 2; duration: 200; } + ScriptAction { script: { + parallelWithTwoSequentialNormalEndsFirst.scaleUpdates += box.scaleChangeCounter; + parallelWithTwoSequentialNormalEndsFirst.afterAnimator++; + } + } + } + SequentialAnimation { + ScriptAction { script: parallelWithTwoSequentialNormalEndsFirst.beforePause++ } + PauseAnimation { duration: 100 } + ScriptAction { script: parallelWithTwoSequentialNormalEndsFirst.afterPause++ } + } + } + + ScriptAction { script: box.rootEnd++; } + } + + } + +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_mixedparallel.qml b/tests/auto/qmltest-blacklist/animators/tst_mixedparallel.qml new file mode 100644 index 0000000000..9d36fd10dc --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_mixedparallel.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testcase + name: "animators-mixedparallel" + when: !animation.running + function test_endresult() { + compare(box.rotationChangeCounter, 1); + compare(box.scale, 2); + compare(box.rotation, 180); + var image = grabImage(root); + verify(image.pixel(0, 0) == Qt.rgba(0, 0, 1)); + verify(image.pixel(199, 199) == Qt.rgba(1, 0, 0)); + } + } + + Box { + id: box + ParallelAnimation { + id: animation + NumberAnimation { target: box; property: "scale"; from: 1; to: 2.0; duration: 100; } + RotationAnimator { target: box; from: 0; to: 180; duration: 100; } + running: true + loops: 1; + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_mixedsequential.qml b/tests/auto/qmltest-blacklist/animators/tst_mixedsequential.qml new file mode 100644 index 0000000000..0d58fc788e --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_mixedsequential.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testcase + name: "animators-mixedsequential" + when: !animation.running + function test_endresult() { + compare(box.rotationChangeCounter, 1); + compare(box.scale, 2); + compare(box.rotation, 180); + var image = grabImage(root); + verify(image.pixel(0, 0) == Qt.rgba(0, 0, 1)); + verify(image.pixel(199, 199) == Qt.rgba(1, 0, 0)); + } + } + + Box { + id: box + ParallelAnimation { + id: animation + NumberAnimation { target: box; property: "scale"; from: 1; to: 2.0; duration: 100; } + RotationAnimator { target: box; from: 0; to: 180; duration: 100; } + running: true + loops: 1; + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_multiwindow.qml b/tests/auto/qmltest-blacklist/animators/tst_multiwindow.qml new file mode 100644 index 0000000000..9a817993fe --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_multiwindow.qml @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 +import QtQuick.Window 2.0 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testCase + name: "animators-mixed" + when: countdown == 0 + function test_endresult() { + verify(true, "Just making sure we didn't crash"); + } + } + + property int countdown: 5; + + Window { + id: window + + width: 100 + height: 100 + + ShaderEffect { + width: 50 + height: 50 + + property real t; + UniformAnimator on t { from: 0; to: 1; duration: 1000; loops: Animation.Infinite } + RotationAnimator on rotation { from: 0; to: 360; duration: 1000; loops: Animation.Infinite } + ScaleAnimator on scale { from: 0.5; to: 1.5; duration: 1000; loops: Animation.Infinite } + XAnimator on x { from: 0; to: 50; duration: 1000; loops: Animation.Infinite } + YAnimator on y { from: 0; to: 50; duration: 1000; loops: Animation.Infinite } + OpacityAnimator on opacity { from: 1; to: 0.5; duration: 1000; loops: Animation.Infinite } + + fragmentShader: " + uniform lowp float t; + uniform lowp float qt_Opacity; + varying highp vec2 qt_TexCoord0; + void main() { + gl_FragColor = vec4(qt_TexCoord0, t, 1) * qt_Opacity; + } + " + } + + visible: true + } + + Timer { + interval: 250 + running: true + repeat: true + onTriggered: { + if (window.visible) + --countdown + window.visible = !window.visible; + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_nested.qml b/tests/auto/qmltest-blacklist/animators/tst_nested.qml new file mode 100644 index 0000000000..85f5d5a922 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_nested.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testCase + name: "animators-nested" + when: !animation.running + function test_endresult() { + compare(box.before, 2); + compare(box.after, 2); + } + } + + Box { + id: box + + anchors.centerIn: undefined + + property int before: 0; + property int after: 0; + + SequentialAnimation { + id: animation; + ScriptAction { script: box.before++; } + ParallelAnimation { + ScaleAnimator { target: box; from: 2.0; to: 1; duration: 100; } + OpacityAnimator { target: box; from: 0; to: 1; duration: 100; } + } + PauseAnimation { duration: 100 } + SequentialAnimation { + ParallelAnimation { + XAnimator { target: box; from: 0; to: 100; duration: 100 } + RotationAnimator { target: box; from: 0; to: 90; duration: 100 } + } + ParallelAnimation { + XAnimator { target: box; from: 100; to: 0; duration: 100 } + RotationAnimator { target: box; from: 90; to: 0; duration: 100 } + } + } + ScriptAction { script: box.after++; } + running: true + loops: 2 + } + } + +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_on.qml b/tests/auto/qmltest-blacklist/animators/tst_on.qml new file mode 100644 index 0000000000..3775350ae5 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_on.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testCase + name: "animators-on" + when: !animx.running && !animy.running + && !anims.running && !animr.running + && !animo.running; + function test_endresult() { + tryCompare(box, 'xChangeCounter', 1); + compare(box.yChangeCounter, 1); + compare(box.scaleChangeCounter, 1); + compare(box.rotationChangeCounter, 1); + compare(box.opacityChangeCounter, 1); + compare(box.x, 100); + compare(box.y, 100); + compare(box.scale, 2); + compare(box.rotation, 180); + compare(box.opacity, 0.5); + } + } + + Box { + id: box + anchors.centerIn: undefined + XAnimator on x { id: animx; from: 0; to: 100; duration: 100 } + YAnimator on y { id: animy; from: 0; to: 100; duration: 100 } + ScaleAnimator on scale { id: anims; from: 1; to: 2; duration: 100 } + RotationAnimator on rotation { id: animr ; from: 0; to: 180; duration: 100 } + OpacityAnimator on opacity { id: animo; from: 1; to: 0.5; duration: 100 } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_opacity.qml b/tests/auto/qmltest-blacklist/animators/tst_opacity.qml new file mode 100644 index 0000000000..72b2c61012 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_opacity.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testCase + name: "animators-opacity" + when: box.opacity == 0.5 + function test_endresult() { + compare(box.opacityChangeCounter, 1); + var image = grabImage(root); + compare(image.red(50, 50), 255); + verify(image.green(50, 50) > 0); + verify(image.blue(50, 50) > 0); + } + } + + Box { + id: box + + OpacityAnimator { + id: animation + target: box + from: 1; + to: 0.5 + duration: 100 + running: true + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_parallel.qml b/tests/auto/qmltest-blacklist/animators/tst_parallel.qml new file mode 100644 index 0000000000..f25bae18d5 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_parallel.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testcase + name: "animators-parallel" + when: !animation.running + function test_endresult() { + compare(box.rotationChangeCounter, 1); + compare(box.scaleChangeCounter, 1); + compare(box.scale, 2); + compare(box.rotation, 180); + var image = grabImage(root); + verify(image.pixel(0, 0) == Qt.rgba(0, 0, 1)); + verify(image.pixel(199, 199) == Qt.rgba(1, 0, 0)); + } + } + + Box { + id: box + ParallelAnimation { + id: animation + ScaleAnimator { target: box; from: 1; to: 2.0; duration: 100; } + RotationAnimator { target: box; from: 0; to: 180; duration: 100; } + running: true + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_restart.qml b/tests/auto/qmltest-blacklist/animators/tst_restart.qml new file mode 100644 index 0000000000..e029ca2059 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_restart.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + property int restartCount: 5; + + TestCase { + id: testcase + name: "animators-restart" + when: root.restartCount == 0 && animation.running == false; + function test_endresult() { + compare(box.scale, 2); + } + } + + Box { + id: box + + ScaleAnimator { + id: animation + target: box; + from: 1; + to: 2.0; + duration: 100; + loops: 1 + running: false; + } + + Timer { + id: timer; + interval: 500 + running: true + repeat: true + onTriggered: { + animation.running = true; + --root.restartCount; + } + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_rotation.qml b/tests/auto/qmltest-blacklist/animators/tst_rotation.qml new file mode 100644 index 0000000000..a91af92ab1 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_rotation.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testCase + name: "animators-rotation" + when: box.rotation == 180 + function test_endresult() { + compare(box.rotationChangeCounter, 1); + var image = grabImage(root); + verify(image.pixel(50, 50) == Qt.rgba(0, 0, 1)); + } + } + + Box { + id: box + RotationAnimator { + id: animation + target: box + from: 0; + to: 180 + duration: 100 + easing.type: Easing.InOutBack + running: true + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_scale.qml b/tests/auto/qmltest-blacklist/animators/tst_scale.qml new file mode 100644 index 0000000000..402468079a --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_scale.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testCase + name: "animators-scale" + when: box.scale == 2; + function test_endresult() { + compare(box.scaleChangeCounter, 1); + var image = grabImage(root); + verify(image.pixel(0, 0) == Qt.rgba(1, 0, 0)); + } + } + + Box { + id: box + + ScaleAnimator { + id: animation + target: box + from: 1; + to: 2.0 + duration: 100 + easing.type: Easing.InOutCubic + running: true + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_sequential.qml b/tests/auto/qmltest-blacklist/animators/tst_sequential.qml new file mode 100644 index 0000000000..f021bbeb7a --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_sequential.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testcase + name: "animators-parallel" + when: !animation.running + function test_endresult() { + compare(box.rotationChangeCounter, 1); + compare(box.scaleChangeCounter, 1); + compare(box.scale, 2); + compare(box.rotation, 180); + var image = grabImage(root); + compare(image.pixel(0, 0), Qt.rgba(0, 0, 1, 1)); + compare(image.pixel(199, 199), Qt.rgba(1, 0, 0, 1)); + } + } + + Box { + id: box + SequentialAnimation { + id: animation + ScaleAnimator { target: box; from: 1; to: 2.0; duration: 100; } + RotationAnimator { target: box; from: 0; to: 180; duration: 100; } + running: true + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_targetdestroyed.qml b/tests/auto/qmltest-blacklist/animators/tst_targetdestroyed.qml new file mode 100644 index 0000000000..bf9efd2162 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_targetdestroyed.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Gunnar Sletta +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testcase + name: "animators-targetdestroyed" + when: false + function test_endresult() { + verify(true, "Got here :)"); + } + } + + Rectangle { + id: box + width: 10 + height: 10 + color: "steelblue" + } + + YAnimator { + id: anim + target: box + from: 0; + to: 100 + duration: 100 + loops: Animation.Infinite + running: true + } + + SequentialAnimation { + running: true + PauseAnimation { duration: 150 } + ScriptAction { script: box.destroy(); } + PauseAnimation { duration: 50 } + ScriptAction { script: anim.destroy(); } + PauseAnimation { duration: 50 } + ScriptAction { script: testcase.when = true } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_transformorigin.qml b/tests/auto/qmltest-blacklist/animators/tst_transformorigin.qml new file mode 100644 index 0000000000..eed93b9843 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_transformorigin.qml @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 300 + height: 300 + + Timer { + id: timer; + running: testCase.windowShown + interval: 1000 + repeat: false + onTriggered: triggered = true; + property bool triggered: false; + } + + TestCase { + id: testCase + name: "animators-transformorigin" + when: timer.triggered + function test_endresult() { + + var image = grabImage(root); + + var white = Qt.rgba(1, 1, 1); + var blue = Qt.rgba(0, 0, 1); + + + // topleft + verify(image.pixel(40, 40) == white); + verify(image.pixel(60, 40) == white); + verify(image.pixel(40, 60) == white); + verify(image.pixel(60, 60) == blue); + + // top + verify(image.pixel(140, 40) == white); + verify(image.pixel(160, 40) == white); + verify(image.pixel(140, 60) == blue); + verify(image.pixel(160, 60) == blue); + + // topright + verify(image.pixel(240, 40) == white); + verify(image.pixel(260, 40) == white); + verify(image.pixel(240, 60) == blue); + verify(image.pixel(260, 60) == white); + + + // left + verify(image.pixel(40, 140) == white); + verify(image.pixel(60, 140) == blue); + verify(image.pixel(40, 160) == white); + verify(image.pixel(60, 160) == blue); + + // center + verify(image.pixel(140, 140) == blue); + verify(image.pixel(160, 140) == blue); + verify(image.pixel(140, 160) == blue); + verify(image.pixel(160, 160) == blue); + + // right + verify(image.pixel(240, 140) == blue); + verify(image.pixel(260, 140) == white); + verify(image.pixel(240, 160) == blue); + verify(image.pixel(260, 160) == white); + + + // bottomleft + verify(image.pixel(40, 240) == white); + verify(image.pixel(60, 240) == blue); + verify(image.pixel(40, 260) == white); + verify(image.pixel(60, 260) == white); + + // bottom + verify(image.pixel(140, 240) == blue); + verify(image.pixel(160, 240) == blue); + verify(image.pixel(140, 260) == white); + verify(image.pixel(160, 260) == white); + + // bottomright + verify(image.pixel(240, 240) == blue); + verify(image.pixel(260, 240) == white); + verify(image.pixel(240, 260) == white); + verify(image.pixel(260, 260) == white); + + } + } + + property var origins: [Item.TopLeft, Item.Top, Item.TopRight, + Item.Left, Item.Center, Item.Right, + Item.BottomLeft, Item.Bottom, Item.BottomRight]; + + Grid { + anchors.fill: parent + rows: 3 + columns: 3 + + Repeater { + model: 9 + Item { + width: 100 + height: 100 + Rectangle { + id: box + color: "blue" + anchors.centerIn: parent + width: 10 + height: 10 + antialiasing: true; + + transformOrigin: root.origins[index]; + + ScaleAnimator { target: box; from: 1; to: 5.5; duration: 100; running: true; } + } + } + } + } + +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_transition.qml b/tests/auto/qmltest-blacklist/animators/tst_transition.qml new file mode 100644 index 0000000000..b02238ef23 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_transition.qml @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testcase + name: "animators-transition" + when: box.scale == 2 + function test_endresult() { + compare(box.scaleChangeCounter, 1); + compare(box.scale, 2); + var image = grabImage(root); + verify(image.pixel(0, 0) == Qt.rgba(1, 0, 0)); + verify(image.pixel(199, 199) == Qt.rgba(0, 0, 1)); + } + } + + states: [ + State { + name: "one" + PropertyChanges { target: box; scale: 1 } + }, + State { + name: "two" + PropertyChanges { target: box; scale: 2 } + } + ] + state: "one" + + transitions: [ + Transition { + ScaleAnimator { duration: 100; } + } + ] + + Box { + id: box + } + + Timer { + interval: 100; + repeat: false + running: true + onTriggered: root.state = "two" + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_x.qml b/tests/auto/qmltest-blacklist/animators/tst_x.qml new file mode 100644 index 0000000000..c462ee87d0 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_x.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testCase + name: "animators-x" + when: box.x == 100 + function test_endresult() { + compare(box.xChangeCounter, 1); + var image = grabImage(root); + verify(image.pixel(100, 0) == Qt.rgba(1, 0, 0)); + verify(image.pixel(99, 0) == Qt.rgba(1, 1, 1)); // outside on the left + } + } + + Box { + id: box + + anchors.centerIn: undefined + + XAnimator { + id: animation + target: box + from: 0; + to: 100 + duration: 100 + running: true + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_y.qml b/tests/auto/qmltest-blacklist/animators/tst_y.qml new file mode 100644 index 0000000000..04487baee0 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_y.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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: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$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testCase + name: "animators-y" + when: box.y == 100 + function test_endresult() { + compare(box.yChangeCounter, 1); + var image = grabImage(root); + verify(image.pixel(0, 100) == Qt.rgba(1, 0, 0)); + verify(image.pixel(0, 99) == Qt.rgba(1, 1, 1)); // outside on the top + } + } + + Box { + id: box + + anchors.centerIn: undefined + + YAnimator { + id: animation + target: box + from: 0; + to: 100 + duration: 100 + running: true + } + } +} diff --git a/tests/auto/qmltest-blacklist/animators/tst_zeroduration.qml b/tests/auto/qmltest-blacklist/animators/tst_zeroduration.qml new file mode 100644 index 0000000000..83ce235f42 --- /dev/null +++ b/tests/auto/qmltest-blacklist/animators/tst_zeroduration.qml @@ -0,0 +1,35 @@ +import QtQuick 2.2 +import QtTest 1.1 + +Item { + id: root; + width: 200 + height: 200 + + TestCase { + id: testCase + name: "animators-y" + when: box.y == 100 + function test_endresult() { + compare(box.yChangeCounter, 1); + var image = grabImage(root); + verify(image.pixel(0, 100) == Qt.rgba(1, 0, 0)); + verify(image.pixel(0, 99) == Qt.rgba(1, 1, 1)); // outside on the top + } + } + + Box { + id: box + + anchors.centerIn: undefined + + YAnimator { + id: animation + target: box + from: 0; + to: 100 + duration: 0 + running: true + } + } +} diff --git a/tests/auto/qmltest-blacklist/item/tst_layerInPositioner.qml b/tests/auto/qmltest-blacklist/item/tst_layerInPositioner.qml new file mode 100644 index 0000000000..315ff0e5e0 --- /dev/null +++ b/tests/auto/qmltest-blacklist/item/tst_layerInPositioner.qml @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtTest 1.1 + +Item { + id: root; + width: 400 + height: 400 + + TestCase { + id: testCase + name: "transparentForPositioner" + when: windowShown + function test_endresult() { + var image = grabImage(root); + + // Row of red, green, blue and white box inside blue + // At 10,10, spanning 10x10 pixels each + verify(image.pixel(10, 10) == Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(20, 10) == Qt.rgba(0, 1, 0, 1)); + verify(image.pixel(30, 10) == Qt.rgba(0, 0, 1, 1)); + + // Column of red, green, blue and white box inside blue + // At 10,30, spanning 10x10 pixels each + verify(image.pixel(10, 30) == Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(10, 40) == Qt.rgba(0, 1, 0, 1)); + verify(image.pixel(10, 50) == Qt.rgba(0, 0, 1, 1)); + + // Flow of red, green, blue and white box inside blue + // At 30,30, spanning 10x10 pixels each, wrapping after two boxes + verify(image.pixel(30, 30) == Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(40, 30) == Qt.rgba(0, 1, 0, 1)); + verify(image.pixel(30, 40) == Qt.rgba(0, 0, 1, 1)); + + // Flow of red, green, blue and white box inside blue + // At 100,10, spanning 10x10 pixels each, wrapping after two boxes + verify(image.pixel(60, 10) == Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(70, 10) == Qt.rgba(0, 1, 0, 1)); + verify(image.pixel(60, 20) == Qt.rgba(0, 0, 1, 1)); + } + } + + Component { + id: greenPassThrough + ShaderEffect { + fragmentShader: + " + uniform lowp sampler2D source; + varying highp vec2 qt_TexCoord0; + void main() { + gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(0, 1, 0, 1); + } + " + } + } + + Row { + id: theRow + x: 10 + y: 10 + Rectangle { + width: 10 + height: 10 + color: "#ff0000" + layer.enabled: true + } + + Rectangle { + width: 10 + height: 10 + color: "#ffffff" + layer.enabled: true + layer.effect: greenPassThrough + } + + Rectangle { + id: blueInRow + width: 10 + height: 10 + color: "#0000ff" + } + } + + Column { + id: theColumn + x: 10 + y: 30 + Rectangle { + width: 10 + height: 10 + color: "#ff0000" + layer.enabled: true + } + + Rectangle { + width: 10 + height: 10 + color: "#ffffff" + layer.enabled: true + layer.effect: greenPassThrough + } + + Rectangle { + id: blueInColumn + width: 10 + height: 10 + color: "#0000ff" + } + } + + Flow { + id: theFlow + x: 30 + y: 30 + width: 20 + Rectangle { + width: 10 + height: 10 + color: "#ff0000" + layer.enabled: true + } + + Rectangle { + width: 10 + height: 10 + color: "#ffffff" + layer.enabled: true + layer.effect: greenPassThrough + } + + Rectangle { + id: blueInFlow + width: 10 + height: 10 + color: "#0000ff" + } + } + + Grid { + id: theGrid + x: 60 + y: 10 + columns: 2 + Rectangle { + width: 10 + height: 10 + color: "#ff0000" + layer.enabled: true + } + + Rectangle { + width: 10 + height: 10 + color: "#ffffff" + layer.enabled: true + layer.effect: greenPassThrough + } + + Rectangle { + id: blueInGrid + width: 10 + height: 10 + color: "#0000ff" + } + } + +} diff --git a/tests/auto/qmltest-blacklist/itemgrabber/tst_itemgrabber.qml b/tests/auto/qmltest-blacklist/itemgrabber/tst_itemgrabber.qml new file mode 100644 index 0000000000..5d65a1666c --- /dev/null +++ b/tests/auto/qmltest-blacklist/itemgrabber/tst_itemgrabber.qml @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtTest 1.1 + +Item { + id: root; + width: 400 + height: 400 + + TestCase { + id: testCase + name: "item-grabber" + when: imageOnDisk.ready && imageOnDiskSmall.ready && imageInCache.ready && imageInCacheSmall.ready + function test_endresult() { + var image = grabImage(root); + + // imageOnDisk at (0, 0) - (100x100) + compare(imageOnDisk.width, 100); + compare(imageOnDisk.height, 100); + verify(image.pixel(0, 0) === Qt.rgba(1, 0, 0, 1)); // Use verify because compare doesn't support colors (QTBUG-34878) + verify(image.pixel(99, 99) === Qt.rgba(0, 0, 1, 1)); + + // imageOnDiskSmall at (100, 0) - 50x50 + compare(imageOnDiskSmall.width, 50); + compare(imageOnDiskSmall.height, 50); + verify(image.pixel(100, 0) === Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(149, 49) === Qt.rgba(0, 0, 1, 1)); + + // imageInCache at (0, 100) - 100x100 + compare(imageInCache.width, 100); + compare(imageInCache.height, 100); + verify(image.pixel(0, 100) === Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(99, 199) === Qt.rgba(0, 0, 1, 1)); + + // imageInCacheSmall at (100, 100) - 50x50 + compare(imageInCacheSmall.width, 50); + compare(imageInCacheSmall.height, 50); + verify(image.pixel(100, 100) === Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(149, 149) === Qt.rgba(0, 0, 1, 1)); + + // After all that has been going on, it should only have been called that one time.. + compare(imageOnDisk.callCount, 1); + } + + onWindowShownChanged: { + box.grabToImage(imageOnDisk.handleGrab); + box.grabToImage(imageOnDiskSmall.handleGrab, Qt.size(50, 50)); + box.grabToImage(imageInCache.handleGrab); + box.grabToImage(imageInCacheSmall.handleGrab, Qt.size(50, 50)); + } + + } + + Rectangle { + id: box + width: 100 + height: 100 + color: "red"; + + visible: false + + Rectangle { + anchors.bottom: parent.bottom; + anchors.right: parent.right; + width: 10 + height: 10 + color: "blue"; + } + } + + Image { + id: imageOnDisk + x: 0 + y: 0 + property int callCount: 0; + property bool ready: false; + function handleGrab(result) { + if (!result.saveToFile("image.png")) + print("Error: Failed to save image to disk..."); + source = "image.png"; + ready = true; + ++callCount; + } + } + + Image { + id: imageOnDiskSmall + x: 100 + y: 0 + property bool ready: false; + function handleGrab(result) { + if (!result.saveToFile("image_small.png")) + print("Error: Failed to save image to disk..."); + source = "image_small.png"; + ready = true; + } + } + + Image { + id: imageInCache + x: 0 + y: 100 + property bool ready: false; + function handleGrab(result) { + source = result.url; + ready = true; + } + } + + Image { + id: imageInCacheSmall + x: 100 + y: 100 + property bool ready: false; + function handleGrab(result) { + source = result.url; + ready = true; + } + } +} diff --git a/tests/auto/qmltest/BLACKLIST b/tests/auto/qmltest/BLACKLIST index cd8b1181e0..490e20d5f8 100644 --- a/tests/auto/qmltest/BLACKLIST +++ b/tests/auto/qmltest/BLACKLIST @@ -3,3 +3,10 @@ * [SelfTests::test_blacklistWithData:test2] * +[tst_grabImage::test_equals] +linux +[Text::test_linecount] +osx +windows +[TextInput::test_doublevalidators] +osx diff --git a/tests/auto/qmltest/animators/Box.qml b/tests/auto/qmltest/animators/Box.qml deleted file mode 100644 index 46218bed3f..0000000000 --- a/tests/auto/qmltest/animators/Box.qml +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 - -Rectangle { - id: box - gradient: Gradient { - GradientStop { position: 0.1; color: "red" } - GradientStop { position: 0.9; color: "blue" } - } - width: 100 - height: 100 - anchors.centerIn: parent - antialiasing: true - - property int rotationChangeCounter: 0 - onRotationChanged: ++rotationChangeCounter; - - property int scaleChangeCounter: 0 - onScaleChanged: ++scaleChangeCounter; - - property int opacityChangeCounter: 0 - onOpacityChanged: ++opacityChangeCounter - - property int xChangeCounter: 0; - onXChanged: ++xChangeCounter; - - property int yChangeCounter: 0; - onYChanged: ++yChangeCounter; - -} diff --git a/tests/auto/qmltest/animators/tst_behavior.qml b/tests/auto/qmltest/animators/tst_behavior.qml deleted file mode 100644 index 91145909d0..0000000000 --- a/tests/auto/qmltest/animators/tst_behavior.qml +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testcase - name: "animators-behavior" - when: box.scale == 2 - function test_endresult() { - compare(box.scaleChangeCounter, 1); - compare(box.scale, 2); - var image = grabImage(root); - - verify(image.pixel(0, 0) == Qt.rgba(1, 0, 0)); - verify(image.pixel(199, 199) == Qt.rgba(0, 0, 1)); - } - } - - Box { - id: box - Behavior on scale { ScaleAnimator { id: animation; duration: 100; } } - } - - Timer { - interval: 100; - repeat: false - running: true - onTriggered: box.scale = 2 - } -} diff --git a/tests/auto/qmltest/animators/tst_mixed.qml b/tests/auto/qmltest/animators/tst_mixed.qml deleted file mode 100644 index a738baba25..0000000000 --- a/tests/auto/qmltest/animators/tst_mixed.qml +++ /dev/null @@ -1,171 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jolla Ltd, author: -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testCase - name: "animators-mixed" - when: !rootAnimation.running - function test_endresult() { - compare(box.rootStart, 2); - compare(box.rootEnd, 2); - - compare(parallelWithOneSequential.before, 4); - compare(parallelWithOneSequential.scaleUpdates, 4); - - compare(parallelWithTwoSequentialNormalEndsLast.beforeAnimator, 4); - compare(parallelWithTwoSequentialNormalEndsLast.scaleUpdates, 4); - compare(parallelWithTwoSequentialNormalEndsLast.afterAnimator, 4); - compare(parallelWithTwoSequentialNormalEndsLast.beforePause, 4); - compare(parallelWithTwoSequentialNormalEndsLast.afterPause, 4); - - compare(parallelWithTwoSequentialNormalEndsFirst.beforeAnimator, 4); - compare(parallelWithTwoSequentialNormalEndsFirst.scaleUpdates, 4); - compare(parallelWithTwoSequentialNormalEndsFirst.afterAnimator, 4); - compare(parallelWithTwoSequentialNormalEndsFirst.beforePause, 4); - compare(parallelWithTwoSequentialNormalEndsFirst.afterPause, 4); - - } - } - - Box { - id: box - - property int rootStart : 0 - property int rootEnd : 0; - - SequentialAnimation { - id: rootAnimation - - running: true - loops: 2 - - ScriptAction { script: box.rootStart++; } - - ParallelAnimation { - id: parallelWithOneSequential - property int before : 0; - property int scaleUpdates : 0; - loops: 2 - SequentialAnimation { - ScriptAction { script: { - parallelWithOneSequential.before++; - box.scale = 1; - box.scaleChangeCounter = 0; - } - } - ScaleAnimator { target: box; from: 1; to: 2; duration: 100; } - ScriptAction { script: { - parallelWithOneSequential.scaleUpdates += box.scaleChangeCounter; - } - } - } - } - - ParallelAnimation { - id: parallelWithTwoSequentialNormalEndsLast - property int beforeAnimator : 0; - property int scaleUpdates : 0; - property int afterAnimator : 0; - property int beforePause : 0; - property int afterPause : 0; - loops: 2 - SequentialAnimation { - ScriptAction { script: { - parallelWithTwoSequentialNormalEndsLast.beforeAnimator++; - box.scale = 1; - box.scaleChangeCounter = 0; - } - } - ScaleAnimator { target: box; from: 1; to: 2; duration: 100; } - ScriptAction { script: { - parallelWithTwoSequentialNormalEndsLast.scaleUpdates += box.scaleChangeCounter; - parallelWithTwoSequentialNormalEndsLast.afterAnimator++; - } - } - } - SequentialAnimation { - ScriptAction { script: { - parallelWithTwoSequentialNormalEndsLast.beforePause++ - } - } - PauseAnimation { duration: 200 } - ScriptAction { script: { - parallelWithTwoSequentialNormalEndsLast.afterPause++ - } - } - } - } - - ParallelAnimation { - id: parallelWithTwoSequentialNormalEndsFirst - property int beforeAnimator : 0; - property int scaleUpdates : 0; - property int afterAnimator : 0; - property int beforePause : 0; - property int afterPause : 0; - loops: 2 - SequentialAnimation { - ScriptAction { script: { - parallelWithTwoSequentialNormalEndsFirst.beforeAnimator++; - box.scale = 1; - box.scaleChangeCounter = 0; - } - } - ScaleAnimator { target: box; from: 1; to: 2; duration: 200; } - ScriptAction { script: { - parallelWithTwoSequentialNormalEndsFirst.scaleUpdates += box.scaleChangeCounter; - parallelWithTwoSequentialNormalEndsFirst.afterAnimator++; - } - } - } - SequentialAnimation { - ScriptAction { script: parallelWithTwoSequentialNormalEndsFirst.beforePause++ } - PauseAnimation { duration: 100 } - ScriptAction { script: parallelWithTwoSequentialNormalEndsFirst.afterPause++ } - } - } - - ScriptAction { script: box.rootEnd++; } - } - - } - -} diff --git a/tests/auto/qmltest/animators/tst_mixedparallel.qml b/tests/auto/qmltest/animators/tst_mixedparallel.qml deleted file mode 100644 index 9d36fd10dc..0000000000 --- a/tests/auto/qmltest/animators/tst_mixedparallel.qml +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testcase - name: "animators-mixedparallel" - when: !animation.running - function test_endresult() { - compare(box.rotationChangeCounter, 1); - compare(box.scale, 2); - compare(box.rotation, 180); - var image = grabImage(root); - verify(image.pixel(0, 0) == Qt.rgba(0, 0, 1)); - verify(image.pixel(199, 199) == Qt.rgba(1, 0, 0)); - } - } - - Box { - id: box - ParallelAnimation { - id: animation - NumberAnimation { target: box; property: "scale"; from: 1; to: 2.0; duration: 100; } - RotationAnimator { target: box; from: 0; to: 180; duration: 100; } - running: true - loops: 1; - } - } -} diff --git a/tests/auto/qmltest/animators/tst_mixedsequential.qml b/tests/auto/qmltest/animators/tst_mixedsequential.qml deleted file mode 100644 index 0d58fc788e..0000000000 --- a/tests/auto/qmltest/animators/tst_mixedsequential.qml +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testcase - name: "animators-mixedsequential" - when: !animation.running - function test_endresult() { - compare(box.rotationChangeCounter, 1); - compare(box.scale, 2); - compare(box.rotation, 180); - var image = grabImage(root); - verify(image.pixel(0, 0) == Qt.rgba(0, 0, 1)); - verify(image.pixel(199, 199) == Qt.rgba(1, 0, 0)); - } - } - - Box { - id: box - ParallelAnimation { - id: animation - NumberAnimation { target: box; property: "scale"; from: 1; to: 2.0; duration: 100; } - RotationAnimator { target: box; from: 0; to: 180; duration: 100; } - running: true - loops: 1; - } - } -} diff --git a/tests/auto/qmltest/animators/tst_multiwindow.qml b/tests/auto/qmltest/animators/tst_multiwindow.qml deleted file mode 100644 index 9a817993fe..0000000000 --- a/tests/auto/qmltest/animators/tst_multiwindow.qml +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jolla Ltd, author: -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 -import QtQuick.Window 2.0 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testCase - name: "animators-mixed" - when: countdown == 0 - function test_endresult() { - verify(true, "Just making sure we didn't crash"); - } - } - - property int countdown: 5; - - Window { - id: window - - width: 100 - height: 100 - - ShaderEffect { - width: 50 - height: 50 - - property real t; - UniformAnimator on t { from: 0; to: 1; duration: 1000; loops: Animation.Infinite } - RotationAnimator on rotation { from: 0; to: 360; duration: 1000; loops: Animation.Infinite } - ScaleAnimator on scale { from: 0.5; to: 1.5; duration: 1000; loops: Animation.Infinite } - XAnimator on x { from: 0; to: 50; duration: 1000; loops: Animation.Infinite } - YAnimator on y { from: 0; to: 50; duration: 1000; loops: Animation.Infinite } - OpacityAnimator on opacity { from: 1; to: 0.5; duration: 1000; loops: Animation.Infinite } - - fragmentShader: " - uniform lowp float t; - uniform lowp float qt_Opacity; - varying highp vec2 qt_TexCoord0; - void main() { - gl_FragColor = vec4(qt_TexCoord0, t, 1) * qt_Opacity; - } - " - } - - visible: true - } - - Timer { - interval: 250 - running: true - repeat: true - onTriggered: { - if (window.visible) - --countdown - window.visible = !window.visible; - } - } -} diff --git a/tests/auto/qmltest/animators/tst_nested.qml b/tests/auto/qmltest/animators/tst_nested.qml deleted file mode 100644 index 85f5d5a922..0000000000 --- a/tests/auto/qmltest/animators/tst_nested.qml +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testCase - name: "animators-nested" - when: !animation.running - function test_endresult() { - compare(box.before, 2); - compare(box.after, 2); - } - } - - Box { - id: box - - anchors.centerIn: undefined - - property int before: 0; - property int after: 0; - - SequentialAnimation { - id: animation; - ScriptAction { script: box.before++; } - ParallelAnimation { - ScaleAnimator { target: box; from: 2.0; to: 1; duration: 100; } - OpacityAnimator { target: box; from: 0; to: 1; duration: 100; } - } - PauseAnimation { duration: 100 } - SequentialAnimation { - ParallelAnimation { - XAnimator { target: box; from: 0; to: 100; duration: 100 } - RotationAnimator { target: box; from: 0; to: 90; duration: 100 } - } - ParallelAnimation { - XAnimator { target: box; from: 100; to: 0; duration: 100 } - RotationAnimator { target: box; from: 90; to: 0; duration: 100 } - } - } - ScriptAction { script: box.after++; } - running: true - loops: 2 - } - } - -} diff --git a/tests/auto/qmltest/animators/tst_on.qml b/tests/auto/qmltest/animators/tst_on.qml deleted file mode 100644 index 3775350ae5..0000000000 --- a/tests/auto/qmltest/animators/tst_on.qml +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testCase - name: "animators-on" - when: !animx.running && !animy.running - && !anims.running && !animr.running - && !animo.running; - function test_endresult() { - tryCompare(box, 'xChangeCounter', 1); - compare(box.yChangeCounter, 1); - compare(box.scaleChangeCounter, 1); - compare(box.rotationChangeCounter, 1); - compare(box.opacityChangeCounter, 1); - compare(box.x, 100); - compare(box.y, 100); - compare(box.scale, 2); - compare(box.rotation, 180); - compare(box.opacity, 0.5); - } - } - - Box { - id: box - anchors.centerIn: undefined - XAnimator on x { id: animx; from: 0; to: 100; duration: 100 } - YAnimator on y { id: animy; from: 0; to: 100; duration: 100 } - ScaleAnimator on scale { id: anims; from: 1; to: 2; duration: 100 } - RotationAnimator on rotation { id: animr ; from: 0; to: 180; duration: 100 } - OpacityAnimator on opacity { id: animo; from: 1; to: 0.5; duration: 100 } - } -} diff --git a/tests/auto/qmltest/animators/tst_opacity.qml b/tests/auto/qmltest/animators/tst_opacity.qml deleted file mode 100644 index 72b2c61012..0000000000 --- a/tests/auto/qmltest/animators/tst_opacity.qml +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testCase - name: "animators-opacity" - when: box.opacity == 0.5 - function test_endresult() { - compare(box.opacityChangeCounter, 1); - var image = grabImage(root); - compare(image.red(50, 50), 255); - verify(image.green(50, 50) > 0); - verify(image.blue(50, 50) > 0); - } - } - - Box { - id: box - - OpacityAnimator { - id: animation - target: box - from: 1; - to: 0.5 - duration: 100 - running: true - } - } -} diff --git a/tests/auto/qmltest/animators/tst_parallel.qml b/tests/auto/qmltest/animators/tst_parallel.qml deleted file mode 100644 index f25bae18d5..0000000000 --- a/tests/auto/qmltest/animators/tst_parallel.qml +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testcase - name: "animators-parallel" - when: !animation.running - function test_endresult() { - compare(box.rotationChangeCounter, 1); - compare(box.scaleChangeCounter, 1); - compare(box.scale, 2); - compare(box.rotation, 180); - var image = grabImage(root); - verify(image.pixel(0, 0) == Qt.rgba(0, 0, 1)); - verify(image.pixel(199, 199) == Qt.rgba(1, 0, 0)); - } - } - - Box { - id: box - ParallelAnimation { - id: animation - ScaleAnimator { target: box; from: 1; to: 2.0; duration: 100; } - RotationAnimator { target: box; from: 0; to: 180; duration: 100; } - running: true - } - } -} diff --git a/tests/auto/qmltest/animators/tst_restart.qml b/tests/auto/qmltest/animators/tst_restart.qml deleted file mode 100644 index e029ca2059..0000000000 --- a/tests/auto/qmltest/animators/tst_restart.qml +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - property int restartCount: 5; - - TestCase { - id: testcase - name: "animators-restart" - when: root.restartCount == 0 && animation.running == false; - function test_endresult() { - compare(box.scale, 2); - } - } - - Box { - id: box - - ScaleAnimator { - id: animation - target: box; - from: 1; - to: 2.0; - duration: 100; - loops: 1 - running: false; - } - - Timer { - id: timer; - interval: 500 - running: true - repeat: true - onTriggered: { - animation.running = true; - --root.restartCount; - } - } - } -} diff --git a/tests/auto/qmltest/animators/tst_rotation.qml b/tests/auto/qmltest/animators/tst_rotation.qml deleted file mode 100644 index a91af92ab1..0000000000 --- a/tests/auto/qmltest/animators/tst_rotation.qml +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testCase - name: "animators-rotation" - when: box.rotation == 180 - function test_endresult() { - compare(box.rotationChangeCounter, 1); - var image = grabImage(root); - verify(image.pixel(50, 50) == Qt.rgba(0, 0, 1)); - } - } - - Box { - id: box - RotationAnimator { - id: animation - target: box - from: 0; - to: 180 - duration: 100 - easing.type: Easing.InOutBack - running: true - } - } -} diff --git a/tests/auto/qmltest/animators/tst_scale.qml b/tests/auto/qmltest/animators/tst_scale.qml deleted file mode 100644 index 402468079a..0000000000 --- a/tests/auto/qmltest/animators/tst_scale.qml +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testCase - name: "animators-scale" - when: box.scale == 2; - function test_endresult() { - compare(box.scaleChangeCounter, 1); - var image = grabImage(root); - verify(image.pixel(0, 0) == Qt.rgba(1, 0, 0)); - } - } - - Box { - id: box - - ScaleAnimator { - id: animation - target: box - from: 1; - to: 2.0 - duration: 100 - easing.type: Easing.InOutCubic - running: true - } - } -} diff --git a/tests/auto/qmltest/animators/tst_sequential.qml b/tests/auto/qmltest/animators/tst_sequential.qml deleted file mode 100644 index f021bbeb7a..0000000000 --- a/tests/auto/qmltest/animators/tst_sequential.qml +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testcase - name: "animators-parallel" - when: !animation.running - function test_endresult() { - compare(box.rotationChangeCounter, 1); - compare(box.scaleChangeCounter, 1); - compare(box.scale, 2); - compare(box.rotation, 180); - var image = grabImage(root); - compare(image.pixel(0, 0), Qt.rgba(0, 0, 1, 1)); - compare(image.pixel(199, 199), Qt.rgba(1, 0, 0, 1)); - } - } - - Box { - id: box - SequentialAnimation { - id: animation - ScaleAnimator { target: box; from: 1; to: 2.0; duration: 100; } - RotationAnimator { target: box; from: 0; to: 180; duration: 100; } - running: true - } - } -} diff --git a/tests/auto/qmltest/animators/tst_targetdestroyed.qml b/tests/auto/qmltest/animators/tst_targetdestroyed.qml deleted file mode 100644 index bf9efd2162..0000000000 --- a/tests/auto/qmltest/animators/tst_targetdestroyed.qml +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Gunnar Sletta -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testcase - name: "animators-targetdestroyed" - when: false - function test_endresult() { - verify(true, "Got here :)"); - } - } - - Rectangle { - id: box - width: 10 - height: 10 - color: "steelblue" - } - - YAnimator { - id: anim - target: box - from: 0; - to: 100 - duration: 100 - loops: Animation.Infinite - running: true - } - - SequentialAnimation { - running: true - PauseAnimation { duration: 150 } - ScriptAction { script: box.destroy(); } - PauseAnimation { duration: 50 } - ScriptAction { script: anim.destroy(); } - PauseAnimation { duration: 50 } - ScriptAction { script: testcase.when = true } - } -} diff --git a/tests/auto/qmltest/animators/tst_transformorigin.qml b/tests/auto/qmltest/animators/tst_transformorigin.qml deleted file mode 100644 index eed93b9843..0000000000 --- a/tests/auto/qmltest/animators/tst_transformorigin.qml +++ /dev/null @@ -1,152 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 300 - height: 300 - - Timer { - id: timer; - running: testCase.windowShown - interval: 1000 - repeat: false - onTriggered: triggered = true; - property bool triggered: false; - } - - TestCase { - id: testCase - name: "animators-transformorigin" - when: timer.triggered - function test_endresult() { - - var image = grabImage(root); - - var white = Qt.rgba(1, 1, 1); - var blue = Qt.rgba(0, 0, 1); - - - // topleft - verify(image.pixel(40, 40) == white); - verify(image.pixel(60, 40) == white); - verify(image.pixel(40, 60) == white); - verify(image.pixel(60, 60) == blue); - - // top - verify(image.pixel(140, 40) == white); - verify(image.pixel(160, 40) == white); - verify(image.pixel(140, 60) == blue); - verify(image.pixel(160, 60) == blue); - - // topright - verify(image.pixel(240, 40) == white); - verify(image.pixel(260, 40) == white); - verify(image.pixel(240, 60) == blue); - verify(image.pixel(260, 60) == white); - - - // left - verify(image.pixel(40, 140) == white); - verify(image.pixel(60, 140) == blue); - verify(image.pixel(40, 160) == white); - verify(image.pixel(60, 160) == blue); - - // center - verify(image.pixel(140, 140) == blue); - verify(image.pixel(160, 140) == blue); - verify(image.pixel(140, 160) == blue); - verify(image.pixel(160, 160) == blue); - - // right - verify(image.pixel(240, 140) == blue); - verify(image.pixel(260, 140) == white); - verify(image.pixel(240, 160) == blue); - verify(image.pixel(260, 160) == white); - - - // bottomleft - verify(image.pixel(40, 240) == white); - verify(image.pixel(60, 240) == blue); - verify(image.pixel(40, 260) == white); - verify(image.pixel(60, 260) == white); - - // bottom - verify(image.pixel(140, 240) == blue); - verify(image.pixel(160, 240) == blue); - verify(image.pixel(140, 260) == white); - verify(image.pixel(160, 260) == white); - - // bottomright - verify(image.pixel(240, 240) == blue); - verify(image.pixel(260, 240) == white); - verify(image.pixel(240, 260) == white); - verify(image.pixel(260, 260) == white); - - } - } - - property var origins: [Item.TopLeft, Item.Top, Item.TopRight, - Item.Left, Item.Center, Item.Right, - Item.BottomLeft, Item.Bottom, Item.BottomRight]; - - Grid { - anchors.fill: parent - rows: 3 - columns: 3 - - Repeater { - model: 9 - Item { - width: 100 - height: 100 - Rectangle { - id: box - color: "blue" - anchors.centerIn: parent - width: 10 - height: 10 - antialiasing: true; - - transformOrigin: root.origins[index]; - - ScaleAnimator { target: box; from: 1; to: 5.5; duration: 100; running: true; } - } - } - } - } - -} diff --git a/tests/auto/qmltest/animators/tst_transition.qml b/tests/auto/qmltest/animators/tst_transition.qml deleted file mode 100644 index b02238ef23..0000000000 --- a/tests/auto/qmltest/animators/tst_transition.qml +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testcase - name: "animators-transition" - when: box.scale == 2 - function test_endresult() { - compare(box.scaleChangeCounter, 1); - compare(box.scale, 2); - var image = grabImage(root); - verify(image.pixel(0, 0) == Qt.rgba(1, 0, 0)); - verify(image.pixel(199, 199) == Qt.rgba(0, 0, 1)); - } - } - - states: [ - State { - name: "one" - PropertyChanges { target: box; scale: 1 } - }, - State { - name: "two" - PropertyChanges { target: box; scale: 2 } - } - ] - state: "one" - - transitions: [ - Transition { - ScaleAnimator { duration: 100; } - } - ] - - Box { - id: box - } - - Timer { - interval: 100; - repeat: false - running: true - onTriggered: root.state = "two" - } -} diff --git a/tests/auto/qmltest/animators/tst_x.qml b/tests/auto/qmltest/animators/tst_x.qml deleted file mode 100644 index c462ee87d0..0000000000 --- a/tests/auto/qmltest/animators/tst_x.qml +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testCase - name: "animators-x" - when: box.x == 100 - function test_endresult() { - compare(box.xChangeCounter, 1); - var image = grabImage(root); - verify(image.pixel(100, 0) == Qt.rgba(1, 0, 0)); - verify(image.pixel(99, 0) == Qt.rgba(1, 1, 1)); // outside on the left - } - } - - Box { - id: box - - anchors.centerIn: undefined - - XAnimator { - id: animation - target: box - from: 0; - to: 100 - duration: 100 - running: true - } - } -} diff --git a/tests/auto/qmltest/animators/tst_y.qml b/tests/auto/qmltest/animators/tst_y.qml deleted file mode 100644 index 04487baee0..0000000000 --- a/tests/auto/qmltest/animators/tst_y.qml +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testCase - name: "animators-y" - when: box.y == 100 - function test_endresult() { - compare(box.yChangeCounter, 1); - var image = grabImage(root); - verify(image.pixel(0, 100) == Qt.rgba(1, 0, 0)); - verify(image.pixel(0, 99) == Qt.rgba(1, 1, 1)); // outside on the top - } - } - - Box { - id: box - - anchors.centerIn: undefined - - YAnimator { - id: animation - target: box - from: 0; - to: 100 - duration: 100 - running: true - } - } -} diff --git a/tests/auto/qmltest/animators/tst_zeroduration.qml b/tests/auto/qmltest/animators/tst_zeroduration.qml deleted file mode 100644 index 83ce235f42..0000000000 --- a/tests/auto/qmltest/animators/tst_zeroduration.qml +++ /dev/null @@ -1,35 +0,0 @@ -import QtQuick 2.2 -import QtTest 1.1 - -Item { - id: root; - width: 200 - height: 200 - - TestCase { - id: testCase - name: "animators-y" - when: box.y == 100 - function test_endresult() { - compare(box.yChangeCounter, 1); - var image = grabImage(root); - verify(image.pixel(0, 100) == Qt.rgba(1, 0, 0)); - verify(image.pixel(0, 99) == Qt.rgba(1, 1, 1)); // outside on the top - } - } - - Box { - id: box - - anchors.centerIn: undefined - - YAnimator { - id: animation - target: box - from: 0; - to: 100 - duration: 0 - running: true - } - } -} diff --git a/tests/auto/qmltest/item/tst_layerInPositioner.qml b/tests/auto/qmltest/item/tst_layerInPositioner.qml deleted file mode 100644 index 315ff0e5e0..0000000000 --- a/tests/auto/qmltest/item/tst_layerInPositioner.qml +++ /dev/null @@ -1,199 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jolla Ltd, author: -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -import QtQuick 2.4 -import QtTest 1.1 - -Item { - id: root; - width: 400 - height: 400 - - TestCase { - id: testCase - name: "transparentForPositioner" - when: windowShown - function test_endresult() { - var image = grabImage(root); - - // Row of red, green, blue and white box inside blue - // At 10,10, spanning 10x10 pixels each - verify(image.pixel(10, 10) == Qt.rgba(1, 0, 0, 1)); - verify(image.pixel(20, 10) == Qt.rgba(0, 1, 0, 1)); - verify(image.pixel(30, 10) == Qt.rgba(0, 0, 1, 1)); - - // Column of red, green, blue and white box inside blue - // At 10,30, spanning 10x10 pixels each - verify(image.pixel(10, 30) == Qt.rgba(1, 0, 0, 1)); - verify(image.pixel(10, 40) == Qt.rgba(0, 1, 0, 1)); - verify(image.pixel(10, 50) == Qt.rgba(0, 0, 1, 1)); - - // Flow of red, green, blue and white box inside blue - // At 30,30, spanning 10x10 pixels each, wrapping after two boxes - verify(image.pixel(30, 30) == Qt.rgba(1, 0, 0, 1)); - verify(image.pixel(40, 30) == Qt.rgba(0, 1, 0, 1)); - verify(image.pixel(30, 40) == Qt.rgba(0, 0, 1, 1)); - - // Flow of red, green, blue and white box inside blue - // At 100,10, spanning 10x10 pixels each, wrapping after two boxes - verify(image.pixel(60, 10) == Qt.rgba(1, 0, 0, 1)); - verify(image.pixel(70, 10) == Qt.rgba(0, 1, 0, 1)); - verify(image.pixel(60, 20) == Qt.rgba(0, 0, 1, 1)); - } - } - - Component { - id: greenPassThrough - ShaderEffect { - fragmentShader: - " - uniform lowp sampler2D source; - varying highp vec2 qt_TexCoord0; - void main() { - gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(0, 1, 0, 1); - } - " - } - } - - Row { - id: theRow - x: 10 - y: 10 - Rectangle { - width: 10 - height: 10 - color: "#ff0000" - layer.enabled: true - } - - Rectangle { - width: 10 - height: 10 - color: "#ffffff" - layer.enabled: true - layer.effect: greenPassThrough - } - - Rectangle { - id: blueInRow - width: 10 - height: 10 - color: "#0000ff" - } - } - - Column { - id: theColumn - x: 10 - y: 30 - Rectangle { - width: 10 - height: 10 - color: "#ff0000" - layer.enabled: true - } - - Rectangle { - width: 10 - height: 10 - color: "#ffffff" - layer.enabled: true - layer.effect: greenPassThrough - } - - Rectangle { - id: blueInColumn - width: 10 - height: 10 - color: "#0000ff" - } - } - - Flow { - id: theFlow - x: 30 - y: 30 - width: 20 - Rectangle { - width: 10 - height: 10 - color: "#ff0000" - layer.enabled: true - } - - Rectangle { - width: 10 - height: 10 - color: "#ffffff" - layer.enabled: true - layer.effect: greenPassThrough - } - - Rectangle { - id: blueInFlow - width: 10 - height: 10 - color: "#0000ff" - } - } - - Grid { - id: theGrid - x: 60 - y: 10 - columns: 2 - Rectangle { - width: 10 - height: 10 - color: "#ff0000" - layer.enabled: true - } - - Rectangle { - width: 10 - height: 10 - color: "#ffffff" - layer.enabled: true - layer.effect: greenPassThrough - } - - Rectangle { - id: blueInGrid - width: 10 - height: 10 - color: "#0000ff" - } - } - -} diff --git a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml deleted file mode 100644 index 5d65a1666c..0000000000 --- a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml +++ /dev/null @@ -1,152 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jolla Ltd, author: -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -import QtQuick 2.4 -import QtTest 1.1 - -Item { - id: root; - width: 400 - height: 400 - - TestCase { - id: testCase - name: "item-grabber" - when: imageOnDisk.ready && imageOnDiskSmall.ready && imageInCache.ready && imageInCacheSmall.ready - function test_endresult() { - var image = grabImage(root); - - // imageOnDisk at (0, 0) - (100x100) - compare(imageOnDisk.width, 100); - compare(imageOnDisk.height, 100); - verify(image.pixel(0, 0) === Qt.rgba(1, 0, 0, 1)); // Use verify because compare doesn't support colors (QTBUG-34878) - verify(image.pixel(99, 99) === Qt.rgba(0, 0, 1, 1)); - - // imageOnDiskSmall at (100, 0) - 50x50 - compare(imageOnDiskSmall.width, 50); - compare(imageOnDiskSmall.height, 50); - verify(image.pixel(100, 0) === Qt.rgba(1, 0, 0, 1)); - verify(image.pixel(149, 49) === Qt.rgba(0, 0, 1, 1)); - - // imageInCache at (0, 100) - 100x100 - compare(imageInCache.width, 100); - compare(imageInCache.height, 100); - verify(image.pixel(0, 100) === Qt.rgba(1, 0, 0, 1)); - verify(image.pixel(99, 199) === Qt.rgba(0, 0, 1, 1)); - - // imageInCacheSmall at (100, 100) - 50x50 - compare(imageInCacheSmall.width, 50); - compare(imageInCacheSmall.height, 50); - verify(image.pixel(100, 100) === Qt.rgba(1, 0, 0, 1)); - verify(image.pixel(149, 149) === Qt.rgba(0, 0, 1, 1)); - - // After all that has been going on, it should only have been called that one time.. - compare(imageOnDisk.callCount, 1); - } - - onWindowShownChanged: { - box.grabToImage(imageOnDisk.handleGrab); - box.grabToImage(imageOnDiskSmall.handleGrab, Qt.size(50, 50)); - box.grabToImage(imageInCache.handleGrab); - box.grabToImage(imageInCacheSmall.handleGrab, Qt.size(50, 50)); - } - - } - - Rectangle { - id: box - width: 100 - height: 100 - color: "red"; - - visible: false - - Rectangle { - anchors.bottom: parent.bottom; - anchors.right: parent.right; - width: 10 - height: 10 - color: "blue"; - } - } - - Image { - id: imageOnDisk - x: 0 - y: 0 - property int callCount: 0; - property bool ready: false; - function handleGrab(result) { - if (!result.saveToFile("image.png")) - print("Error: Failed to save image to disk..."); - source = "image.png"; - ready = true; - ++callCount; - } - } - - Image { - id: imageOnDiskSmall - x: 100 - y: 0 - property bool ready: false; - function handleGrab(result) { - if (!result.saveToFile("image_small.png")) - print("Error: Failed to save image to disk..."); - source = "image_small.png"; - ready = true; - } - } - - Image { - id: imageInCache - x: 0 - y: 100 - property bool ready: false; - function handleGrab(result) { - source = result.url; - ready = true; - } - } - - Image { - id: imageInCacheSmall - x: 100 - y: 100 - property bool ready: false; - function handleGrab(result) { - source = result.url; - ready = true; - } - } -} diff --git a/tests/auto/qmltest/listview/tst_listview.qml b/tests/auto/qmltest/listview/tst_listview.qml index 988d30f9a2..a3cae7fce2 100644 --- a/tests/auto/qmltest/listview/tst_listview.qml +++ b/tests/auto/qmltest/listview/tst_listview.qml @@ -272,6 +272,7 @@ Item { } function test_asyncLoaderCurrentIndexChange() { + skip("more details in QTBUG-53780") for (var i = 0; i < 500; i++) { asyncLoaderCurrentIndexListView.currentIndex = 0; asyncLoaderCurrentIndexListView.currentIndex = 1; @@ -284,6 +285,7 @@ Item { } function test_asyncListViewLoader() { + skip("more details in QTBUG-53780") for (var i = 0; i < 50; i++) { wait(10); asyncListViewLoaderView.currentIndex = 0; diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index 7662cb1687..175ed42c0f 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -11,5 +11,5 @@ importFiles.path = . DEPLOYMENT += importFiles DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 -CONFIG+=insignificant_test # QTBUG-33723 - +# Please do not make this test insignificant again, thanks. +# Just skip those unstable ones. See also QTBUG-33723. diff --git a/tests/auto/qmltest/window/tst_clickwindow.qml b/tests/auto/qmltest/window/tst_clickwindow.qml index bbe091990c..c3577be4dc 100644 --- a/tests/auto/qmltest/window/tst_clickwindow.qml +++ b/tests/auto/qmltest/window/tst_clickwindow.qml @@ -75,6 +75,8 @@ Item { when: windowShown function test_clickBothWindows() { + if (Qt.platform.os === "linux" || Qt.platform.os === "windows") + skip("more details in QTBUG-53785") mouseClick(ma, 20, 20); verify(ma.everClicked); mouseClick(ma2, 20, 20); -- cgit v1.2.3 From 7039db4885a143f8f1d363c9b71665e9160d3d55 Mon Sep 17 00:00:00 2001 From: Filippo Cucchetto Date: Thu, 2 Jun 2016 21:49:37 +0200 Subject: Revert removal of "Fixed MouseArea threshold with preventStealing" This reverts commit 9c8dab537819f0d999e680490c2d125b8836cbbb where commit e1400b5b4d8311769ad3b9f631479ee2b0271197 was removed due to the breakage of QtLocation tests. After some hours of debugging it seems that the problem in QtLocation was due to filtering of mouse move events in QDeclarativeGeoMapItemBase. See QTBUG-52075 Task-number: QTBUG-52534 Change-Id: I00f002c1d6f60f74a148b5a6ac2b9f63e93718a9 Reviewed-by: Paolo Angelelli Reviewed-by: Michal Klocek --- src/quick/items/qquickmousearea.cpp | 21 +++++++++++++-------- src/quick/items/qquickmousearea_p_p.h | 1 + .../quick/qquickmousearea/tst_qquickmousearea.cpp | 11 +++++++++++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index ef053abbd1..d66e55aa12 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -52,7 +52,7 @@ DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING) QQuickMouseAreaPrivate::QQuickMouseAreaPrivate() : enabled(true), scrollGestureEnabled(true), hovered(false), longPress(false), moved(false), stealMouse(false), doubleClick(false), preventStealing(false), - propagateComposedEvents(false), pressed(0) + propagateComposedEvents(false), overThreshold(false), pressed(0) #ifndef QT_NO_DRAGANDDROP , drag(0) #endif @@ -715,7 +715,7 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) curLocalPos = event->windowPos(); } - if (keepMouseGrab() && d->stealMouse && !d->drag->active()) + if (keepMouseGrab() && d->stealMouse && d->overThreshold && !d->drag->active()) d->drag->setActive(true); QPointF startPos = d->drag->target()->parentItem() @@ -741,16 +741,19 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) if (d->drag->active()) d->drag->target()->setPosition(dragPos); - if (!keepMouseGrab() - && (QQuickWindowPrivate::dragOverThreshold(dragPos.x() - startPos.x(), Qt::XAxis, event, d->drag->threshold()) - || QQuickWindowPrivate::dragOverThreshold(dragPos.y() - startPos.y(), Qt::YAxis, event, d->drag->threshold()))) { - setKeepMouseGrab(true); - d->stealMouse = true; - + if (!d->overThreshold && (QQuickWindowPrivate::dragOverThreshold(dragPos.x() - startPos.x(), Qt::XAxis, event, d->drag->threshold()) + || QQuickWindowPrivate::dragOverThreshold(dragPos.y() - startPos.y(), Qt::YAxis, event, d->drag->threshold()))) + { + d->overThreshold = true; if (d->drag->smoothed()) d->startScene = event->windowPos(); } + if (!keepMouseGrab() && d->overThreshold) { + setKeepMouseGrab(true); + d->stealMouse = true; + } + d->moved = true; } #endif @@ -767,6 +770,7 @@ void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event) { Q_D(QQuickMouseArea); d->stealMouse = false; + d->overThreshold = false; if (!d->enabled && !d->pressed) { QQuickItem::mouseReleaseEvent(event); } else { @@ -875,6 +879,7 @@ void QQuickMouseArea::ungrabMouse() d->pressed = 0; d->stealMouse = false; d->doubleClick = false; + d->overThreshold = false; setKeepMouseGrab(false); #ifndef QT_NO_DRAGANDDROP diff --git a/src/quick/items/qquickmousearea_p_p.h b/src/quick/items/qquickmousearea_p_p.h index 014729b651..f63c5f6092 100644 --- a/src/quick/items/qquickmousearea_p_p.h +++ b/src/quick/items/qquickmousearea_p_p.h @@ -86,6 +86,7 @@ public: bool doubleClick : 1; bool preventStealing : 1; bool propagateComposedEvents : 1; + bool overThreshold : 1; Qt::MouseButtons pressed; #ifndef QT_NO_DRAGANDDROP QQuickDrag *drag; diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 82c053d76a..9cdfd21f9c 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -79,6 +79,7 @@ private slots: void dragging_data() { acceptedButton_data(); } void dragging(); void dragSmoothed(); + void dragThreshold_data(); void dragThreshold(); void invalidDrag_data() { rejectedButton_data(); } void invalidDrag(); @@ -375,8 +376,17 @@ void tst_QQuickMouseArea::dragSmoothed() QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(100, 122)); } +void tst_QQuickMouseArea::dragThreshold_data() +{ + QTest::addColumn("preventStealing"); + QTest::newRow("without preventStealing") << false; + QTest::newRow("with preventStealing") << true; +} + void tst_QQuickMouseArea::dragThreshold() { + QFETCH(bool, preventStealing); + QQuickView window; QByteArray errorMessage; QVERIFY2(initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); @@ -386,6 +396,7 @@ void tst_QQuickMouseArea::dragThreshold() QVERIFY(window.rootObject() != 0); QQuickMouseArea *mouseRegion = window.rootObject()->findChild("mouseregion"); + mouseRegion->setPreventStealing(preventStealing); QQuickDrag *drag = mouseRegion->drag(); drag->setThreshold(5); -- cgit v1.2.3 From b6c64c2ef3b1fd314ea85be003fd88d43e36ee54 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 3 Jun 2016 13:44:30 +0200 Subject: Item: mention the actual type of the grab result Change-Id: I2c73a4bdfa43200c52df33cec573a8799e4fe5e0 Reviewed-by: Simon Hausmann --- src/quick/items/qquickitemgrabresult.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp index 939d19ea02..4e3e5570ee 100644 --- a/src/quick/items/qquickitemgrabresult.cpp +++ b/src/quick/items/qquickitemgrabresult.cpp @@ -321,7 +321,8 @@ QSharedPointer QQuickItem::grabToImage(const QSize &target * Grabs the item into an in-memory image. * * The grab happens asynchronously and the JavaScript function \a callback is - * invoked when the grab is completed. + * invoked when the grab is completed. The callback takes one argument, which + * is the result of the grab operation; an \l ItemGrabResult object. * * Use \a targetSize to specify the size of the target image. By default, the result * will have the same size as the item. -- cgit v1.2.3 From a8399c5440ef35f8feebcd086b5e46ce0000eea5 Mon Sep 17 00:00:00 2001 From: Valery Kotov Date: Wed, 17 Jun 2015 22:37:42 +0300 Subject: QML Engine: Support for "PATCH" method for XMLHTTPRequest Support for HTTP PATCH method was added to QQmlXMLHttpRequest. Tests for PATCH method in XMLHttpRequest were added. [ChangeLog][QtQml][QQmlXMLHttpRequest] QQmlXMLHttpRequest now supports the PATCH method in HTTP requests. Task-number: QTBUG-38175 Change-Id: Ib3c104b558f626bb63624e234f1362adcf6cbd4d Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlxmlhttprequest.cpp | 6 +- .../qml/qqmlxmlhttprequest/data/send_patch.expect | 17 ++++++ .../qml/qqmlxmlhttprequest/data/send_patch.qml | 68 ++++++++++++++++++++++ .../qml/qqmlxmlhttprequest/data/send_patch.reply | 3 + .../qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp | 21 +++++++ 5 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect create mode 100644 tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml create mode 100644 tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index e9644839d1..e2784103b0 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1235,7 +1235,8 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url) } else if (m_method == QLatin1String("DELETE")) { m_network = networkAccessManager()->deleteResource(request); } else if ((m_method == QLatin1String("OPTIONS")) || - m_method == QLatin1String("PROPFIND")) { + m_method == QLatin1String("PROPFIND") || + m_method == QLatin1String("PATCH")) { QBuffer *buffer = new QBuffer; buffer->setData(m_data); buffer->open(QIODevice::ReadOnly); @@ -1735,7 +1736,8 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx) method != QLatin1String("POST") && method != QLatin1String("DELETE") && method != QLatin1String("OPTIONS") && - method != QLatin1String("PROPFIND")) + method != QLatin1String("PROPFIND") && + method != QLatin1String("PATCH")) V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Unsupported HTTP method type"); // Argument 1 - URL diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect new file mode 100644 index 0000000000..8c13977462 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect @@ -0,0 +1,17 @@ +PATCH /qqmlxmlhttprequest.cpp HTTP/1.1 +Accept-Language: en-US +If-Match: "ETagNumber" +Content-Type: application/example +Content-Length: 247 +Connection: Keep-Alive +Accept-Encoding: gzip, deflate +User-Agent: Mozilla/5.0 +Host: {{ServerHostUrl}} + +--- a/qqmlxmlhttprequest.cpp ++++ b/qqmlxmlhttprequest.cpp +@@ -1238,11 +1238,13 @@ +- } else if (m_method == QLatin1String("OPTIONS")) { ++ } else if (m_method == QLatin1String("OPTIONS") || ++ (m_method == QLatin1String("PATCH"))) { + diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml new file mode 100644 index 0000000000..2abf1c60a8 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Canonical Limited and/or its subsidiary(-ies). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +import QtQuick 2.0 + +QtObject { + property string url + + property bool dataOK: false + property bool headerOK: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + x.open("PATCH", url); + x.setRequestHeader("Accept-Language","en-US"); + x.setRequestHeader("If-Match","\"ETagNumber\""); + + // Test to the end + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.HEADERS_RECEIVED) { + headerOK = (x.getResponseHeader("Content-Location") == "/qqmlxmlhttprequest.cpp") && + (x.getResponseHeader("ETag") == "\"ETagNumber\"") && + (x.status == "204"); + } else if (x.readyState == XMLHttpRequest.DONE) { + dataOK = (x.responseText === ""); + } + } + + var body = "--- a/qqmlxmlhttprequest.cpp\n" + + "+++ b/qqmlxmlhttprequest.cpp\n" + + "@@ -1238,11 +1238,13 @@\n" + + "- } else if (m_method == QLatin1String(\"OPTIONS\")) {\n" + + "+ } else if (m_method == QLatin1String(\"OPTIONS\") ||\n" + + "+ (m_method == QLatin1String(\"PATCH\"))) {\n" + + x.send(body); + } +} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply new file mode 100644 index 0000000000..cece41ced1 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply @@ -0,0 +1,3 @@ +HTTP/1.1 204 No Content +Content-Location: /qqmlxmlhttprequest.cpp +ETag: "ETagNumber" diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 425e12677f..1ce07ecdab 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -71,6 +71,7 @@ private slots: void send_withdata_data(); void send_options(); void send_options_data(); + void send_patch(); void abort(); void abort_unsent(); void abort_opened(); @@ -639,6 +640,26 @@ void tst_qqmlxmlhttprequest::send_options_data() QTest::newRow("OPTIONS (with data)") << "testdocument.html" << "send_data.10.expect" << "send_data.9.qml" << "send_data.2.reply"; } +void tst_qqmlxmlhttprequest::send_patch() +{ + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("send_patch.expect"), + testFileUrl("send_patch.reply"), + // the content of response file will be ignored due to 204 status code + testFileUrl("testdocument.html"))); + + QQmlComponent component(&engine, testFileUrl("send_patch.qml")); + QScopedPointer object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); + object->setProperty("url", server.urlString("/qqmlxmlhttprequest.cpp")); + component.completeCreate(); + + QTRY_VERIFY(object->property("dataOK").toBool()); + QTRY_VERIFY(object->property("headerOK").toBool()); +} + + // Test abort() has no effect in unsent state void tst_qqmlxmlhttprequest::abort_unsent() { -- cgit v1.2.3 From 23ec4ac8ba5450b79dbef857f0062bfb068e98d3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 3 Jun 2016 11:22:35 +0200 Subject: Minor refcounting cleaning Replace manual reference counting with automated counting, which is less error prone. Change-Id: I6a133bf9610f0d789f745b3cc1f0016e670a9525 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlcontext.cpp | 5 +---- src/qml/qml/qqmlcontext_p.h | 2 +- src/qml/qml/qqmlobjectcreator.cpp | 1 - src/qml/qml/qqmltypeloader.cpp | 4 ---- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index b3081ddbe6..6621f48e20 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -524,7 +524,7 @@ QQmlContextData::QQmlContextData(QQmlContext *ctxt) : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false), publicContext(ctxt), activeVMEData(0), componentObjectIndex(-1), - contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), + contextObject(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), componentAttached(0) { @@ -628,9 +628,6 @@ void QQmlContextData::destroy() } contextGuards = 0; - if (imports) - imports->release(); - delete [] idValues; if (isInternal) diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index 05ce39401e..62cd3d4877 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -174,7 +174,7 @@ public: QString urlString() const; // List of imports that apply to this context - QQmlTypeNameCache *imports; + QQmlRefPointer imports; // My children QQmlContextData *childContexts; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 80e0fa3595..9e2040469b 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -167,7 +167,6 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI context = new QQmlContextData; context->isInternal = true; context->imports = compilationUnit->importCache; - context->imports->addref(); context->initFromTypeCompilationUnit(compilationUnit, subComponentIndex); context->setParent(parentContext); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index b2f4c6749d..87036d9ea4 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2552,10 +2552,6 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent ctxt->importedScripts = effectiveCtxt->importedScripts; } - if (ctxt->imports) { - ctxt->imports->addref(); - } - if (effectiveCtxt) { ctxt->setParent(effectiveCtxt, true); } else { -- cgit v1.2.3 From 4018a91d2f17f4c8d0fe17a788fdd8453b386606 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 3 Jun 2016 11:23:25 +0200 Subject: Minor const cleanup The unit data of QV4::CompiledData::CompilationUnit is read-only, which we should "enforce" with a const, especially as in the future it may come from a read-only mmap'ed chunk of memory. Change-Id: I58dde9fe2494d3cfa52cdc9f612c7fa90b98aa26 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 2 +- src/qml/compiler/qv4compileddata_p.h | 2 +- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 0f81e8b0d9..6cb462cda3 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -82,7 +82,7 @@ CompilationUnit::~CompilationUnit() { unlink(); if (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) - free(data); + free(const_cast(data)); data = 0; } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 26a27d79c8..b33882ba97 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -656,7 +656,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount virtual ~CompilationUnit(); #endif - Unit *data; + const Unit *data; // Called only when building QML, when we build the header for JS first and append QML data virtual QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index fde861ce42..e800d64471 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2074,7 +2074,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() QQmlTypeData *td = eng->typeLoader.getType(url); Q_ASSERT(td); - QV4::CompiledData::Unit *qmlUnit = td->compilationUnit()->data; + const QV4::CompiledData::Unit *qmlUnit = td->compilationUnit()->data; Q_ASSERT(qmlUnit); const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject); QCOMPARE(qmlUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); -- cgit v1.2.3 From e2fd32cf30d0b5e947ad6c068781507988528c64 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 3 Jun 2016 11:38:31 +0200 Subject: More reference counting cleanups Replace more uses of manual reference counting with automatic counting. Change-Id: Id3ce13814ac7f13d62562be21e4d59119d099944 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 2 -- src/qml/compiler/qv4compileddata.cpp | 8 -------- src/qml/compiler/qv4compileddata_p.h | 7 ++++--- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index cfb5df5a60..e6ba827993 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -91,7 +91,6 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() return nullptr; } ref->compilationUnit = resolvedType->typeData->compilationUnit(); - ref->compilationUnit->addref(); } else if (qmlType) { ref->type = qmlType; Q_ASSERT(ref->type); @@ -118,7 +117,6 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() recordError(cacheError); return nullptr; } - ref->typePropertyCache->addref(); } } ref->majorVersion = resolvedType->majorVersion; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6cb462cda3..df29930aad 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -187,13 +187,6 @@ void CompilationUnit::unlink() importCache = nullptr; - for (auto resolvedType = resolvedTypes.begin(), end = resolvedTypes.end(); - resolvedType != end; ++resolvedType) { - if ((*resolvedType)->compilationUnit) - (*resolvedType)->compilationUnit->release(); - if ((*resolvedType)->typePropertyCache) - (*resolvedType)->typePropertyCache->release(); - } qDeleteAll(resolvedTypes); resolvedTypes.clear(); @@ -367,7 +360,6 @@ QQmlPropertyCache *CompilationUnit::ResolvedTypeReference::createPropertyCache(Q return typePropertyCache; } else if (type) { typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject()); - typePropertyCache->addref(); return typePropertyCache; } else { return compilationUnit->rootPropertyCache(); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index b33882ba97..744d2bb8da 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -64,6 +64,7 @@ #include #ifndef V4_BOOTSTRAP #include +#include #endif QT_BEGIN_NAMESPACE @@ -699,15 +700,15 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount struct ResolvedTypeReference { ResolvedTypeReference() - : type(0), typePropertyCache(0), compilationUnit(0) + : type(0) , majorVersion(0) , minorVersion(0) , isFullyDynamicType(false) {} QQmlType *type; - QQmlPropertyCache *typePropertyCache; - QV4::CompiledData::CompilationUnit *compilationUnit; + QQmlRefPointer typePropertyCache; + QQmlRefPointer compilationUnit; int majorVersion; int minorVersion; -- cgit v1.2.3 From 8ea3763a22d4ae114eee0063ef1c3ce31ac1b44d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 3 Jun 2016 12:57:25 +0200 Subject: Use automatic refcounting for the compilation unit in QQmlComponentPrivate and the incubator Change-Id: I70d609ce282a537b67a5e7c01c12d9ce65995133 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlcomponent.cpp | 11 +---------- src/qml/qml/qqmlcomponent_p.h | 4 ++-- src/qml/qml/qqmlincubator.cpp | 11 +++-------- src/qml/qml/qqmlincubator_p.h | 2 +- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 282f915141..e615318636 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -339,8 +339,6 @@ void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data) if (!compilationUnit) { Q_ASSERT(data->isError()); state.errors = data->errors(); - } else { - compilationUnit->addref(); } data->release(); @@ -354,10 +352,7 @@ void QQmlComponentPrivate::clear() typeData = 0; } - if (compilationUnit) { - compilationUnit->release(); - compilationUnit = 0; - } + compilationUnit = nullptr; } /*! @@ -391,8 +386,6 @@ QQmlComponent::~QQmlComponent() d->typeData->unregisterCallback(d); d->typeData->release(); } - if (d->compilationUnit) - d->compilationUnit->release(); } /*! @@ -567,7 +560,6 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::CompiledData::CompilationU { Q_D(QQmlComponent); d->compilationUnit = compilationUnit; - compilationUnit->addref(); d->start = start; d->url = compilationUnit->url(); d->progress = 1.0; @@ -1048,7 +1040,6 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->engine); p->compilationUnit = d->compilationUnit; - p->compilationUnit->addref(); p->enginePriv = enginePriv; p->creator.reset(new QQmlObjectCreator(contextData, d->compilationUnit, d->creationContext, p.data())); p->subComponentToCreate = d->start; diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index bb6c6d1c05..be69986117 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -79,7 +79,7 @@ class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public public: QQmlComponentPrivate() - : typeData(0), progress(0.), start(-1), compilationUnit(0), engine(0), creationContext(0), depthIncreased(false) {} + : typeData(0), progress(0.), start(-1), engine(0), creationContext(0), depthIncreased(false) {} void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous); @@ -97,7 +97,7 @@ public: qreal progress; int start; - QV4::CompiledData::CompilationUnit *compilationUnit; + QQmlRefPointer compilationUnit; struct ConstructionState { ConstructionState() diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index c52b5014ca..7494d6a633 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -131,7 +131,7 @@ QQmlIncubationController *QQmlEngine::incubationController() const QQmlIncubatorPrivate::QQmlIncubatorPrivate(QQmlIncubator *q, QQmlIncubator::IncubationMode m) : q(q), status(QQmlIncubator::Null), mode(m), isAsynchronous(false), progress(Execute), - result(0), enginePriv(0), compilationUnit(0), waitingOnMe(0) + result(0), enginePriv(0), waitingOnMe(0) { } @@ -142,18 +142,13 @@ QQmlIncubatorPrivate::~QQmlIncubatorPrivate() void QQmlIncubatorPrivate::clear() { + compilationUnit = nullptr; if (next.isInList()) { next.remove(); - Q_ASSERT(compilationUnit); - compilationUnit->release(); - compilationUnit = 0; enginePriv->incubatorCount--; QQmlIncubationController *controller = enginePriv->incubationController; if (controller) controller->incubatingObjectCountChanged(enginePriv->incubatorCount); - } else if (compilationUnit) { - compilationUnit->release(); - compilationUnit = 0; } enginePriv = 0; if (!rootContext.isNull()) { @@ -570,7 +565,7 @@ void QQmlIncubator::clear() d->clear(); - Q_ASSERT(d->compilationUnit == 0); + Q_ASSERT(d->compilationUnit.isNull()); Q_ASSERT(d->waitingOnMe.data() == 0); Q_ASSERT(d->waitingFor.isEmpty()); diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index 7c6bc15404..ecf3b6d2ca 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -85,7 +85,7 @@ public: QPointer result; QQmlGuardedContextData rootContext; QQmlEnginePrivate *enginePriv; - QV4::CompiledData::CompilationUnit *compilationUnit; + QQmlRefPointer compilationUnit; QScopedPointer creator; int subComponentToCreate; QQmlVMEGuard vmeGuard; -- cgit v1.2.3 From bfa87b7bd8f8fb94889fd99ee413e69bc17f9e81 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 3 Jun 2016 13:44:24 +0200 Subject: Clean up property cache reference counting in QQmlVMEMetaObject When the intercepting functionality of the VME meta-object was split into the QQmlInterceptorMetaObject the property cache was moved as a member but the manual refcounting remained in the QQmlVMEMetaObject constructor and destructor. That's fragile and potential future sub-classes of the interceptor would have to do the same refcounting. Instead let's just store a QQmlRefPointer in the right place. Similar logic applies to the QQmlDesignerMetaObject, which had a copy of the property cache for now aparent reason. Change-Id: I7f6d3917fc5ef1c4beb065525be10bb9c0fadcf3 Reviewed-by: Thomas Hartmann Reviewed-by: Lars Knoll --- src/qml/qml/qqmlvmemetaobject.cpp | 4 ---- src/qml/qml/qqmlvmemetaobject_p.h | 2 +- src/quick/designer/qqmldesignermetaobject.cpp | 19 ++++++------------- src/quick/designer/qqmldesignermetaobject_p.h | 1 - 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 8697a291e2..6907b26cd8 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -319,8 +319,6 @@ QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj, ctxt(QQmlData::get(obj, true)->outerContext), aliasEndpoints(0), compilationUnit(qmlCompilationUnit), compiledObject(0) { - cache->addref(); - QQmlData::get(obj)->hasVMEMetaObject = true; if (compilationUnit && qmlObjectId >= 0) { @@ -345,8 +343,6 @@ QQmlVMEMetaObject::~QQmlVMEMetaObject() delete [] aliasEndpoints; qDeleteAll(varObjectGuards); - - cache->release(); } QV4::MemberData *QQmlVMEMetaObject::propertyAndMethodStorageAsMemberData() diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index 41f454eb62..5aa141d026 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -111,7 +111,7 @@ protected: public: QObject *object; - QQmlPropertyCache *cache; + QQmlRefPointer cache; QBiPointer parent; QQmlPropertyValueInterceptor *interceptors; diff --git a/src/quick/designer/qqmldesignermetaobject.cpp b/src/quick/designer/qqmldesignermetaobject.cpp index 23a29d5831..5e897218c5 100644 --- a/src/quick/designer/qqmldesignermetaobject.cpp +++ b/src/quick/designer/qqmldesignermetaobject.cpp @@ -120,13 +120,7 @@ void QQmlDesignerMetaObject::init(QObject *object, QQmlEngine *engine) QObjectPrivate *op = QObjectPrivate::get(object); op->metaObject = this; - m_cache = QQmlEnginePrivate::get(engine)->cache(this); - - if (m_cache != cache) { - m_cache->addref(); - cache->release(); - cache = m_cache; - } + cache = QQmlEnginePrivate::get(engine)->cache(this); nodeInstanceMetaObjectList.insert(this, true); hasAssignedMetaObjectData = true; @@ -135,8 +129,7 @@ void QQmlDesignerMetaObject::init(QObject *object, QQmlEngine *engine) QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engine) : QQmlVMEMetaObject(object, cacheForObject(object, engine), /*qml compilation unit*/nullptr, /*qmlObjectId*/-1), m_context(engine->contextForObject(object)), - m_data(new MetaPropertyData), - m_cache(0) + m_data(new MetaPropertyData) { init(object, engine); @@ -146,8 +139,8 @@ QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engi cache->setParent(ddata->propertyCache); cache->invalidate(engine, this); ddata->propertyCache->release(); - ddata->propertyCache = m_cache; - m_cache->addref(); + ddata->propertyCache = cache; + ddata->propertyCache->addref(); } } @@ -168,9 +161,9 @@ void QQmlDesignerMetaObject::createNewDynamicProperty(const QString &name) Q_UNUSED(id); //Updating cache - QQmlPropertyCache *oldParent = m_cache->parent(); + QQmlPropertyCache *oldParent = cache->parent(); QQmlEnginePrivate::get(m_context->engine())->cache(this)->invalidate(m_context->engine(), this); - m_cache->setParent(oldParent); + cache->setParent(oldParent); QQmlProperty property(myObject(), name, m_context); Q_ASSERT(property.isValid()); diff --git a/src/quick/designer/qqmldesignermetaobject_p.h b/src/quick/designer/qqmldesignermetaobject_p.h index a5402ccbc4..01512f6af0 100644 --- a/src/quick/designer/qqmldesignermetaobject_p.h +++ b/src/quick/designer/qqmldesignermetaobject_p.h @@ -101,7 +101,6 @@ private: QQmlOpenMetaObjectType *m_type; QScopedPointer m_data; //QAbstractDynamicMetaObject *m_parent; - QQmlPropertyCache *m_cache; friend class QQuickDesignerSupportProperties; }; -- cgit v1.2.3 From 6d63e3ba09954ce7b56b4b24432bd036e636b164 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 3 Jun 2016 16:14:21 +0200 Subject: Fix bug in SQL database integration with null values Since commit 91d6a63ab317817990c3b2306860adbd8916cca4 a null JS value is mapped to a QVariant(VoidStar) to properly create null JS values when converting back again. However that broke the binding of values in the SQL database, where it ended up mapping null to an empty string. [ChangeLog][QtQml] Fix mapping of null JS values to null SQL values instead of empty strings. Task-number: QTBUG-53412 Change-Id: Icf1fea4674e9dd8bb5313e3770ed2d3f99849987 Reviewed-by: Robin Burchell --- src/imports/localstorage/plugin.cpp | 18 ++++++++++++---- tests/auto/qml/qqmlsqldatabase/data/nullvalues.js | 24 ++++++++++++++++++++++ .../qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp | 3 ++- 3 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 tests/auto/qml/qqmlsqldatabase/data/nullvalues.js diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index b8d7030763..d32bb0069d 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -257,6 +257,15 @@ static ReturnedValue qmlsqldatabase_rows_item(CallContext *ctx) return qmlsqldatabase_rows_index(r, scope.engine, ctx->argc() ? ctx->args()[0].toUInt32() : 0); } +static QVariant toSqlVariant(QV4::ExecutionEngine *engine, const QV4::ScopedValue &value) +{ + // toVariant() maps a null JS value to QVariant(VoidStar), but the SQL module + // expects a null variant. (this is because of QTBUG-40880) + if (value->isNull()) + return QVariant(); + return engine->toVariant(value, /*typehint*/-1); +} + static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) { QV4::Scope scope(ctx); @@ -287,8 +296,9 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) ScopedArrayObject array(scope, values); quint32 size = array->getLength(); QV4::ScopedValue v(scope); - for (quint32 ii = 0; ii < size; ++ii) - query.bindValue(ii, scope.engine->toVariant((v = array->getIndexed(ii)), -1)); + for (quint32 ii = 0; ii < size; ++ii) { + query.bindValue(ii, toSqlVariant(scope.engine, (v = array->getIndexed(ii)))); + } } else if (values->as()) { ScopedObject object(scope, values); ObjectIterator it(scope, object, ObjectIterator::WithProtoChain|ObjectIterator::EnumerableOnly); @@ -298,7 +308,7 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) key = it.nextPropertyName(val); if (key->isNull()) break; - QVariant v = scope.engine->toVariant(val, -1); + QVariant v = toSqlVariant(scope.engine, val); if (key->isString()) { query.bindValue(key->stringValue()->toQString(), v); } else { @@ -307,7 +317,7 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) } } } else { - query.bindValue(0, scope.engine->toVariant(values, -1)); + query.bindValue(0, toSqlVariant(scope.engine, values)); } } if (query.exec()) { diff --git a/tests/auto/qml/qqmlsqldatabase/data/nullvalues.js b/tests/auto/qml/qqmlsqldatabase/data/nullvalues.js new file mode 100644 index 0000000000..322a7aea03 --- /dev/null +++ b/tests/auto/qml/qqmlsqldatabase/data/nullvalues.js @@ -0,0 +1,24 @@ +.import QtQuick.LocalStorage 2.0 as Sql + +function test() { + var db = Sql.LocalStorage.openDatabaseSync("QmlTestDB-nullvalues", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; + + db.transaction( + function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS NullValues(salutation TEXT, salutee TEXT)'); + tx.executeSql('INSERT INTO NullValues VALUES(?, ?)', [ 'hello', null ]); + var firstRow = tx.executeSql("SELECT * FROM NullValues").rows.item(0); + if (firstRow.salutation !== "hello") + return + if (firstRow.salutee === "") { + r = "wrong_data_type" + return + } + if (firstRow.salutee === null) + r = "passed"; + } + ); + + return r; +} diff --git a/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp b/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp index b5b77ad10b..debafa3fe4 100644 --- a/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp +++ b/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp @@ -127,7 +127,7 @@ void tst_qqmlsqldatabase::checkDatabasePath() QVERIFY(engine->offlineStoragePath().contains("OfflineStorage")); } -static const int total_databases_created_by_tests = 12; +static const int total_databases_created_by_tests = 13; void tst_qqmlsqldatabase::testQml_data() { QTest::addColumn("jsfile"); // The input file @@ -149,6 +149,7 @@ void tst_qqmlsqldatabase::testQml_data() QTest::newRow("error-outsidetransaction") << "error-outsidetransaction.js"; // reuse above QTest::newRow("reopen1") << "reopen1.js"; QTest::newRow("reopen2") << "reopen2.js"; // re-uses above DB + QTest::newRow("null-values") << "nullvalues.js"; // If you add a test, you should usually use a new database in the // test - in which case increment total_databases_created_by_tests above. -- cgit v1.2.3 From 8c8ec31b7ac79b10b5db0825ee338fc95e24a76f Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Sun, 29 May 2016 21:33:59 +0200 Subject: Add QJSEngine::newQMetaObject QJSEngine::newQMetaObject let us expose QMetaObject to the QJSEngine, allowing to construct QObjects instance from javascript. Additionally, enums values are exposed as property of the QMetaObject wrapper. (The engine takes ownership of the created objects) Change-Id: I5428d4b7061cceacfa89f51e703dce3379b2c329 Reviewed-by: Simon Hausmann --- src/qml/jsapi/qjsengine.cpp | 43 +++++++ src/qml/jsapi/qjsengine.h | 8 ++ src/qml/jsapi/qjsvalue.cpp | 36 ++++++ src/qml/jsapi/qjsvalue.h | 2 + src/qml/jsruntime/qv4qobjectwrapper.cpp | 190 +++++++++++++++++++++++++++-- src/qml/jsruntime/qv4qobjectwrapper_p.h | 28 +++++ src/qml/qml/qqmlpropertycache.cpp | 79 ++++++++---- src/qml/qml/qqmlpropertycache_p.h | 20 ++- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 99 +++++++++++++++ 9 files changed, 467 insertions(+), 38 deletions(-) diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 09e8bbda55..4404a5d79f 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -166,6 +166,17 @@ Q_DECLARE_METATYPE(QList) properties of the proxy object. No binding code is needed because it is done dynamically using the Qt meta object system. + Use newQMetaObject() to wrap a QMetaObject; this gives you a + "script representation" of a QObject-based class. newQMetaObject() + returns a proxy script object; enum values of the class are available + as properties of the proxy object. + + Constructors exposed to the meta-object system ( using Q_INVOKABLE ) can be + called from the script to create a new QObject instance with + JavaScriptOwnership. + + + \snippet code/src_script_qjsengine.cpp 5 \section1 Extensions @@ -510,6 +521,38 @@ QJSValue QJSEngine::newQObject(QObject *object) return QJSValue(v4, v->asReturnedValue()); } +/*! + \since 5.8 + + Creates a JavaScript object that wraps the given QMetaObject + The metaObject must outlive the script engine. It is recommended to only + use this method with static metaobjects. + + + When called as a constructor, a new instance of the class will be created. + Only constructors exposed by Q_INVOKABLE will be visible from the script engine. + + \sa newQObject() +*/ + +QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) { + Q_D(QJSEngine); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(d); + QV4::Scope scope(v4); + QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(v4, metaObject)); + return QJSValue(v4, v->asReturnedValue()); +} + +/*! \fn QJSValue QJSEngine::newQMetaObject() + + \since 5.8 + Creates a JavaScript object that wraps the static QMetaObject associated + with class \c{T}. + + \sa newQObject() +*/ + + /*! Returns this engine's Global Object. diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 6ecd0c7ec0..41c4b81270 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -74,6 +74,14 @@ public: QJSValue newQObject(QObject *object); + QJSValue newQMetaObject(const QMetaObject* metaObject); + + template + QJSValue newQMetaObject() + { + return newQMetaObject(&T::staticMetaObject); + } + template inline QJSValue toScriptValue(const T &value) { diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 4860908bd3..44746b8c2b 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -1233,6 +1233,28 @@ QObject *QJSValue::toQObject() const return wrapper->object(); } +/*! + \since 5.8 + + * If this QJSValue is a QMetaObject, returns the QMetaObject pointer + * that the QJSValue represents; otherwise, returns 0. + * + * \sa isQMetaObject() + */ +const QMetaObject *QJSValue::toQMetaObject() const +{ + QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this); + if (!engine) + return 0; + QV4::Scope scope(engine); + QV4::Scoped wrapper(scope, QJSValuePrivate::getValue(this)); + if (!wrapper) + return 0; + + return wrapper->metaObject(); +} + + /*! Returns a QDateTime representation of this value, in local time. If this QJSValue is not a date, or the value of the date is NaN @@ -1286,4 +1308,18 @@ bool QJSValue::isQObject() const return val && val->as() != 0; } +/*! + \since 5.8 + + Returns true if this QJSValue is a QMetaObject; otherwise returns + false. + + \sa toQMetaObject(), QJSEngine::newQMetaObject() +*/ +bool QJSValue::isQMetaObject() const +{ + QV4::Value *val = QJSValuePrivate::getValue(this); + return val && val->as() != 0; +} + QT_END_NAMESPACE diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h index e207e1b099..ab20a2607d 100644 --- a/src/qml/jsapi/qjsvalue.h +++ b/src/qml/jsapi/qjsvalue.h @@ -98,6 +98,7 @@ public: bool isUndefined() const; bool isVariant() const; bool isQObject() const; + bool isQMetaObject() const; bool isObject() const; bool isDate() const; bool isRegExp() const; @@ -111,6 +112,7 @@ public: bool toBool() const; QVariant toVariant() const; QObject *toQObject() const; + const QMetaObject *toQMetaObject() const; QDateTime toDateTime() const; bool equals(const QJSValue &other) const; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index c888f1e44c..44c7fc9823 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -75,6 +75,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -1114,7 +1115,8 @@ private: } static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, int returnType, int argCount, - int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) + int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs, + QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { if (argCount > 0) { // Convert all arguments. @@ -1126,7 +1128,7 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index for (int ii = 0; ii < args.count(); ++ii) argData[ii] = args[ii].dataPtr(); - object.metacall(QMetaObject::InvokeMetaMethod, index, argData.data()); + object.metacall(callType, index, argData.data()); return args[0].toValue(engine); @@ -1137,14 +1139,14 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index void *args[] = { arg.dataPtr() }; - object.metacall(QMetaObject::InvokeMetaMethod, index, args); + object.metacall(callType, index, args); return arg.toValue(engine); } else { void *args[] = { 0 }; - object.metacall(QMetaObject::InvokeMetaMethod, index, args); + object.metacall(callType, index, args); return Encode::undefined(); } @@ -1354,7 +1356,8 @@ static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object, } static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data, - QV4::ExecutionEngine *engine, QV4::CallData *callArgs) + QV4::ExecutionEngine *engine, QV4::CallData *callArgs, + QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { QByteArray unknownTypeError; @@ -1371,7 +1374,10 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ int *args = 0; QVarLengthArray dummy; - args = object.methodParameterTypes(data.coreIndex, dummy, &unknownTypeError); + if (data.isConstructor()) + args = static_cast(object).constructorParameterTypes(data.coreIndex, dummy, &unknownTypeError); + else + args = object.methodParameterTypes(data.coreIndex, dummy, &unknownTypeError); if (!args) { QString typeName = QString::fromLatin1(unknownTypeError); @@ -1384,11 +1390,11 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ return engine->throwError(error); } - return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs); + return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs, callType); } else { - return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs); + return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs, callType); } } @@ -1407,7 +1413,8 @@ Resolve the overloaded method to call. The algorithm works conceptually like th score is constructed by adding the matchScore() result for each of the parameters. */ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData &data, - QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache) + QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache, + QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { int argumentCount = callArgs->argc; @@ -1457,7 +1464,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const } while ((attempt = RelatedMethod(object, attempt, dummy, propertyCache)) != 0); if (best.isValid()) { - return CallPrecise(object, best, engine, callArgs); + return CallPrecise(object, best, engine, callArgs, callType); } else { QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); const QQmlPropertyData *candidate = &data; @@ -1884,6 +1891,169 @@ void QObjectMethod::markObjects(Heap::Base *that, ExecutionEngine *e) DEFINE_OBJECT_VTABLE(QObjectMethod); + +Heap::QMetaObjectWrapper::QMetaObjectWrapper(const QMetaObject *metaObject) + : metaObject(metaObject) { + +} + +void Heap::QMetaObjectWrapper::ensureConstructorsCache() { + + const int count = metaObject->constructorCount(); + if (constructors.size() != count) { + constructors.clear(); + constructors.reserve(count); + + for (int i = 0; i < count; ++i) { + QMetaMethod method = metaObject->constructor(i); + QQmlPropertyData d; + d.load(method); + d.coreIndex = i; + constructors << d; + } + } +} + + +ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) { + + QV4::Scope scope(engine); + Scoped mo(scope, engine->memoryManager->allocObject(metaObject)->asReturnedValue()); + mo->init(engine); + return mo->asReturnedValue(); +} + +void QMetaObjectWrapper::init(ExecutionEngine *) { + const QMetaObject & mo = *d()->metaObject; + + for (int i = 0; i < mo.enumeratorCount(); i++) { + QMetaEnum Enum = mo.enumerator(i); + for (int k = 0; k < Enum.keyCount(); k++) { + const char* key = Enum.key(k); + const int value = Enum.value(k); + defineReadonlyProperty(QLatin1String(key), Primitive::fromInt32(value)); + } + } +} + +ReturnedValue QMetaObjectWrapper::construct(const Managed *m, CallData *callData) +{ + const QMetaObjectWrapper *This = static_cast(m); + return This->constructInternal(callData); +} + +ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const { + + d()->ensureConstructorsCache(); + + ExecutionEngine *v4 = engine(); + const QMetaObject* mo = d()->metaObject; + if (d()->constructors.isEmpty()) { + return v4->throwTypeError(QStringLiteral("%1 has no invokable constructor") + .arg(QLatin1String(mo->className()))); + } + + Scope scope(v4); + Scoped object(scope); + + if (d()->constructors.size() == 1) { + object = callConstructor(d()->constructors.first(), v4, callData); + } + else { + object = callOverloadedConstructor(v4, callData); + } + Scoped metaObject(scope, this); + object->defineDefaultProperty(v4->id_constructor(), metaObject); + object->setPrototype(const_cast(this)); + return object.asReturnedValue(); + +} + +ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const { + + const QMetaObject* mo = d()->metaObject; + const QQmlStaticMetaObject object(mo); + return CallPrecise(object, data, engine, callArgs, QMetaObject::InvokeMetaMethod); +} + + +ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const { + const int numberOfConstructors = d()->constructors.size(); + const int argumentCount = callArgs->argc; + const QQmlStaticMetaObject object(d()->metaObject); + + QQmlPropertyData best; + int bestParameterScore = INT_MAX; + int bestMatchScore = INT_MAX; + + QV4::Scope scope(engine); + QV4::ScopedValue v(scope); + + for (int i = 0; i < numberOfConstructors; i++) { + const QQmlPropertyData & attempt = d()->constructors.at(i); + QVarLengthArray dummy; + int methodArgumentCount = 0; + int *methodArgTypes = 0; + if (attempt.hasArguments()) { + int *args = object.constructorParameterTypes(attempt.coreIndex, dummy, 0); + if (!args) // Must be an unknown argument + continue; + + methodArgumentCount = args[0]; + methodArgTypes = args + 1; + } + + if (methodArgumentCount > argumentCount) + continue; // We don't have sufficient arguments to call this method + + int methodParameterScore = argumentCount - methodArgumentCount; + if (methodParameterScore > bestParameterScore) + continue; // We already have a better option + + int methodMatchScore = 0; + for (int ii = 0; ii < methodArgumentCount; ++ii) + methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]); + + if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { + best = attempt; + bestParameterScore = methodParameterScore; + bestMatchScore = methodMatchScore; + } + + if (bestParameterScore == 0 && bestMatchScore == 0) + break; // We can't get better than that + }; + + if (best.isValid()) { + return CallPrecise(object, best, engine, callArgs, QMetaObject::CreateInstance); + } else { + QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); + for (int i = 0; i < numberOfConstructors; i++) { + const QQmlPropertyData & candidate = d()->constructors.at(i); + error += QLatin1String("\n ") + + QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex) + .methodSignature()); + } + + return engine->throwError(error); + } +} + +bool QMetaObjectWrapper::isEqualTo(Managed *a, Managed *b) +{ + Q_ASSERT(a->as()); + QMetaObjectWrapper *aMetaObject = a->as(); + QMetaObjectWrapper *bMetaObject = b->as(); + if (!bMetaObject) + return true; + return aMetaObject->metaObject() == bMetaObject->metaObject(); +} + +DEFINE_OBJECT_VTABLE(QMetaObjectWrapper); + + + + Heap::QmlSignalHandler::QmlSignalHandler(QObject *object, int signalIndex) : object(object) , signalIndex(signalIndex) diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index d53bb88d20..4d2b263e97 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -94,6 +94,14 @@ struct QObjectMethod : FunctionObject { const QMetaObject *metaObject(); }; +struct QMetaObjectWrapper : FunctionObject { + QMetaObjectWrapper(const QMetaObject* metaObject); + const QMetaObject* metaObject; + QVector constructors; + + void ensureConstructorsCache(); +}; + struct QmlSignalHandler : Object { QmlSignalHandler(QObject *object, int signalIndex); QPointer object; @@ -194,6 +202,26 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject static QPair extractQtMethod(const QV4::FunctionObject *function); }; + +struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject +{ + V4_OBJECT2(QMetaObjectWrapper, QV4::FunctionObject) + V4_NEEDS_DESTROY + + static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); + static ReturnedValue construct(const Managed *, CallData *callData); + static bool isEqualTo(Managed *a, Managed *b); + + const QMetaObject *metaObject() const { return d()->metaObject; } + +private: + void init(ExecutionEngine *engine); + ReturnedValue constructInternal(CallData *callData) const; + ReturnedValue callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; + ReturnedValue callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; + +}; + struct QmlSignalHandler : public QV4::Object { V4_OBJECT2(QmlSignalHandler, QV4::Object) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 08d51ecfa1..562e7d1746 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -184,10 +184,16 @@ void QQmlPropertyData::load(const QMetaMethod &m) { coreIndex = m.methodIndex(); arguments = 0; + + propType = m.returnType(); + flags |= IsFunction; if (m.methodType() == QMetaMethod::Signal) flags |= IsSignal; - propType = m.returnType(); + else if (m.methodType() == QMetaMethod::Constructor) { + flags |= IsConstructor; + propType = QMetaType::QObjectStar; + } if (m.parameterCount()) { flags |= HasArguments; @@ -206,11 +212,15 @@ void QQmlPropertyData::load(const QMetaMethod &m) void QQmlPropertyData::lazyLoad(const QMetaMethod &m) { coreIndex = m.methodIndex(); + propType = QMetaType::Void; arguments = 0; flags |= IsFunction; if (m.methodType() == QMetaMethod::Signal) flags |= IsSignal; - propType = QMetaType::Void; + else if (m.methodType() == QMetaMethod::Constructor) { + flags |= IsConstructor; + propType = QMetaType::QObjectStar; + } const char *returnType = m.typeName(); if (!returnType) @@ -1508,39 +1518,49 @@ int *QQmlMetaObject::methodParameterTypes(int index, QVarLengthArray &du } else { QMetaMethod m = _m.asT2()->method(index); - int argc = m.parameterCount(); - dummy.resize(argc + 1); - dummy[0] = argc; - QList argTypeNames; // Only loaded if needed + return methodParameterTypes(m, dummy, unknownTypeError); - for (int ii = 0; ii < argc; ++ii) { - int type = m.parameterType(ii); - QMetaType::TypeFlags flags = QMetaType::typeFlags(type); - if (flags & QMetaType::IsEnumeration) - type = QVariant::Int; - else if (type == QMetaType::UnknownType || - (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) && - type != qMetaTypeId())) { - //the UserType clause is to catch registered QFlags) - if (argTypeNames.isEmpty()) - argTypeNames = m.parameterTypes(); - type = EnumType(_m.asT2(), argTypeNames.at(ii), type); - } - if (type == QMetaType::UnknownType) { - if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); - return 0; - } - dummy[ii + 1] = type; - } + } +} - return dummy.data(); +int* QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, QVarLengthArray &dummy, QByteArray *unknownTypeError) const { + int argc = m.parameterCount(); + dummy.resize(argc + 1); + dummy[0] = argc; + QList argTypeNames; // Only loaded if needed + + for (int ii = 0; ii < argc; ++ii) { + int type = m.parameterType(ii); + QMetaType::TypeFlags flags = QMetaType::typeFlags(type); + if (flags & QMetaType::IsEnumeration) + type = QVariant::Int; + else if (type == QMetaType::UnknownType || + (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) && + type != qMetaTypeId())) { + //the UserType clause is to catch registered QFlags) + if (argTypeNames.isEmpty()) + argTypeNames = m.parameterTypes(); + type = EnumType(_m.asT2(), argTypeNames.at(ii), type); + } + if (type == QMetaType::UnknownType) { + if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); + return 0; + } + dummy[ii + 1] = type; } + + return dummy.data(); } void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const { - if (ptr.isT1()) + if (ptr.isNull()) { + const QMetaObject *metaObject = _m.asT2(); + metaObject->d.static_metacall(0, type, index, argv); + } + else if (ptr.isT1()) { QMetaObject::metacall(ptr.asT1(), type, index, argv); + } else { const QMetaObject *metaObject = _m.asT1()->metaObject(); QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index); @@ -1548,4 +1568,9 @@ void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv } } +int* QQmlStaticMetaObject::constructorParameterTypes(int index, QVarLengthArray &dummy, QByteArray *unknownTypeError) const { + QMetaMethod m = _m.asT2()->constructor(index); + return methodParameterTypes(m, dummy, unknownTypeError); +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index b2171dd86b..3e84fb3070 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -115,9 +115,10 @@ public: IsSignalHandler = 0x00800000, // Function is a signal handler IsOverload = 0x01000000, // Function is an overload of another function IsCloned = 0x02000000, // The function was marked as cloned + IsConstructor = 0x04000000, // The function was marked is a constructor // Internal QQmlPropertyCache flags - NotFullyResolved = 0x04000000, // True if the type data is to be lazily resolved + NotFullyResolved = 0x08000000, // True if the type data is to be lazily resolved // Flags that are set based on the propType field PropTypeFlagMask = IsQObjectDerived | IsEnumType | IsQList | IsQmlBinding | IsQJSValue | @@ -156,6 +157,7 @@ public: bool isSignalHandler() const { return flags & IsSignalHandler; } bool isOverload() const { return flags & IsOverload; } bool isCloned() const { return flags & IsCloned; } + bool isConstructor() const { return flags & IsConstructor; } bool hasOverride() const { return !(flags & IsValueTypeVirtual) && !(flags & HasAccessors) && @@ -437,6 +439,8 @@ public: protected: QBiPointer _m; + int *methodParameterTypes(const QMetaMethod &method, QVarLengthArray &dummy, QByteArray *unknownTypeError) const; + }; class QQmlObjectOrGadget: public QQmlMetaObject @@ -455,6 +459,20 @@ public: private: QBiPointer ptr; + +protected: + QQmlObjectOrGadget(const QMetaObject* metaObject) + : QQmlMetaObject(metaObject) + {} + +}; + +class QQmlStaticMetaObject : public QQmlObjectOrGadget { +public: + QQmlStaticMetaObject(const QMetaObject* metaObject) + : QQmlObjectOrGadget(metaObject) + {} + int *constructorParameterTypes(int index, QVarLengthArray &dummy, QByteArray *unknownTypeError) const; }; QQmlPropertyData::QQmlPropertyData() diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 99c18f91a1..43f43764d5 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -73,6 +73,7 @@ private slots: void newQObject(); void newQObject_ownership(); void newQObject_deletedEngine(); + void newQMetaObject(); void exceptionInSlot(); void globalObjectProperties(); void globalObjectEquals(); @@ -712,6 +713,104 @@ void tst_QJSEngine::newQObject_deletedEngine() QTRY_VERIFY(spy.count()); } +class TestQMetaObject : public QObject { + Q_OBJECT + Q_PROPERTY(int called READ called) +public: + enum Enum1 { + Zero = 0, + One, + Two + }; + enum Enum2 { + A = 0, + B, + C + }; + Q_ENUMS(Enum1 Enum2) + + Q_INVOKABLE TestQMetaObject() + : m_called(1) { + } + Q_INVOKABLE TestQMetaObject(int) + : m_called(2) { + } + Q_INVOKABLE TestQMetaObject(QString) + : m_called(3) { + } + Q_INVOKABLE TestQMetaObject(QString, int) + : m_called(4) { + } + int called() const { + return m_called; + } +private: + int m_called; +}; + +void tst_QJSEngine::newQMetaObject() { + { + QJSEngine engine; + QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject::staticMetaObject); + QCOMPARE(metaObject.isNull(), false); + QCOMPARE(metaObject.isObject(), true); + QCOMPARE(metaObject.isQObject(), false); + QCOMPARE(metaObject.isCallable(), true); + QCOMPARE(metaObject.isQMetaObject(), true); + + QCOMPARE(metaObject.toQMetaObject(), &TestQMetaObject::staticMetaObject); + + QVERIFY(metaObject.strictlyEquals(engine.newQMetaObject())); + + + { + auto result = metaObject.callAsConstructor(); + if (result.isError()) + qDebug() << result.toString(); + QCOMPARE(result.isError(), false); + QCOMPARE(result.isNull(), false); + QCOMPARE(result.isObject(), true); + QCOMPARE(result.isQObject(), true); + QVERIFY(result.property("constructor").strictlyEquals(metaObject)); + QVERIFY(result.prototype().strictlyEquals(metaObject)); + + + QCOMPARE(result.property("called").toInt(), 1); + + } + + QJSValue integer(42); + QJSValue string("foo"); + + { + auto result = metaObject.callAsConstructor({integer}); + QCOMPARE(result.property("called").toInt(), 2); + } + + { + auto result = metaObject.callAsConstructor({string}); + QCOMPARE(result.property("called").toInt(), 3); + } + + { + auto result = metaObject.callAsConstructor({string, integer}); + QCOMPARE(result.property("called").toInt(), 4); + } + } + + { + QJSEngine engine; + QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject::staticMetaObject); + QCOMPARE(metaObject.property("Zero").toInt(), 0); + QCOMPARE(metaObject.property("One").toInt(), 1); + QCOMPARE(metaObject.property("Two").toInt(), 2); + QCOMPARE(metaObject.property("A").toInt(), 0); + QCOMPARE(metaObject.property("B").toInt(), 1); + QCOMPARE(metaObject.property("C").toInt(), 2); + } + +} + void tst_QJSEngine::exceptionInSlot() { QJSEngine engine; -- cgit v1.2.3 From ffce29dc2036f26dff8142978cb4e86e869cd38f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 6 Jun 2016 08:33:13 +0200 Subject: Fix memory leak regression of commit 77773997d085e83c811fe1e312f0402b932a56ac QQmlRefCount subclasses are created with a reference count of 1, which means that if the first assignment is to a RefPtr we need to adopt. Change-Id: I41e54506e2d538a488844e45db6b0404457234f7 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index e6ba827993..cd204573c3 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -64,7 +64,7 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() { - importCache = new QQmlTypeNameCache; + importCache.adopt(new QQmlTypeNameCache); foreach (const QString &ns, typeData->namespaces()) importCache->add(ns); -- cgit v1.2.3 From 921c1e0e4f6f00f449136f116c6fc5f55fbf1df6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 6 Jun 2016 12:39:38 +0200 Subject: Another manual refcounting cleanup There is little reason to use manual reference counting for an encapsulated member. Change-Id: I024a6ff700872da6837954a5167e502487e9b12c Reviewed-by: Lars Knoll --- src/qml/qml/qqmltypeloader.cpp | 14 +++++--------- src/qml/qml/qqmltypeloader_p.h | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 87036d9ea4..3cbbaf4129 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2008,7 +2008,7 @@ QQmlTypeData::TypeDataCallback::~TypeDataCallback() QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) : QQmlTypeLoader::Blob(url, QmlFile, manager), - m_typesResolved(false), m_compiledData(0), m_implicitImport(0), m_implicitImportLoaded(false) + m_typesResolved(false), m_implicitImport(0), m_implicitImportLoaded(false) { } @@ -2026,9 +2026,6 @@ QQmlTypeData::~QQmlTypeData() if (QQmlTypeData *tdata = it->typeData) tdata->release(); } - - if (m_compiledData) - m_compiledData->release(); } const QList &QQmlTypeData::resolvedScripts() const @@ -2048,7 +2045,7 @@ const QList &QQmlTypeData::compositeSingletons() co QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const { - return m_compiledData; + return m_compiledData.data(); } void QQmlTypeData::registerCallback(TypeDataCallback *callback) @@ -2301,14 +2298,13 @@ QString QQmlTypeData::stringAt(int index) const void QQmlTypeData::compile() { - Q_ASSERT(m_compiledData == 0); + Q_ASSERT(m_compiledData.isNull()); QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), this, m_document.data()); m_compiledData = compiler.compile(); - if (m_compiledData) { - m_compiledData->addref(); - } else { + if (!m_compiledData) { setError(compiler.compilationErrors()); + return; } } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 7d54b9bdd1..baea9dbd0a 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -467,7 +467,7 @@ private: QHash m_resolvedTypes; bool m_typesResolved:1; - QV4::CompiledData::CompilationUnit *m_compiledData; + QQmlRefPointer m_compiledData; QList m_callbacks; -- cgit v1.2.3 From 4f322f7f2c46e7ffc172400010a944afd4b51489 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 6 Jun 2016 13:12:02 +0200 Subject: Clean up CompiledData::Function * We don't need 64 bits for the flags, for now 8 bits are sufficient. * Removed the unused index member. It's symmetric to the index of the function object in the function table anyway. Change-Id: I18248e8ed79b3c3af87e17689dadf7323d5d9bb3 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata_p.h | 3 +-- src/qml/compiler/qv4compiler.cpp | 5 ++--- src/qml/compiler/qv4compiler_p.h | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 744d2bb8da..9be45184a7 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -179,9 +179,8 @@ struct Function HasCatchOrWith = 0x10 }; - quint32 index; // in CompilationUnit's function table + quint8 flags; quint32 nameIndex; - qint64 flags; quint32 nFormals; quint32 formalsOffset; quint32 nLocals; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index aacf0e9928..81f407485f 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -268,7 +268,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO if (function == irModule->rootFunction) unit->indexOfRootFunction = i; - const int bytes = writeFunction(f, i, function); + const int bytes = writeFunction(f, function); f += bytes; } @@ -307,13 +307,12 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO return unit; } -int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::Function *irFunction) +int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; quint32 currentOffset = sizeof(QV4::CompiledData::Function); - function->index = index; function->nameIndex = getStringId(*irFunction->name); function->flags = 0; if (irFunction->hasDirectEval) diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 0321a83b4f..df10e9919b 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -114,7 +114,7 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable); // Returns bytes written - int writeFunction(char *f, int index, IR::Function *irFunction); + int writeFunction(char *f, IR::Function *irFunction); StringTableGenerator stringTable; private: -- cgit v1.2.3 From 6d54a59bd725ba83e758236c5ff0fc7855109dac Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 2 Jun 2016 15:57:41 +0200 Subject: Fix crash when using with statement with an expression that throws We need to evaluate the expression for the "with" statement that is supposed to define the new scope _before_ opening up the scope, otherwise - when the evaluation of the expression throws an exception - we'll try to pop the "with" scope we couldn't open in the first place. [ChangeLog][QtQml] Fix crash when using the "with" statement with an expression that throws an exception. Task-number: QTBUG-53794 Change-Id: I7733f5a4c5d844916302b9a91c789a0f6b421e8a Reviewed-by: Lars Knoll --- src/qml/compiler/qv4codegen.cpp | 5 +++-- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index ea82d07e69..c14163a2f7 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2718,6 +2718,9 @@ bool Codegen::visit(WithStatement *ast) _function->hasWith = true; + const int withObject = _block->newTemp(); + _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); + // need an exception handler for with to cleanup the with scope IR::BasicBlock *withExceptionHandler = _function->newBasicBlock(exceptionHandler()); withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); @@ -2732,8 +2735,6 @@ bool Codegen::visit(WithStatement *ast) _block->JUMP(withBlock); _block = withBlock; - int withObject = _block->newTemp(); - _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); IR::ExprList *args = _function->New(); args->init(_block->TEMP(withObject)); _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args)); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 9a0865c0ac..8594aec8cd 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -193,6 +193,8 @@ private slots: void v4FunctionWithoutQML(); + void withNoContext(); + signals: void testSignal(); }; @@ -3840,6 +3842,13 @@ void tst_QJSEngine::v4FunctionWithoutQML() QVERIFY(obj.called); } +void tst_QJSEngine::withNoContext() +{ + // Don't crash (QTBUG-53794) + QJSEngine engine; + engine.evaluate("with (noContext) true"); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" -- cgit v1.2.3 From cb8ce2bfaffc42aedeeaf5e68f7e5b481ba4c95b Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Tue, 7 Jun 2016 16:27:30 +0100 Subject: KeyNavigation: fix the documentation for its properties It does not make any sense to document these properties with the same text, as qdoc will duplicate the text under the documentation of each and every property in the same documentation comment, resulting in weird documentation (such as the element you assign to "left" is the one you move to when you press the *right* key). Change-Id: Ic27502a5965cc2b8c61cfdaf887434d3c79256af Reviewed-by: Mitch Curtis --- src/quick/items/qquickitem.cpp | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index e963f91961..d0f5f162fc 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -414,21 +414,44 @@ void QQuickItemKeyFilter::componentComplete() /*! \qmlproperty Item QtQuick::KeyNavigation::left + + This property holds the item to assign focus to + when the left cursor key is pressed. +*/ + +/*! \qmlproperty Item QtQuick::KeyNavigation::right + + This property holds the item to assign focus to + when the right cursor key is pressed. +*/ + +/*! \qmlproperty Item QtQuick::KeyNavigation::up + + This property holds the item to assign focus to + when the up cursor key is pressed. +*/ + +/*! \qmlproperty Item QtQuick::KeyNavigation::down - These properties hold the item to assign focus to - when the left, right, up or down cursor keys - are pressed. + This property holds the item to assign focus to + when the down cursor key is pressed. */ /*! \qmlproperty Item QtQuick::KeyNavigation::tab + + This property holds the item to assign focus to + when the Tab key is pressed. +*/ + +/*! \qmlproperty Item QtQuick::KeyNavigation::backtab - These properties hold the item to assign focus to - when the Tab key or Shift+Tab key combination (Backtab) are pressed. + This property holds the item to assign focus to + when the Shift+Tab key combination (Backtab) is pressed. */ QQuickKeyNavigationAttached::QQuickKeyNavigationAttached(QObject *parent) -- cgit v1.2.3 From fc83d811812d2e16f4139f6db7e357b5d2eb945b Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 1 Jun 2016 10:13:07 +0200 Subject: Add \since 5.8 to Qt.callLater() It wasn't added in 6cc908e25. Change-Id: I1a880984965b379494f153cbe21b1f7fdbc28a65 Reviewed-by: Simon Hausmann --- src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 54c29b4b8a..482da31779 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -2001,6 +2001,7 @@ ReturnedValue GlobalExtensions::method_string_arg(CallContext *ctx) /*! \qmlmethod Qt::callLater(function) \qmlmethod Qt::callLater(function, argument1, argument2, ...) +\since 5.8 Use this function to eliminate redundant calls to a function or signal. The function passed as the first argument to \l{QML:Qt::callLater()}{Qt.callLater()} -- cgit v1.2.3 From 6887fa0baf0f0e09a3dda74390236b27619f76d4 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 7 Jun 2016 15:10:17 +0200 Subject: Fix 'Threaded Render Loop' docs Change-Id: I682a4765d1e0173664460fe4e163949d968d97d7 Reviewed-by: Gunnar Sletta --- src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc index 9ccb05cdf1..516630d034 100644 --- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc @@ -214,7 +214,7 @@ user input. An event is posted to the render thread to initiate a new frame. \li The render thread prepares to draw a new frame and makes the -OpenGL context current and initiates a blocks on the GUI thread. +OpenGL context current and initiates a block on the GUI thread. \li While the render thread is preparing the new frame, the GUI thread calls QQuickItem::updatePolish() to do final touch-up of items before -- cgit v1.2.3 From cd0efef04bd45eca6cc72b5a000e4e5586153290 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 7 Jun 2016 15:22:12 +0200 Subject: Fix spelling error in QSGNode docs Change-Id: I9ece132b87a8de06924e71b6f5fc552a14dea336 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/coreapi/qsgnode.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index c09af7c0a3..48f5854a54 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -331,12 +331,12 @@ QSGNode::~QSGNode() to the scene graph and will cause the preprocess() function to be called for every frame the node is rendered. - The preprocess function is called before the update pass that propegates + The preprocess function is called before the update pass that propagates opacity and transformations through the scene graph. That means that functions like QSGOpacityNode::combinedOpacity() and QSGTransformNode::combinedMatrix() will not contain up-to-date values. If such values are changed during the preprocess, these changes will be - propegated through the scene graph before it is rendered. + propagated through the scene graph before it is rendered. \warning Beware of deleting nodes while they are being preprocessed. It is possible, with a small performance hit, to delete a single node during its @@ -1331,7 +1331,7 @@ const qreal OPACITY_THRESHOLD = 0.001; Sets the opacity of this node to \a opacity. Before rendering the graph, the renderer will do an update pass - over the subtree to propegate the opacity to its children. + over the subtree to propagate the opacity to its children. The value will be bounded to the range 0 to 1. */ -- cgit v1.2.3 From ce0800060246e68fffc226b366cb4b0ea2e41ded Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 26 May 2016 17:46:24 +0200 Subject: V4: tighten up various casts to check also check type. All those type conversions assumed that the content of a Value was either the requested type, or 0 (zero, a null pointer). Now, attempting to convert e.g. undefined to a string will fail, instead of returning a weird address. Change-Id: I0f567cdcc9cc9728d019f17693f4a6007394a9c6 Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4persistent_p.h | 4 ++-- src/qml/jsruntime/qv4value_p.h | 13 +++++++++++-- src/qml/memory/qv4mm.cpp | 5 +++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index 5b1926468a..a0ade2068f 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -118,7 +118,7 @@ public: Managed *asManaged() const { if (!val) return 0; - return val->as(); + return val->managed(); } template T *as() const { @@ -167,7 +167,7 @@ public: Managed *asManaged() const { if (!val) return 0; - return val->as(); + return val->managed(); } template T *as() const { diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index a8d9b0fa71..f99e24c6e9 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -286,16 +286,22 @@ struct Q_QML_PRIVATE_EXPORT Value } Q_ALWAYS_INLINE String *stringValue() const { + if (!isString()) + return nullptr; return m() ? reinterpret_cast(const_cast(this)) : 0; } Q_ALWAYS_INLINE Object *objectValue() const { + if (!isObject()) + return nullptr; return m() ? reinterpret_cast(const_cast(this)) : 0; } Q_ALWAYS_INLINE Managed *managed() const { + if (!isManaged()) + return nullptr; return m() ? reinterpret_cast(const_cast(this)) : 0; } Q_ALWAYS_INLINE Heap::Base *heapObject() const { - return m(); + return isManaged() ? m() : nullptr; } Q_ALWAYS_INLINE quint64 &rawValueRef() { @@ -357,7 +363,10 @@ struct Q_QML_PRIVATE_EXPORT Value } template T *as() { - return const_cast(const_cast(this)->as()); + if (isManaged()) + return const_cast(const_cast(this)->as()); + else + return nullptr; } template inline T *cast() { diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 4fb2acebc5..226358074b 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -382,6 +382,7 @@ static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) { while (engine->jsStackTop > markBase) { Heap::Base *h = engine->popForGC(); + Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen. Q_ASSERT (h->vtable()->markObjects); h->vtable()->markObjects(h, engine); } @@ -438,7 +439,7 @@ void MemoryManager::sweep(bool lastSweep) for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { if (!(*it).isManaged()) continue; - Managed *m = (*it).as(); + Managed *m = (*it).managed(); if (m->markBit()) continue; // we need to call detroyObject on qobjectwrappers now, so that they can emit the destroyed @@ -676,7 +677,7 @@ void MemoryManager::collectFromJSStack() const Value *v = engine->jsStackBase; Value *top = engine->jsStackTop; while (v < top) { - Managed *m = v->as(); + Managed *m = v->managed(); if (m && m->inUse()) // Skip pointers to already freed objects, they are bogus as well m->mark(engine); -- cgit v1.2.3 From 6202ea699f6c2361c0ffcb7690b6c67643cba038 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 8 Jun 2016 12:44:26 +0200 Subject: Mention license change in 5.7.0 changelog. Change-Id: Id5619de54769468cf61b4be55ba45459921f28c4 Task-number: QTBUG-53913 Reviewed-by: Sami Makkonen Reviewed-by: Antti Kokko --- dist/changes-5.7.0 | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/dist/changes-5.7.0 b/dist/changes-5.7.0 index 0545fe7eb1..ac8c63b2ad 100644 --- a/dist/changes-5.7.0 +++ b/dist/changes-5.7.0 @@ -1,6 +1,7 @@ Qt 5.7 introduces many new features and improvements as well as bugfixes -over the 5.6.x series. For more details, refer to the online documentation -included in this distribution. The documentation is also available online: +over the 5.6.x series. Also, there is a change in the licensing terms. +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: http://doc.qt.io/qt-5/index.html @@ -15,6 +16,21 @@ corresponding to tasks in the Qt Bug Tracker: Each of these identifiers can be entered in the bug tracker to obtain more information about a particular change. +**************************************************************************** +* Important License Changes * +**************************************************************************** + + This module is no longer available under LGPLv2.1. The libraries are + now available under the following licenses: + * Commercial License + * GNU General Public License v2.0 (LICENSE.GPL2) and later + * GNU Lesser General Public License v3.0 (LICENSE.LGPL3) + + The tools are now available under the following licenses: + * Commercial License + * GNU General Public License 3.0 (LICENSE.GPL3) with exceptions + described in The Qt Company GPL Exception 1.0 (LICENSE.GPL3-EXCEPT) + **************************************************************************** * Important Behavior Changes * **************************************************************************** -- cgit v1.2.3 From 950994cab62816dfbcba3a1c9f8304ddd24bb726 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 10 Mar 2016 10:37:58 +0100 Subject: V4: Do not rely on the vtable for dynamic casts or visiting of the IR. By using "hand-rolled RTTI", the virtual function call for asBlahBlah() can de done with an load+branch, which is more efficient. Also things like isLValue() are now just a switch. Also, by not using a visitor (where each visit+accept takes two virtual function calls), the visitors get reduced to a switch. The visitor for expressions can then do tail-call optimizatin too, bringing the number of calls down even further. Change-Id: I5e623175bd090b7efb03131b0a6a257e4ae01802 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4jsir_p.h | 319 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 262 insertions(+), 57 deletions(-) diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 94fa65cf71..8ba641ab6a 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -192,7 +192,7 @@ enum AluOp { AluOp binaryOperator(int op); const char *opname(IR::AluOp op); -enum Type { +enum Type : quint16 { UnknownType = 0, MissingType = 1 << 0, @@ -288,28 +288,131 @@ struct MemberExpressionResolver }; struct Q_AUTOTEST_EXPORT Expr { + enum ExprKind : quint8 { + NameExpr, + TempExpr, + ArgLocalExpr, + SubscriptExpr, + MemberExpr, + + LastLValue = MemberExpr, + + ConstExpr, + StringExpr, + RegExpExpr, + ClosureExpr, + ConvertExpr, + UnopExpr, + BinopExpr, + CallExpr, + NewExpr + }; + Type type; + const ExprKind exprKind; + + Expr &operator=(const Expr &other) { + Q_ASSERT(exprKind == other.exprKind); + type = other.type; + return *this; + } + + template + inline bool isa() const { + return To::classof(this); + } + + template + inline To *as() { + if (isa()) { + return static_cast(this); + } else { + return nullptr; + } + } + + template + inline const To *as() const { + if (isa()) { + return static_cast(this); + } else { + return nullptr; + } + } - Expr(): type(UnknownType) {} + Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {} virtual ~Expr() {} virtual void accept(ExprVisitor *) = 0; - virtual bool isLValue() { return false; } - virtual Const *asConst() { return 0; } - virtual String *asString() { return 0; } - virtual RegExp *asRegExp() { return 0; } - virtual Name *asName() { return 0; } - virtual Temp *asTemp() { return 0; } - virtual ArgLocal *asArgLocal() { return 0; } - virtual Closure *asClosure() { return 0; } - virtual Convert *asConvert() { return 0; } - virtual Unop *asUnop() { return 0; } - virtual Binop *asBinop() { return 0; } - virtual Call *asCall() { return 0; } - virtual New *asNew() { return 0; } - virtual Subscript *asSubscript() { return 0; } - virtual Member *asMember() { return 0; } + bool isLValue() const; + + Const *asConst() { return as(); } + String *asString() { return as(); } + RegExp *asRegExp() { return as(); } + Name *asName() { return as(); } + Temp *asTemp() { return as(); } + ArgLocal *asArgLocal() { return as(); } + Closure *asClosure() { return as(); } + Convert *asConvert() { return as(); } + Unop *asUnop() { return as(); } + Binop *asBinop() { return as(); } + Call *asCall() { return as(); } + New *asNew() { return as(); } + Subscript *asSubscript() { return as(); } + Member *asMember() { return as(); } }; +#define EXPR_VISIT_ALL_KINDS(e) \ + switch (e->exprKind) { \ + case QV4::IR::Expr::ConstExpr: \ + break; \ + case QV4::IR::Expr::StringExpr: \ + break; \ + case QV4::IR::Expr::RegExpExpr: \ + break; \ + case QV4::IR::Expr::NameExpr: \ + break; \ + case QV4::IR::Expr::TempExpr: \ + break; \ + case QV4::IR::Expr::ArgLocalExpr: \ + break; \ + case QV4::IR::Expr::ClosureExpr: \ + break; \ + case QV4::IR::Expr::ConvertExpr: { \ + auto casted = e->asConvert(); \ + visit(casted->expr); \ + } break; \ + case QV4::IR::Expr::UnopExpr: { \ + auto casted = e->asUnop(); \ + visit(casted->expr); \ + } break; \ + case QV4::IR::Expr::BinopExpr: { \ + auto casted = e->asBinop(); \ + visit(casted->left); \ + visit(casted->right); \ + } break; \ + case QV4::IR::Expr::CallExpr: { \ + auto casted = e->asCall(); \ + visit(casted->base); \ + for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \ + visit(it->expr); \ + } break; \ + case QV4::IR::Expr::NewExpr: { \ + auto casted = e->asNew(); \ + visit(casted->base); \ + for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \ + visit(it->expr); \ + } break; \ + case QV4::IR::Expr::SubscriptExpr: { \ + auto casted = e->asSubscript(); \ + visit(casted->base); \ + visit(casted->index); \ + } break; \ + case QV4::IR::Expr::MemberExpr: { \ + auto casted = e->asMember(); \ + visit(casted->base); \ + } break; \ + } + struct ExprList { Expr *expr; ExprList *next; @@ -326,6 +429,8 @@ struct ExprList { struct Const: Expr { double value; + Const(): Expr(ConstExpr) {} + void init(Type type, double value) { this->type = type; @@ -333,19 +438,23 @@ struct Const: Expr { } virtual void accept(ExprVisitor *v) { v->visitConst(this); } - virtual Const *asConst() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == ConstExpr; } }; struct String: Expr { const QString *value; + String(): Expr(StringExpr) {} + void init(const QString *value) { this->value = value; } virtual void accept(ExprVisitor *v) { v->visitString(this); } - virtual String *asString() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == StringExpr; } }; struct RegExp: Expr { @@ -359,6 +468,8 @@ struct RegExp: Expr { const QString *value; int flags; + RegExp(): Expr(RegExpExpr) {} + void init(const QString *value, int flags) { this->value = value; @@ -366,7 +477,8 @@ struct RegExp: Expr { } virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } - virtual RegExp *asRegExp() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == RegExpExpr; } }; struct Name: Expr { @@ -399,13 +511,15 @@ struct Name: Expr { quint32 line; quint32 column; + Name(): Expr(NameExpr) {} + void initGlobal(const QString *id, quint32 line, quint32 column); void init(const QString *id, quint32 line, quint32 column); void init(Builtin builtin, quint32 line, quint32 column); virtual void accept(ExprVisitor *v) { v->visitName(this); } - virtual bool isLValue() { return true; } - virtual Name *asName() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == NameExpr; } }; struct Q_AUTOTEST_EXPORT Temp: Expr { @@ -424,7 +538,8 @@ struct Q_AUTOTEST_EXPORT Temp: Expr { MemberExpressionResolver *memberResolver; Temp() - : index((1 << 28) - 1) + : Expr(TempExpr) + , index((1 << 28) - 1) , isReadOnly(0) , kind(Invalid) , memberResolver(0) @@ -439,8 +554,9 @@ struct Q_AUTOTEST_EXPORT Temp: Expr { bool isInvalid() const { return kind == Invalid; } virtual void accept(ExprVisitor *v) { v->visitTemp(this); } - virtual bool isLValue() { return !isReadOnly; } virtual Temp *asTemp() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == TempExpr; } }; inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW @@ -479,18 +595,22 @@ struct Q_AUTOTEST_EXPORT ArgLocal: Expr { this->isArgumentsOrEval = false; } + ArgLocal(): Expr(ArgLocalExpr) {} + virtual void accept(ExprVisitor *v) { v->visitArgLocal(this); } - virtual bool isLValue() { return true; } - virtual ArgLocal *asArgLocal() { return this; } bool operator==(const ArgLocal &other) const { return index == other.index && scope == other.scope && kind == other.kind; } + + static bool classof(const Expr *c) { return c->exprKind == ArgLocalExpr; } }; struct Closure: Expr { int value; // index in _module->functions const QString *functionName; + Closure(): Expr(ClosureExpr) {} + void init(int functionInModule, const QString *functionName) { this->value = functionInModule; @@ -498,12 +618,15 @@ struct Closure: Expr { } virtual void accept(ExprVisitor *v) { v->visitClosure(this); } - virtual Closure *asClosure() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == ClosureExpr; } }; struct Convert: Expr { Expr *expr; + Convert(): Expr(ConvertExpr) {} + void init(Expr *expr, Type type) { this->expr = expr; @@ -511,12 +634,15 @@ struct Convert: Expr { } virtual void accept(ExprVisitor *v) { v->visitConvert(this); } - virtual Convert *asConvert() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == ConvertExpr; } }; struct Unop: Expr { - AluOp op; Expr *expr; + AluOp op; + + Unop(): Expr(UnopExpr) {} void init(AluOp op, Expr *expr) { @@ -525,13 +651,16 @@ struct Unop: Expr { } virtual void accept(ExprVisitor *v) { v->visitUnop(this); } - virtual Unop *asUnop() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == UnopExpr; } }; struct Binop: Expr { - AluOp op; Expr *left; // Temp or Const Expr *right; // Temp or Const + AluOp op; + + Binop(): Expr(BinopExpr) {} void init(AluOp op, Expr *left, Expr *right) { @@ -541,13 +670,16 @@ struct Binop: Expr { } virtual void accept(ExprVisitor *v) { v->visitBinop(this); } - virtual Binop *asBinop() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == BinopExpr; } }; struct Call: Expr { Expr *base; // Name, Member, Temp ExprList *args; // List of Temps + Call(): Expr(CallExpr) {} + void init(Expr *base, ExprList *args) { this->base = base; @@ -561,13 +693,16 @@ struct Call: Expr { } virtual void accept(ExprVisitor *v) { v->visitCall(this); } - virtual Call *asCall() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == CallExpr; } }; struct New: Expr { Expr *base; // Name, Member, Temp ExprList *args; // List of Temps + New(): Expr(NewExpr) {} + void init(Expr *base, ExprList *args) { this->base = base; @@ -581,13 +716,16 @@ struct New: Expr { } virtual void accept(ExprVisitor *v) { v->visitNew(this); } - virtual New *asNew() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == NewExpr; } }; struct Subscript: Expr { Expr *base; Expr *index; + Subscript(): Expr(SubscriptExpr) {} + void init(Expr *base, Expr *index) { this->base = base; @@ -595,8 +733,8 @@ struct Subscript: Expr { } virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } - virtual bool isLValue() { return true; } - virtual Subscript *asSubscript() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == SubscriptExpr; } }; struct Member: Expr { @@ -628,6 +766,8 @@ struct Member: Expr { uchar kind: 3; // MemberKind + Member(): Expr(MemberExpr) {} + void setEnumValue(int value) { kind = MemberOfEnum; enumValue = value; @@ -650,16 +790,44 @@ struct Member: Expr { } virtual void accept(ExprVisitor *v) { v->visitMember(this); } - virtual bool isLValue() { return true; } - virtual Member *asMember() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == MemberExpr; } }; +inline bool Expr::isLValue() const { + if (auto t = as()) + return !t->isReadOnly; + return exprKind <= LastLValue; +} + struct Stmt { + enum StmtKind: quint8 { + MoveStmt, + ExpStmt, + JumpStmt, + CJumpStmt, + RetStmt, + PhiStmt + }; + + template + inline bool isa() const { + return To::classof(this); + } + + template + inline To *as() { + if (isa()) + return static_cast(this); + else + return nullptr; + } + enum { InvalidId = -1 }; QQmlJS::AST::SourceLocation location; - explicit Stmt(int id): _id(id) {} + explicit Stmt(int id, StmtKind stmtKind): _id(id), stmtKind(stmtKind) {} virtual ~Stmt() { @@ -672,12 +840,12 @@ struct Stmt { virtual Stmt *asTerminator() { return 0; } virtual void accept(StmtVisitor *) = 0; - virtual Exp *asExp() { return 0; } - virtual Move *asMove() { return 0; } - virtual Jump *asJump() { return 0; } - virtual CJump *asCJump() { return 0; } - virtual Ret *asRet() { return 0; } - virtual Phi *asPhi() { return 0; } + Exp *asExp() { return as(); } + Move *asMove() { return as(); } + Jump *asJump() { return as(); } + CJump *asCJump() { return as(); } + Ret *asRet() { return as(); } + Phi *asPhi() { return as(); } int id() const { return _id; } @@ -687,12 +855,45 @@ private: // For memory management in BasicBlock private: friend struct Function; int _id; + +public: + const StmtKind stmtKind; }; +#define STMT_VISIT_ALL_KINDS(s) \ + switch (s->stmtKind) { \ + case QV4::IR::Stmt::MoveStmt: { \ + auto casted = s->asMove(); \ + visit(casted->target); \ + visit(casted->source); \ + } break; \ + case QV4::IR::Stmt::ExpStmt: { \ + auto casted = s->asExp(); \ + visit(casted->expr); \ + } break; \ + case QV4::IR::Stmt::JumpStmt: \ + break; \ + case QV4::IR::Stmt::CJumpStmt: { \ + auto casted = s->asCJump(); \ + visit(casted->cond); \ + } break; \ + case QV4::IR::Stmt::RetStmt: { \ + auto casted = s->asRet(); \ + visit(casted->expr); \ + } break; \ + case QV4::IR::Stmt::PhiStmt: { \ + auto casted = s->asPhi(); \ + visit(casted->targetTemp); \ + for (auto *e : casted->incoming) { \ + visit(e); \ + } \ + } break; \ + } + struct Exp: Stmt { Expr *expr; - Exp(int id): Stmt(id) {} + Exp(int id): Stmt(id, ExpStmt) {} void init(Expr *expr) { @@ -700,8 +901,8 @@ struct Exp: Stmt { } virtual void accept(StmtVisitor *v) { v->visitExp(this); } - virtual Exp *asExp() { return this; } + static bool classof(const Stmt *c) { return c->stmtKind == ExpStmt; } }; struct Move: Stmt { @@ -709,7 +910,7 @@ struct Move: Stmt { Expr *source; bool swap; - Move(int id): Stmt(id) {} + Move(int id): Stmt(id, MoveStmt) {} void init(Expr *target, Expr *source) { @@ -719,14 +920,14 @@ struct Move: Stmt { } virtual void accept(StmtVisitor *v) { v->visitMove(this); } - virtual Move *asMove() { return this; } + static bool classof(const Stmt *c) { return c->stmtKind == MoveStmt; } }; struct Jump: Stmt { BasicBlock *target; - Jump(int id): Stmt(id) {} + Jump(int id): Stmt(id, JumpStmt) {} void init(BasicBlock *target) { @@ -736,7 +937,8 @@ struct Jump: Stmt { virtual Stmt *asTerminator() { return this; } virtual void accept(StmtVisitor *v) { v->visitJump(this); } - virtual Jump *asJump() { return this; } + + static bool classof(const Stmt *c) { return c->stmtKind == JumpStmt; } }; struct CJump: Stmt { @@ -745,7 +947,7 @@ struct CJump: Stmt { BasicBlock *iffalse; BasicBlock *parent; - CJump(int id): Stmt(id) {} + CJump(int id): Stmt(id, CJumpStmt) {} void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse, BasicBlock *parent) { @@ -758,13 +960,14 @@ struct CJump: Stmt { virtual Stmt *asTerminator() { return this; } virtual void accept(StmtVisitor *v) { v->visitCJump(this); } - virtual CJump *asCJump() { return this; } + + static bool classof(const Stmt *c) { return c->stmtKind == CJumpStmt; } }; struct Ret: Stmt { Expr *expr; - Ret(int id): Stmt(id) {} + Ret(int id): Stmt(id, RetStmt) {} void init(Expr *expr) { @@ -774,7 +977,8 @@ struct Ret: Stmt { virtual Stmt *asTerminator() { return this; } virtual void accept(StmtVisitor *v) { v->visitRet(this); } - virtual Ret *asRet() { return this; } + + static bool classof(const Stmt *c) { return c->stmtKind == RetStmt; } }; // Phi nodes can only occur at the start of a basic block. If there are any, they need to be @@ -785,10 +989,11 @@ struct Phi: Stmt { Temp *targetTemp; VarLengthArray incoming; - Phi(int id): Stmt(id) {} + Phi(int id): Stmt(id, PhiStmt) {} virtual void accept(StmtVisitor *v) { v->visitPhi(this); } - virtual Phi *asPhi() { return this; } + + static bool classof(const Stmt *c) { return c->stmtKind == PhiStmt; } void destroyData() { incoming.~VarLengthArray(); } -- cgit v1.2.3 From c3241f99cf80ff1e9bdd45f67516d5d22fe8821d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 3 May 2016 12:49:59 +0200 Subject: V4: Change uses of StmtVisitor/ExprVisitor to use new style visitors. Change-Id: I668c829bf04e0e16ed94db169507cc5290deec50 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 29 +- src/qml/compiler/qqmltypecompiler_p.h | 81 ++-- src/qml/compiler/qv4isel_moth.cpp | 6 +- src/qml/compiler/qv4isel_p.h | 34 +- src/qml/compiler/qv4isel_util_p.h | 50 +- src/qml/compiler/qv4jsir.cpp | 316 ++++++------ src/qml/compiler/qv4jsir_p.h | 125 +---- src/qml/compiler/qv4ssa.cpp | 877 ++++++++++++++++++---------------- src/qml/jit/qv4isel_masm.cpp | 2 +- src/qml/jit/qv4regalloc.cpp | 83 ++-- 10 files changed, 787 insertions(+), 816 deletions(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index cd204573c3..b9a3f1419c 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -2736,7 +2736,7 @@ bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR: for (QV4::IR::BasicBlock *bb : function->basicBlocks()) { for (QV4::IR::Stmt *s : bb->statements()) { - s->accept(this); + visit(s); if (!_canSimplify) return false; } @@ -2906,7 +2906,7 @@ void QQmlIRFunctionCleanser::clean() foreach (QV4::IR::Function *function, module->functions) { for (QV4::IR::BasicBlock *block : function->basicBlocks()) { for (QV4::IR::Stmt *s : block->statements()) { - s->accept(this); + visit(s); } } } @@ -2917,9 +2917,30 @@ void QQmlIRFunctionCleanser::clean() } } -void QQmlIRFunctionCleanser::visitClosure(QV4::IR::Closure *closure) +void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s) { - closure->value = newFunctionIndices.at(closure->value); + + switch (s->stmtKind) { + case QV4::IR::Stmt::PhiStmt: + // nothing to do + break; + default: + STMT_VISIT_ALL_KINDS(s); + break; + } +} + +void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e) +{ + switch (e->exprKind) { + case QV4::IR::Expr::ClosureExpr: { + auto closure = e->asClosure(); + closure->value = newFunctionIndices.at(closure->value); + } break; + default: + EXPR_VISIT_ALL_KINDS(e); + break; + } } QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index b55106ac7b..915626e183 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -358,7 +358,7 @@ private: const QQmlPropertyCacheVector &propertyCaches; }; -class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass, public QV4::IR::StmtVisitor +class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass { public: QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler); @@ -368,12 +368,30 @@ public: private: void reduceTranslationBindings(int objectIndex); - virtual void visitMove(QV4::IR::Move *move); - virtual void visitJump(QV4::IR::Jump *) {} - virtual void visitCJump(QV4::IR::CJump *) { discard(); } - virtual void visitExp(QV4::IR::Exp *) { discard(); } - virtual void visitPhi(QV4::IR::Phi *) {} - virtual void visitRet(QV4::IR::Ret *ret); + void visit(QV4::IR::Stmt *s) + { + switch (s->stmtKind) { + case QV4::IR::Stmt::MoveStmt: + visitMove(s->asMove()); + break; + case QV4::IR::Stmt::RetStmt: + visitRet(s->asRet()); + break; + case QV4::IR::Stmt::CJumpStmt: + discard(); + break; + case QV4::IR::Stmt::ExpStmt: + discard(); + break; + case QV4::IR::Stmt::JumpStmt: + break; + case QV4::IR::Stmt::PhiStmt: + break; + } + } + + void visitMove(QV4::IR::Move *move); + void visitRet(QV4::IR::Ret *ret); void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target); @@ -397,8 +415,7 @@ private: QVector irFunctionsToRemove; }; -class QQmlIRFunctionCleanser : public QQmlCompilePass, public QV4::IR::StmtVisitor, - public QV4::IR::ExprVisitor +class QQmlIRFunctionCleanser : public QQmlCompilePass { public: QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector &functionsToRemove); @@ -406,51 +423,13 @@ public: void clean(); private: - virtual void visitClosure(QV4::IR::Closure *closure); - - virtual void visitTemp(QV4::IR::Temp *) {} - virtual void visitArgLocal(QV4::IR::ArgLocal *) {} - virtual void visitMove(QV4::IR::Move *s) { - s->source->accept(this); - s->target->accept(this); - } - - virtual void visitConvert(QV4::IR::Convert *e) { e->expr->accept(this); } - virtual void visitPhi(QV4::IR::Phi *) { } - - virtual void visitExp(QV4::IR::Exp *s) { s->expr->accept(this); } - - virtual void visitJump(QV4::IR::Jump *) {} - virtual void visitCJump(QV4::IR::CJump *s) { s->cond->accept(this); } - virtual void visitRet(QV4::IR::Ret *s) { s->expr->accept(this); } - - virtual void visitConst(QV4::IR::Const *) {} - virtual void visitString(QV4::IR::String *) {} - virtual void visitRegExp(QV4::IR::RegExp *) {} - virtual void visitName(QV4::IR::Name *) {} - virtual void visitUnop(QV4::IR::Unop *e) { e->expr->accept(this); } - virtual void visitBinop(QV4::IR::Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(QV4::IR::Call *e) { - e->base->accept(this); - for (QV4::IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(QV4::IR::New *e) { - e->base->accept(this); - for (QV4::IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); + visit(s->source); + visit(s->target); } - virtual void visitSubscript(QV4::IR::Subscript *e) { - e->base->accept(this); - e->index->accept(this); - } - - virtual void visitMember(QV4::IR::Member *e) { - e->base->accept(this); - } + void visit(QV4::IR::Stmt *s); + void visit(QV4::IR::Expr *e); private: QV4::IR::Module *module; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index b452c4b3d9..967dca13a1 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -252,7 +252,7 @@ protected: _unhandled.removeLast(); } - s->accept(this); + visit(s); } if (IR::Jump *jump = s->asJump()) { @@ -275,7 +275,7 @@ protected: moves.order(); QList newMoves = moves.insertMoves(_currentBasicBlock, _function, true); foreach (IR::Move *move, newMoves) - move->accept(this); + visit(move); } } @@ -425,7 +425,7 @@ void InstructionSelection::run(int functionIndex) } } - s->accept(this); + visit(s); } } diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index 2ee776adf4..61fd11c435 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -111,17 +111,34 @@ public: }; namespace IR { -class Q_QML_PRIVATE_EXPORT IRDecoder: protected IR::StmtVisitor +class Q_QML_PRIVATE_EXPORT IRDecoder { public: IRDecoder() : _function(0) {} virtual ~IRDecoder() = 0; - virtual void visitPhi(IR::Phi *) {} - -public: // visitor methods for StmtVisitor: - virtual void visitMove(IR::Move *s); - virtual void visitExp(IR::Exp *s); + void visit(Stmt *s) + { + if (auto e = s->asExp()) { + visitExp(e); + } else if (auto m = s->asMove()) { + visitMove(m); + } else if (auto j = s->asJump()) { + visitJump(j); + } else if (auto c = s->asCJump()) { + visitCJump(c); + } else if (auto r = s->asRet()) { + visitRet(r); + } else if (auto p = s->asPhi()) { + visitPhi(p); + } else { + Q_UNREACHABLE(); + } + } + +private: // visitor methods for StmtVisitor: + void visitMove(IR::Move *s); + void visitExp(IR::Exp *s); public: // to implement by subclasses: virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0; @@ -179,6 +196,11 @@ public: // to implement by subclasses: virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) = 0; protected: + virtual void visitJump(IR::Jump *) = 0; + virtual void visitCJump(IR::CJump *) = 0; + virtual void visitRet(IR::Ret *) = 0; + virtual void visitPhi(IR::Phi *) {} + virtual void callBuiltin(IR::Call *c, IR::Expr *result); IR::Function *_function; // subclass needs to set diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h index 674fc01623..1755193d32 100644 --- a/src/qml/compiler/qv4isel_util_p.h +++ b/src/qml/compiler/qv4isel_util_p.h @@ -104,7 +104,7 @@ inline Primitive convertToValue(IR::Const *c) return Primitive::undefinedValue(); } -class ConvertTemps: protected IR::StmtVisitor, protected IR::ExprVisitor +class ConvertTemps { void renumber(IR::Temp *t) { @@ -132,7 +132,7 @@ protected: virtual void process(IR::Stmt *s) { - s->accept(this); + visit(s); } public: @@ -157,34 +157,28 @@ public: } protected: - virtual void visitConst(IR::Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(IR::Name *) {} - virtual void visitTemp(IR::Temp *e) { renumber(e); } - virtual void visitArgLocal(IR::ArgLocal *) {} - virtual void visitClosure(IR::Closure *) {} - virtual void visitConvert(IR::Convert *e) { e->expr->accept(this); } - virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } - virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(IR::Call *e) { - e->base->accept(this); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); + void visit(IR::Stmt *s) { + switch (s->stmtKind) { + case IR::Stmt::PhiStmt: + visitPhi(s->asPhi()); + break; + default: + STMT_VISIT_ALL_KINDS(s); + break; + } } - virtual void visitNew(IR::New *e) { - e->base->accept(this); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); + + virtual void visitPhi(IR::Phi *) + { Q_UNREACHABLE(); } + +private: + void visit(IR::Expr *e) { + if (auto temp = e->asTemp()) { + renumber(temp); + } else { + EXPR_VISIT_ALL_KINDS(e); + } } - virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(IR::Member *e) { e->base->accept(this); } - virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } - virtual void visitMove(IR::Move *s) { s->target->accept(this); s->source->accept(this); } - virtual void visitJump(IR::Jump *) {} - virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } - virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } - virtual void visitPhi(IR::Phi *) { Q_UNREACHABLE(); } }; } // namespace QV4 diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 370dfd0fae..b994d2f707 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -157,12 +157,13 @@ AluOp binaryOperator(int op) } } -struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor +class RemoveSharedExpressions { CloneExpr clone; std::vector subexpressions; // contains all the non-cloned subexpressions in the given function. sorted using std::lower_bound. Expr *uniqueExpr; +public: RemoveSharedExpressions(): uniqueExpr(0) {} void operator()(IR::Function *function) @@ -176,11 +177,12 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor clone.setBasicBlock(block); for (Stmt *s : block->statements()) { - s->accept(this); + visit(s); } } } +private: template Expr_ *cleanup(Expr_ *expr) { @@ -189,7 +191,7 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor subexpressions.insert(it, expr); IR::Expr *e = expr; qSwap(uniqueExpr, e); - expr->accept(this); + visit(expr); qSwap(uniqueExpr, e); return static_cast(e); } @@ -199,83 +201,45 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor return clone(expr); } - // statements - virtual void visitExp(Exp *s) + void visit(Stmt *s) { - s->expr = cleanup(s->expr); - } - - virtual void visitMove(Move *s) - { - s->target = cleanup(s->target); - s->source = cleanup(s->source); - } - - virtual void visitJump(Jump *) - { - // nothing to do for Jump statements - } - - virtual void visitCJump(CJump *s) - { - s->cond = cleanup(s->cond); - } - - virtual void visitRet(Ret *s) - { - s->expr = cleanup(s->expr); - } - - virtual void visitPhi(IR::Phi *) { Q_UNIMPLEMENTED(); } - - // expressions - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - - virtual void visitConvert(Convert *e) - { - e->expr = cleanup(e->expr); - } - - virtual void visitUnop(Unop *e) - { - e->expr = cleanup(e->expr); - } - - virtual void visitBinop(Binop *e) - { - e->left = cleanup(e->left); - e->right = cleanup(e->right); - } - - virtual void visitCall(Call *e) - { - e->base = cleanup(e->base); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr = cleanup(it->expr); - } - - virtual void visitNew(New *e) - { - e->base = cleanup(e->base); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr = cleanup(it->expr); - } - - virtual void visitSubscript(Subscript *e) - { - e->base = cleanup(e->base); - e->index = cleanup(e->index); + if (auto e = s->asExp()) { + e->expr = cleanup(e->expr); + } else if (auto m = s->asMove()) { + m->target = cleanup(m->target); + m->source = cleanup(m->source); + } else if (auto c = s->asCJump()) { + c->cond = cleanup(c->cond); + } else if (auto r = s->asRet()) { + r->expr = cleanup(r->expr); + } } - virtual void visitMember(Member *e) + void visit(Expr *e) { - e->base = cleanup(e->base); + if (auto c = e->asConvert()) { + c->expr = cleanup(c->expr); + } else if (auto u = e->asUnop()) { + u->expr = cleanup(u->expr); + } else if (auto b = e->asBinop()) { + b->left = cleanup(b->left); + b->right = cleanup(b->right); + } else if (auto c = e->asCall()) { + c->base = cleanup(c->base); + for (IR::ExprList *it = c->args; it; it = it->next) { + it->expr = cleanup(it->expr); + } + } else if (auto n = e->asNew()) { + n->base = cleanup(n->base); + for (IR::ExprList *it = n->args; it; it = it->next) { + it->expr = cleanup(it->expr); + } + } else if (auto s = e->asSubscript()) { + s->base = cleanup(s->base); + s->index = cleanup(s->index); + } else if (auto m = e->asMember()) { + m->base = cleanup(m->base); + } } }; @@ -548,75 +512,39 @@ ExprList *CloneExpr::clone(ExprList *list) return clonedList; } -void CloneExpr::visitConst(Const *e) -{ - cloned = cloneConst(e, block->function); -} - -void CloneExpr::visitString(String *e) -{ - cloned = block->STRING(e->value); -} - -void CloneExpr::visitRegExp(RegExp *e) -{ - cloned = block->REGEXP(e->value, e->flags); -} - -void CloneExpr::visitName(Name *e) -{ - cloned = cloneName(e, block->function); -} - -void CloneExpr::visitTemp(Temp *e) -{ - cloned = cloneTemp(e, block->function); -} - -void CloneExpr::visitArgLocal(ArgLocal *e) -{ - cloned = cloneArgLocal(e, block->function); -} - -void CloneExpr::visitClosure(Closure *e) -{ - cloned = block->CLOSURE(e->value); -} - -void CloneExpr::visitConvert(Convert *e) -{ - cloned = block->CONVERT(clone(e->expr), e->type); -} - -void CloneExpr::visitUnop(Unop *e) -{ - cloned = block->UNOP(e->op, clone(e->expr)); -} - -void CloneExpr::visitBinop(Binop *e) -{ - cloned = block->BINOP(e->op, clone(e->left), clone(e->right)); -} - -void CloneExpr::visitCall(Call *e) -{ - cloned = block->CALL(clone(e->base), clone(e->args)); -} - -void CloneExpr::visitNew(New *e) -{ - cloned = block->NEW(clone(e->base), clone(e->args)); -} - -void CloneExpr::visitSubscript(Subscript *e) -{ - cloned = block->SUBSCRIPT(clone(e->base), clone(e->index)); -} - -void CloneExpr::visitMember(Member *e) -{ - Expr *clonedBase = clone(e->base); - cloned = block->MEMBER(clonedBase, e->name, e->property, e->kind, e->idIndex); +void CloneExpr::visit(Expr *e) +{ + if (auto c = e->asConst()) { + cloned = cloneConst(c, block->function); + } else if (auto s = e->asString()) { + cloned = block->STRING(s->value); + } else if (auto r = e->asRegExp()) { + cloned = block->REGEXP(r->value, r->flags); + } else if (auto n = e->asName()) { + cloned = cloneName(n, block->function); + } else if (auto t = e->asTemp()) { + cloned = cloneTemp(t, block->function); + } else if (auto a = e->asArgLocal()) { + cloned = cloneArgLocal(a, block->function); + } else if (auto c = e->asClosure()) { + cloned = block->CLOSURE(c->value); + } else if (auto c = e->asConvert()) { + cloned = block->CONVERT(clone(c->expr), c->type); + } else if (auto u = e->asUnop()) { + cloned = block->UNOP(u->op, clone(u->expr)); + } else if (auto b = e->asBinop()) { + cloned = block->BINOP(b->op, clone(b->left), clone(b->right)); + } else if (auto c = e->asCall()) { + cloned = block->CALL(clone(c->base), clone(c->args)); + } else if (auto n = e->asNew()) { + cloned = block->NEW(clone(n->base), clone(n->args)); + } else if (auto s = e->asSubscript()) { + cloned = block->SUBSCRIPT(clone(s->base), clone(s->index)); + } else if (auto m = e->asMember()) { + cloned = block->MEMBER(clone(m->base), m->name, m->property, m->kind, m->idIndex); + } else { + Q_UNREACHABLE(); + } } IRPrinter::IRPrinter(QTextStream *out) @@ -632,17 +560,17 @@ IRPrinter::~IRPrinter() void IRPrinter::print(Stmt *s) { - s->accept(this); + visit(s); } void IRPrinter::print(const Expr &e) { - const_cast(&e)->accept(this); + visit(const_cast(&e)); } void IRPrinter::print(Expr *e) { - e->accept(this); + visit(e); } void IRPrinter::print(Function *f) @@ -696,7 +624,7 @@ void IRPrinter::print(BasicBlock *bb) QTextStream *prevOut = &os; std::swap(out, prevOut); addStmtNr(s); - s->accept(this); + visit(s); if (s->location.startLine) { out->flush(); for (int i = 58 - str.length(); i > 0; --i) @@ -713,10 +641,29 @@ void IRPrinter::print(BasicBlock *bb) std::swap(currentBB, bb); } +void IRPrinter::visit(Stmt *s) +{ + if (auto e = s->asExp()) { + visitExp(e); + } else if (auto m = s->asMove()) { + visitMove(m); + } else if (auto j = s->asJump()) { + visitJump(j); + } else if (auto c = s->asCJump()) { + visitCJump(c); + } else if (auto r = s->asRet()) { + visitRet(r); + } else if (auto p = s->asPhi()) { + visitPhi(p); + } else { + Q_UNREACHABLE(); + } +} + void IRPrinter::visitExp(Exp *s) { *out << "void "; - s->expr->accept(this); + visit(s->expr); } void IRPrinter::visitMove(Move *s) @@ -725,13 +672,13 @@ void IRPrinter::visitMove(Move *s) if (!s->swap && targetTemp->type != UnknownType) *out << typeName(targetTemp->type) << ' '; - s->target->accept(this); + visit(s->target); *out << ' '; if (s->swap) *out << "<=> "; else *out << "= "; - s->source->accept(this); + visit(s->source); } void IRPrinter::visitJump(Jump *s) @@ -742,7 +689,7 @@ void IRPrinter::visitJump(Jump *s) void IRPrinter::visitCJump(CJump *s) { *out << "if "; - s->cond->accept(this); + visit(s->cond); *out << " goto L" << s->iftrue->index() << " else goto L" << s->iffalse->index(); } @@ -752,7 +699,7 @@ void IRPrinter::visitRet(Ret *s) *out << "return"; if (s->expr) { *out << ' '; - s->expr->accept(this); + visit(s->expr); } } @@ -761,7 +708,7 @@ void IRPrinter::visitPhi(Phi *s) if (s->targetTemp->type != UnknownType) *out << typeName(s->targetTemp->type) << ' '; - s->targetTemp->accept(this); + visit(s->targetTemp); *out << " = phi "; for (int i = 0, ei = s->incoming.size(); i < ei; ++i) { if (i > 0) @@ -769,7 +716,42 @@ void IRPrinter::visitPhi(Phi *s) if (currentBB) *out << 'L' << currentBB->in.at(i)->index() << ": "; if (s->incoming[i]) - s->incoming[i]->accept(this); + visit(s->incoming[i]); + } +} + +void IRPrinter::visit(Expr *e) +{ + if (auto c = e->asConst()) { + visitConst(c); + } else if (auto s = e->asString()) { + visitString(s); + } else if (auto r = e->asRegExp()) { + visitRegExp(r); + } else if (auto n = e->asName()) { + visitName(n); + } else if (auto t = e->asTemp()) { + visitTemp(t); + } else if (auto a = e->asArgLocal()) { + visitArgLocal(a); + } else if (auto c = e->asClosure()) { + visitClosure(c); + } else if (auto c = e->asConvert()) { + visitConvert(c); + } else if (auto u = e->asUnop()) { + visitUnop(u); + } else if (auto b = e->asBinop()) { + visitBinop(b); + } else if (auto c = e->asCall()) { + visitCall(c); + } else if (auto n = e->asNew()) { + visitNew(n); + } else if (auto s = e->asSubscript()) { + visitSubscript(s); + } else if (auto m = e->asMember()) { + visitMember(m); + } else { + Q_UNREACHABLE(); } } @@ -867,32 +849,32 @@ void IRPrinter::visitClosure(Closure *e) void IRPrinter::visitConvert(Convert *e) { *out << "convert " << typeName(e->expr->type) << " to " << typeName(e->type) << ' '; - e->expr->accept(this); + visit(e->expr); } void IRPrinter::visitUnop(Unop *e) { *out << opname(e->op) << ' '; - e->expr->accept(this); + visit(e->expr); } void IRPrinter::visitBinop(Binop *e) { *out << opname(e->op) << ' '; - e->left->accept(this); + visit(e->left); *out << ", "; - e->right->accept(this); + visit(e->right); } void IRPrinter::visitCall(Call *e) { *out << "call "; - e->base->accept(this); + visit(e->base); *out << '('; for (ExprList *it = e->args; it; it = it->next) { if (it != e->args) *out << ", "; - it->expr->accept(this); + visit(it->expr); } *out << ')'; } @@ -900,21 +882,21 @@ void IRPrinter::visitCall(Call *e) void IRPrinter::visitNew(New *e) { *out << "new "; - e->base->accept(this); + visit(e->base); *out << '('; for (ExprList *it = e->args; it; it = it->next) { if (it != e->args) *out << ", "; - it->expr->accept(this); + visit(it->expr); } *out << ')'; } void IRPrinter::visitSubscript(Subscript *e) { - e->base->accept(this); + visit(e->base); *out << '['; - e->index->accept(this); + visit(e->index); *out << ']'; } @@ -924,7 +906,7 @@ void IRPrinter::visitMember(Member *e) && e->attachedPropertiesId != 0 && !e->base->asTemp()) *out << "[[attached property from " << e->attachedPropertiesId << "]]"; else - e->base->accept(this); + visit(e->base); *out << '.' << *e->name; #ifndef V4_BOOTSTRAP if (e->property) { diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 8ba641ab6a..0254ae224d 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -217,34 +217,6 @@ inline bool strictlyEqualTypes(Type t1, Type t2) QString typeName(Type t); -struct ExprVisitor { - virtual ~ExprVisitor() {} - virtual void visitConst(Const *) = 0; - virtual void visitString(String *) = 0; - virtual void visitRegExp(RegExp *) = 0; - virtual void visitName(Name *) = 0; - virtual void visitTemp(Temp *) = 0; - virtual void visitArgLocal(ArgLocal *) = 0; - virtual void visitClosure(Closure *) = 0; - virtual void visitConvert(Convert *) = 0; - virtual void visitUnop(Unop *) = 0; - virtual void visitBinop(Binop *) = 0; - virtual void visitCall(Call *) = 0; - virtual void visitNew(New *) = 0; - virtual void visitSubscript(Subscript *) = 0; - virtual void visitMember(Member *) = 0; -}; - -struct StmtVisitor { - virtual ~StmtVisitor() {} - virtual void visitExp(Exp *) = 0; - virtual void visitMove(Move *) = 0; - virtual void visitJump(Jump *) = 0; - virtual void visitCJump(CJump *) = 0; - virtual void visitRet(Ret *) = 0; - virtual void visitPhi(Phi *) = 0; -}; - struct MemberExpressionResolver; struct DiscoveredType { @@ -341,8 +313,6 @@ struct Q_AUTOTEST_EXPORT Expr { } Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {} - virtual ~Expr() {} - virtual void accept(ExprVisitor *) = 0; bool isLValue() const; Const *asConst() { return as(); } @@ -437,8 +407,6 @@ struct Const: Expr { this->value = value; } - virtual void accept(ExprVisitor *v) { v->visitConst(this); } - static bool classof(const Expr *c) { return c->exprKind == ConstExpr; } }; @@ -452,8 +420,6 @@ struct String: Expr { this->value = value; } - virtual void accept(ExprVisitor *v) { v->visitString(this); } - static bool classof(const Expr *c) { return c->exprKind == StringExpr; } }; @@ -476,8 +442,6 @@ struct RegExp: Expr { this->flags = flags; } - virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } - static bool classof(const Expr *c) { return c->exprKind == RegExpExpr; } }; @@ -517,8 +481,6 @@ struct Name: Expr { void init(const QString *id, quint32 line, quint32 column); void init(Builtin builtin, quint32 line, quint32 column); - virtual void accept(ExprVisitor *v) { v->visitName(this); } - static bool classof(const Expr *c) { return c->exprKind == NameExpr; } }; @@ -553,8 +515,6 @@ struct Q_AUTOTEST_EXPORT Temp: Expr { } bool isInvalid() const { return kind == Invalid; } - virtual void accept(ExprVisitor *v) { v->visitTemp(this); } - virtual Temp *asTemp() { return this; } static bool classof(const Expr *c) { return c->exprKind == TempExpr; } }; @@ -597,8 +557,6 @@ struct Q_AUTOTEST_EXPORT ArgLocal: Expr { ArgLocal(): Expr(ArgLocalExpr) {} - virtual void accept(ExprVisitor *v) { v->visitArgLocal(this); } - bool operator==(const ArgLocal &other) const { return index == other.index && scope == other.scope && kind == other.kind; } @@ -617,8 +575,6 @@ struct Closure: Expr { this->functionName = functionName; } - virtual void accept(ExprVisitor *v) { v->visitClosure(this); } - static bool classof(const Expr *c) { return c->exprKind == ClosureExpr; } }; @@ -633,8 +589,6 @@ struct Convert: Expr { this->type = type; } - virtual void accept(ExprVisitor *v) { v->visitConvert(this); } - static bool classof(const Expr *c) { return c->exprKind == ConvertExpr; } }; @@ -650,8 +604,6 @@ struct Unop: Expr { this->expr = expr; } - virtual void accept(ExprVisitor *v) { v->visitUnop(this); } - static bool classof(const Expr *c) { return c->exprKind == UnopExpr; } }; @@ -669,8 +621,6 @@ struct Binop: Expr { this->right = right; } - virtual void accept(ExprVisitor *v) { v->visitBinop(this); } - static bool classof(const Expr *c) { return c->exprKind == BinopExpr; } }; @@ -692,8 +642,6 @@ struct Call: Expr { return 0; } - virtual void accept(ExprVisitor *v) { v->visitCall(this); } - static bool classof(const Expr *c) { return c->exprKind == CallExpr; } }; @@ -715,8 +663,6 @@ struct New: Expr { return 0; } - virtual void accept(ExprVisitor *v) { v->visitNew(this); } - static bool classof(const Expr *c) { return c->exprKind == NewExpr; } }; @@ -732,8 +678,6 @@ struct Subscript: Expr { this->index = index; } - virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } - static bool classof(const Expr *c) { return c->exprKind == SubscriptExpr; } }; @@ -789,8 +733,6 @@ struct Member: Expr { this->kind = kind; } - virtual void accept(ExprVisitor *v) { v->visitMember(this); } - static bool classof(const Expr *c) { return c->exprKind == MemberExpr; } }; @@ -829,17 +771,8 @@ struct Stmt { explicit Stmt(int id, StmtKind stmtKind): _id(id), stmtKind(stmtKind) {} - virtual ~Stmt() - { -#ifdef Q_CC_MSVC - // MSVC complains about potential memory leaks if a destructor never returns. -#else - Q_UNREACHABLE(); -#endif - } - virtual Stmt *asTerminator() { return 0; } + Stmt *asTerminator(); - virtual void accept(StmtVisitor *) = 0; Exp *asExp() { return as(); } Move *asMove() { return as(); } Jump *asJump() { return as(); } @@ -900,8 +833,6 @@ struct Exp: Stmt { this->expr = expr; } - virtual void accept(StmtVisitor *v) { v->visitExp(this); } - static bool classof(const Stmt *c) { return c->stmtKind == ExpStmt; } }; @@ -919,8 +850,6 @@ struct Move: Stmt { this->swap = false; } - virtual void accept(StmtVisitor *v) { v->visitMove(this); } - static bool classof(const Stmt *c) { return c->stmtKind == MoveStmt; } }; @@ -934,10 +863,6 @@ struct Jump: Stmt { this->target = target; } - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitJump(this); } - static bool classof(const Stmt *c) { return c->stmtKind == JumpStmt; } }; @@ -957,10 +882,6 @@ struct CJump: Stmt { this->parent = parent; } - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitCJump(this); } - static bool classof(const Stmt *c) { return c->stmtKind == CJumpStmt; } }; @@ -974,10 +895,6 @@ struct Ret: Stmt { this->expr = expr; } - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitRet(this); } - static bool classof(const Stmt *c) { return c->stmtKind == RetStmt; } }; @@ -991,14 +908,25 @@ struct Phi: Stmt { Phi(int id): Stmt(id, PhiStmt) {} - virtual void accept(StmtVisitor *v) { v->visitPhi(this); } - static bool classof(const Stmt *c) { return c->stmtKind == PhiStmt; } void destroyData() { incoming.~VarLengthArray(); } }; +inline Stmt *Stmt::asTerminator() +{ + if (auto s = asJump()) { + return s; + } else if (auto s = asCJump()) { + return s; + } else if (auto s = asRet()) { + return s; + } else { + return nullptr; + } +} + struct Q_QML_PRIVATE_EXPORT Module { QQmlJS::MemoryPool pool; QVector functions; @@ -1397,7 +1325,7 @@ private: int _statementCount; }; -class CloneExpr: protected IR::ExprVisitor +class CloneExpr { public: explicit CloneExpr(IR::BasicBlock *block = 0); @@ -1415,7 +1343,7 @@ public: { Expr *c = expr; qSwap(cloned, c); - expr->accept(this); + visit(expr); qSwap(cloned, c); return static_cast(c); } @@ -1458,23 +1386,10 @@ public: return newArgLocal; } -protected: +private: IR::ExprList *clone(IR::ExprList *list); - virtual void visitConst(Const *); - virtual void visitString(String *); - virtual void visitRegExp(RegExp *); - virtual void visitName(Name *); - virtual void visitTemp(Temp *); - virtual void visitArgLocal(ArgLocal *); - virtual void visitClosure(Closure *); - virtual void visitConvert(Convert *); - virtual void visitUnop(Unop *); - virtual void visitBinop(Binop *); - virtual void visitCall(Call *); - virtual void visitNew(New *); - virtual void visitSubscript(Subscript *); - virtual void visitMember(Member *); + void visit(Expr *e); protected: IR::BasicBlock *block; @@ -1483,7 +1398,7 @@ private: IR::Expr *cloned; }; -class Q_AUTOTEST_EXPORT IRPrinter: public StmtVisitor, public ExprVisitor +class Q_AUTOTEST_EXPORT IRPrinter { public: IRPrinter(QTextStream *out); @@ -1496,6 +1411,7 @@ public: virtual void print(Function *f); virtual void print(BasicBlock *bb); + void visit(Stmt *s); virtual void visitExp(Exp *s); virtual void visitMove(Move *s); virtual void visitJump(Jump *s); @@ -1503,6 +1419,7 @@ public: virtual void visitRet(Ret *s); virtual void visitPhi(Phi *s); + void visit(Expr *e); virtual void visitConst(Const *e); virtual void visitString(String *e); virtual void visitRegExp(RegExp *e); diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 6965d839ab..90e72facfc 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -898,7 +898,7 @@ private: } }; -class VariableCollector: public StmtVisitor, ExprVisitor { +class VariableCollector { std::vector _allTemps; std::vector _defsites; std::vector > A_orig; @@ -946,7 +946,7 @@ public: currentBB = bb; killed.assign(function->tempCount, false); for (Stmt *s : bb->statements()) - s->accept(this); + visit(s); } } @@ -971,62 +971,45 @@ public: return nonLocals.at(var.index); } -protected: - virtual void visitPhi(Phi *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { e->base->accept(this); } - virtual void visitExp(Exp *s) { s->expr->accept(this); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitMove(Move *s) { - s->source->accept(this); +private: + void visit(Stmt *s) + { + if (s->asPhi()) { + // nothing to do + } else if (auto move = s->asMove()) { + visit(move->source); - if (Temp *t = s->target->asTemp()) { - addTemp(t); + if (Temp *t = move->target->asTemp()) { + addTemp(t); - if (isCollectable(t)) { - _defsites[t->index].insert(currentBB); - addDefInCurrentBlock(t); + if (isCollectable(t)) { + _defsites[t->index].insert(currentBB); + addDefInCurrentBlock(t); - // For semi-pruned SSA: - killed.setBit(t->index); + // For semi-pruned SSA: + killed.setBit(t->index); + } + } else { + visit(move->target); } } else { - s->target->accept(this); + STMT_VISIT_ALL_KINDS(s) } } - virtual void visitTemp(Temp *t) + void visit(Expr *e) { - addTemp(t); + if (auto t = e->asTemp()) { + addTemp(t); - if (isCollectable(t)) - if (!killed.at(t->index)) - nonLocals.setBit(t->index); + if (isCollectable(t)) { + if (!killed.at(t->index)) { + nonLocals.setBit(t->index); + } + } + } else { + EXPR_VISIT_ALL_KINDS(e); + } } }; @@ -1345,7 +1328,7 @@ void insertPhiNode(const Temp &a, BasicBlock *y, IR::Function *f) { // // Undo(t, c) = // mapping[t] = c -class VariableRenamer: public StmtVisitor, public ExprVisitor +class VariableRenamer { Q_DISABLE_COPY(VariableRenamer) @@ -1466,7 +1449,7 @@ private: for (Stmt *s : bb->statements()) { currentStmt = s; - s->accept(this); + visit(s); } for (BasicBlock *Y : bb->out) { @@ -1532,23 +1515,35 @@ private: return newIndex; } -protected: - virtual void visitTemp(Temp *e) { // only called for uses, not defs -// qDebug()<<"I: replacing use of"<index<<"with"<index].top(); - e->index = currentNumber(*e); - e->kind = Temp::VirtualRegister; - defUses.addUse(*e, currentStmt); - } +private: + void visit(Stmt *s) + { + if (auto move = s->asMove()) { + // uses: + visit(move->source); - virtual void visitMove(Move *s) { - // uses: - s->source->accept(this); + // defs: + if (Temp *t = move->target->asTemp()) { + renameTemp(t); + } else { + visit(move->target); + } + } else if (auto phi = s->asPhi()) { + renameTemp(phi->targetTemp); + } else { + STMT_VISIT_ALL_KINDS(s); + } + } - // defs: - if (Temp *t = s->target->asTemp()) - renameTemp(t); - else - s->target->accept(this); + void visit(Expr *e) + { + if (auto temp = e->asTemp()) { + temp->index = currentNumber(*temp); + temp->kind = Temp::VirtualRegister; + defUses.addUse(*temp, currentStmt); + } else { + EXPR_VISIT_ALL_KINDS(e); + } } void renameTemp(Temp *t) { // only called for defs, not uses @@ -1558,44 +1553,6 @@ protected: t->index = newIdx; defUses.addDef(t, currentStmt, currentBB); } - - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitPhi(Phi *s) { renameTemp(s->targetTemp); } - - virtual void visitExp(Exp *s) { s->expr->accept(this); } - - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitSubscript(Subscript *e) { - e->base->accept(this); - e->index->accept(this); - } - - virtual void visitMember(Member *e) { - e->base->accept(this); - } }; // This function converts the IR to semi-pruned SSA form. For details about SSA and the algorightm, @@ -1922,7 +1879,7 @@ private: } }; -class SideEffectsChecker: public ExprVisitor +class SideEffectsChecker { bool _sideEffect; @@ -1931,11 +1888,14 @@ public: : _sideEffect(false) {} + ~SideEffectsChecker() + {} + bool hasSideEffects(Expr *expr) { bool sideEffect = false; qSwap(_sideEffect, sideEffect); - expr->accept(this); + visit(expr); qSwap(_sideEffect, sideEffect); return sideEffect; } @@ -1948,12 +1908,35 @@ protected: bool seenSideEffects() const { return _sideEffect; } -protected: - void visitConst(Const *) Q_DECL_OVERRIDE {} - void visitString(IR::String *) Q_DECL_OVERRIDE {} - void visitRegExp(IR::RegExp *) Q_DECL_OVERRIDE {} + void visit(Expr *e) + { + if (auto n = e->asName()) { + visitName(n); + } else if (auto t = e->asTemp()) { + visitTemp(t); + } else if (auto c = e->asClosure()) { + visitClosure(c); + } else if (auto c = e->asConvert()) { + visitConvert(c); + } else if (auto u = e->asUnop()) { + visitUnop(u); + } else if (auto b = e->asBinop()) { + visitBinop(b); + } else if (auto c = e->asCall()) { + visitCall(c); + } else if (auto n = e->asNew()) { + visitNew(n); + } else if (auto s = e->asSubscript()) { + visitSubscript(s); + } else if (auto m = e->asMember()) { + visitMember(m); + } + } - void visitName(Name *e) Q_DECL_OVERRIDE { + virtual void visitTemp(Temp *) {} + +private: + void visitName(Name *e) { if (e->freeOfSideEffects) return; // TODO: maybe we can distinguish between built-ins of which we know that they do not have @@ -1962,15 +1945,12 @@ protected: markAsSideEffect(); } - void visitTemp(Temp *) Q_DECL_OVERRIDE {} - void visitArgLocal(ArgLocal *) Q_DECL_OVERRIDE {} - - void visitClosure(Closure *) Q_DECL_OVERRIDE { + void visitClosure(Closure *) { markAsSideEffect(); } - void visitConvert(Convert *e) Q_DECL_OVERRIDE { - e->expr->accept(this); + void visitConvert(Convert *e) { + visit(e->expr); switch (e->expr->type) { case QObjectType: @@ -1983,8 +1963,8 @@ protected: } } - void visitUnop(Unop *e) Q_DECL_OVERRIDE { - e->expr->accept(this); + void visitUnop(Unop *e) { + visit(e->expr); switch (e->op) { case OpUPlus: @@ -2001,7 +1981,7 @@ protected: } } - void visitBinop(Binop *e) Q_DECL_OVERRIDE { + void visitBinop(Binop *e) { // TODO: prune parts that don't have a side-effect. For example, in: // function f(x) { +x+1; return 0; } // we can prune the binop and leave the unop/conversion. @@ -2013,30 +1993,30 @@ protected: markAsSideEffect(); } - void visitSubscript(Subscript *e) Q_DECL_OVERRIDE { - e->base->accept(this); - e->index->accept(this); + void visitSubscript(Subscript *e) { + visit(e->base); + visit(e->index); markAsSideEffect(); } - void visitMember(Member *e) Q_DECL_OVERRIDE { - e->base->accept(this); + void visitMember(Member *e) { + visit(e->base); if (e->freeOfSideEffects) return; markAsSideEffect(); } - void visitCall(Call *e) Q_DECL_OVERRIDE { - e->base->accept(this); + void visitCall(Call *e) { + visit(e->base); for (ExprList *args = e->args; args; args = args->next) - args->expr->accept(this); + visit(args->expr); markAsSideEffect(); // TODO: there are built-in functions that have no side effect. } - void visitNew(New *e) Q_DECL_OVERRIDE { - e->base->accept(this); + void visitNew(New *e) { + visit(e->base); for (ExprList *args = e->args; args; args = args->next) - args->expr->accept(this); + visit(args->expr); markAsSideEffect(); // TODO: there are built-in types that have no side effect. } }; @@ -2045,21 +2025,19 @@ class EliminateDeadCode: public SideEffectsChecker { DefUses &_defUses; StatementWorklist &_worklist; - QVector _collectedTemps; + QVarLengthArray _collectedTemps; public: EliminateDeadCode(DefUses &defUses, StatementWorklist &worklist) : _defUses(defUses) , _worklist(worklist) - { - _collectedTemps.reserve(8); - } + {} void run(Expr *&expr, Stmt *stmt) { _collectedTemps.clear(); if (!hasSideEffects(expr)) { expr = 0; - foreach (Temp *t, _collectedTemps) { + for (Temp *t : _collectedTemps) { _defUses.removeUse(stmt, *t); _worklist += _defUses.defStmt(*t); } @@ -2067,13 +2045,13 @@ public: } protected: - void visitTemp(Temp *e) Q_DECL_OVERRIDE + void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL { _collectedTemps.append(e); } }; -class PropagateTempTypes: public StmtVisitor, ExprVisitor +class PropagateTempTypes { const DefUses &defUses; UntypedTemp theTemp; @@ -2089,64 +2067,31 @@ public: newType = type; theTemp = temp; if (Stmt *defStmt = defUses.defStmt(temp.temp)) - defStmt->accept(this); + visit(defStmt); foreach (Stmt *use, defUses.uses(temp.temp)) - use->accept(this); - } - -protected: - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *e) { - if (theTemp == UntypedTemp(*e)) { - e->type = static_cast(newType.type); - e->memberResolver = newType.memberResolver; - } - } - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitSubscript(Subscript *e) { - e->base->accept(this); - e->index->accept(this); + visit(use); } - virtual void visitMember(Member *e) { - e->base->accept(this); - } - - virtual void visitExp(Exp *s) {s->expr->accept(this);} - virtual void visitMove(Move *s) { - s->source->accept(this); - s->target->accept(this); +private: + void visit(Stmt *s) + { + STMT_VISIT_ALL_KINDS(s); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitPhi(Phi *s) { - s->targetTemp->accept(this); - foreach (Expr *e, s->incoming) - e->accept(this); + void visit(Expr *e) + { + if (auto temp = e->asTemp()) { + if (theTemp == UntypedTemp(*temp)) { + temp->type = static_cast(newType.type); + temp->memberResolver = newType.memberResolver; + } + } else { + EXPR_VISIT_ALL_KINDS(e); + } } }; -class TypeInference: public StmtVisitor, public ExprVisitor +class TypeInference { enum { DebugTypeInference = 0 }; @@ -2248,7 +2193,7 @@ private: TypingResult ty; std::swap(_ty, ty); std::swap(_currentStmt, s); - _currentStmt->accept(this); + visit(_currentStmt); std::swap(_currentStmt, s); std::swap(_ty, ty); return ty.fullyTyped; @@ -2257,7 +2202,7 @@ private: TypingResult run(Expr *e) { TypingResult ty; std::swap(_ty, ty); - e->accept(this); + visit(e); std::swap(_ty, ty); if (ty.type != UnknownType) @@ -2299,39 +2244,74 @@ private: } } -protected: - virtual void visitConst(Const *e) { - if (e->type & NumberType) { - if (canConvertToSignedInteger(e->value)) +private: + void visit(Expr *e) + { + if (auto c = e->asConst()) { + visitConst(c); + } else if (auto s = e->asString()) { + visitString(s); + } else if (auto r = e->asRegExp()) { + visitRegExp(r); + } else if (auto n = e->asName()) { + visitName(n); + } else if (auto t = e->asTemp()) { + visitTemp(t); + } else if (auto a = e->asArgLocal()) { + visitArgLocal(a); + } else if (auto c = e->asClosure()) { + visitClosure(c); + } else if (auto c = e->asConvert()) { + visitConvert(c); + } else if (auto u = e->asUnop()) { + visitUnop(u); + } else if (auto b = e->asBinop()) { + visitBinop(b); + } else if (auto c = e->asCall()) { + visitCall(c); + } else if (auto n = e->asNew()) { + visitNew(n); + } else if (auto s = e->asSubscript()) { + visitSubscript(s); + } else if (auto m = e->asMember()) { + visitMember(m); + } else { + Q_UNREACHABLE(); + } + } + + void visitConst(Const *c) { + if (c->type & NumberType) { + if (canConvertToSignedInteger(c->value)) _ty = TypingResult(SInt32Type); - else if (canConvertToUnsignedInteger(e->value)) + else if (canConvertToUnsignedInteger(c->value)) _ty = TypingResult(UInt32Type); else - _ty = TypingResult(e->type); + _ty = TypingResult(c->type); } else - _ty = TypingResult(e->type); + _ty = TypingResult(c->type); } - virtual void visitString(IR::String *) { _ty = TypingResult(StringType); } - virtual void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); } - virtual void visitName(Name *) { _ty = TypingResult(VarType); } - virtual void visitTemp(Temp *e) { + void visitString(IR::String *) { _ty = TypingResult(StringType); } + void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); } + void visitName(Name *) { _ty = TypingResult(VarType); } + void visitTemp(Temp *e) { if (e->memberResolver && e->memberResolver->isValid()) _ty = TypingResult(e->memberResolver); else _ty = TypingResult(_tempTypes[e->index]); setType(e, _ty.type); } - virtual void visitArgLocal(ArgLocal *e) { + void visitArgLocal(ArgLocal *e) { _ty = TypingResult(VarType); setType(e, _ty.type); } - virtual void visitClosure(Closure *) { _ty = TypingResult(VarType); } - virtual void visitConvert(Convert *e) { + void visitClosure(Closure *) { _ty = TypingResult(VarType); } + void visitConvert(Convert *e) { _ty = TypingResult(e->type); } - virtual void visitUnop(Unop *e) { + void visitUnop(Unop *e) { _ty = run(e->expr); switch (e->op) { case OpUPlus: _ty.type = DoubleType; return; @@ -2348,7 +2328,7 @@ protected: } } - virtual void visitBinop(Binop *e) { + void visitBinop(Binop *e) { TypingResult leftTy = run(e->left); TypingResult rightTy = run(e->right); _ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped; @@ -2406,24 +2386,24 @@ protected: } } - virtual void visitCall(Call *e) { + void visitCall(Call *e) { _ty = run(e->base); for (ExprList *it = e->args; it; it = it->next) _ty.fullyTyped &= run(it->expr).fullyTyped; _ty.type = VarType; } - virtual void visitNew(New *e) { + void visitNew(New *e) { _ty = run(e->base); for (ExprList *it = e->args; it; it = it->next) _ty.fullyTyped &= run(it->expr).fullyTyped; _ty.type = VarType; } - virtual void visitSubscript(Subscript *e) { + void visitSubscript(Subscript *e) { _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped; _ty.type = VarType; } - virtual void visitMember(Member *e) { + void visitMember(Member *e) { _ty = run(e->base); if (_ty.fullyTyped && _ty.type.memberResolver && _ty.type.memberResolver->isValid()) { @@ -2433,8 +2413,27 @@ protected: _ty.type = VarType; } - virtual void visitExp(Exp *s) { _ty = run(s->expr); } - virtual void visitMove(Move *s) { + void visit(Stmt *s) + { + if (auto e = s->asExp()) { + visitExp(e); + } else if (auto m = s->asMove()) { + visitMove(m); + } else if (auto j = s->asJump()) { + visitJump(j); + } else if (auto c = s->asCJump()) { + visitCJump(c); + } else if (auto r = s->asRet()) { + visitRet(r); + } else if (auto p = s->asPhi()) { + visitPhi(p); + } else { + Q_UNREACHABLE(); + } + } + + void visitExp(Exp *s) { _ty = run(s->expr); } + void visitMove(Move *s) { if (Temp *t = s->target->asTemp()) { if (Name *n = s->source->asName()) { if (n->builtin == Name::builtin_qml_context) { @@ -2455,10 +2454,10 @@ protected: _ty.fullyTyped &= sourceTy.fullyTyped; } - virtual void visitJump(Jump *) { _ty = TypingResult(MissingType); } - virtual void visitCJump(CJump *s) { _ty = run(s->cond); } - virtual void visitRet(Ret *s) { _ty = run(s->expr); } - virtual void visitPhi(Phi *s) { + void visitJump(Jump *) { _ty = TypingResult(MissingType); } + void visitCJump(CJump *s) { _ty = run(s->cond); } + void visitRet(Ret *s) { _ty = run(s->expr); } + void visitPhi(Phi *s) { _ty = run(s->incoming[0]); for (int i = 1, ei = s->incoming.size(); i != ei; ++i) { TypingResult ty = run(s->incoming[i]); @@ -2677,14 +2676,15 @@ void convertConst(Const *c, Type targetType) c->type = targetType; } -class TypePropagation: public StmtVisitor, public ExprVisitor { +class TypePropagation +{ DefUses &_defUses; Type _ty; IR::Function *_f; bool run(Expr *&e, Type requestedType = UnknownType, bool insertConversion = true) { qSwap(_ty, requestedType); - e->accept(this); + visit(e); qSwap(_ty, requestedType); if (requestedType != UnknownType) { @@ -2731,7 +2731,7 @@ public: for (Stmt *s : bb->statements()) { _currStmt = s; - s->accept(this); + visit(s); } foreach (const Conversion &conversion, _conversions) { @@ -2817,8 +2817,29 @@ public: } } -protected: - virtual void visitConst(Const *c) { +private: + void visit(Expr *e) + { + if (auto c = e->asConst()) { + visitConst(c); + } else if (auto c = e->asConvert()) { + run(c->expr, c->type); + } else if (auto u = e->asUnop()) { + run(u->expr, u->type); + } else if (auto b = e->asBinop()) { + visitBinop(b); + } else if (auto c = e->asCall()) { + visitCall(c); + } else if (auto n = e->asNew()) { + visitNew(n); + } else if (auto s = e->asSubscript()) { + visitSubscript(s); + } else if (auto m = e->asMember()) { + visitMember(m); + } + } + + void visitConst(Const *c) { if (_ty & NumberType && c->type & NumberType) { if (_ty == SInt32Type) c->value = QV4::Primitive::toInt32(c->value); @@ -2828,15 +2849,7 @@ protected: } } - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { run(e->expr, e->type); } - virtual void visitUnop(Unop *e) { run(e->expr, e->type); } - virtual void visitBinop(Binop *e) { + void visitBinop(Binop *e) { // FIXME: This routine needs more tuning! switch (e->op) { case OpAdd: @@ -2887,20 +2900,36 @@ protected: Q_UNREACHABLE(); } } - virtual void visitCall(Call *e) { + void visitCall(Call *e) { run(e->base); for (ExprList *it = e->args; it; it = it->next) run(it->expr); } - virtual void visitNew(New *e) { + void visitNew(New *e) { run(e->base); for (ExprList *it = e->args; it; it = it->next) run(it->expr); } - virtual void visitSubscript(Subscript *e) { run(e->base); run(e->index); } - virtual void visitMember(Member *e) { run(e->base); } - virtual void visitExp(Exp *s) { run(s->expr); } - virtual void visitMove(Move *s) { + void visitSubscript(Subscript *e) { run(e->base); run(e->index); } + void visitMember(Member *e) { run(e->base); } + + void visit(Stmt *s) + { + if (auto e = s->asExp()) { + visitExp(e); + } else if (auto m = s->asMove()) { + visitMove(m); + } else if (auto c = s->asCJump()) { + visitCJump(c); + } else if (auto r = s->asRet()) { + visitRet(r); + } else if (auto p = s->asPhi()) { + visitPhi(p); + } + } + + void visitExp(Exp *s) { run(s->expr); } + void visitMove(Move *s) { if (s->source->asConvert()) return; // this statement got inserted for a phi-node type conversion @@ -2925,12 +2954,11 @@ protected: run(s->source, s->target->type, !inhibitConversion); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { + void visitCJump(CJump *s) { run(s->cond, BoolType); } - virtual void visitRet(Ret *s) { run(s->expr); } - virtual void visitPhi(Phi *s) { + void visitRet(Ret *s) { run(s->expr); } + void visitPhi(Phi *s) { Type ty = s->targetTemp->type; for (int i = 0, ei = s->incoming.size(); i != ei; ++i) run(s->incoming[i], ty); @@ -3504,7 +3532,7 @@ static Expr *clone(Expr *e, IR::Function *function) { } } -class ExprReplacer: public StmtVisitor, public ExprVisitor +class ExprReplacer { DefUses &_defUses; IR::Function* _function; @@ -3535,7 +3563,7 @@ public: // qout << " " << uses.size() << " uses:"<dump(qout);qout<<"\n"; - use->accept(this); + visit(use); // qout<<" -> ";use->dump(qout);qout<<"\n"; W += use; if (newUses) @@ -3546,45 +3574,101 @@ public: qSwap(_toReplace, toReplace); } -protected: - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { check(e->expr); } - virtual void visitUnop(Unop *e) { check(e->expr); } - virtual void visitBinop(Binop *e) { check(e->left); check(e->right); } - virtual void visitCall(Call *e) { +private: + void visit(Expr *e) + { + if (auto c = e->asConst()) { + visitConst(c); + } else if (auto s = e->asString()) { + visitString(s); + } else if (auto r = e->asRegExp()) { + visitRegExp(r); + } else if (auto n = e->asName()) { + visitName(n); + } else if (auto t = e->asTemp()) { + visitTemp(t); + } else if (auto a = e->asArgLocal()) { + visitArgLocal(a); + } else if (auto c = e->asClosure()) { + visitClosure(c); + } else if (auto c = e->asConvert()) { + visitConvert(c); + } else if (auto u = e->asUnop()) { + visitUnop(u); + } else if (auto b = e->asBinop()) { + visitBinop(b); + } else if (auto c = e->asCall()) { + visitCall(c); + } else if (auto n = e->asNew()) { + visitNew(n); + } else if (auto s = e->asSubscript()) { + visitSubscript(s); + } else if (auto m = e->asMember()) { + visitMember(m); + } else { + Q_UNREACHABLE(); + } + } + + void visitConst(Const *) {} + void visitString(IR::String *) {} + void visitRegExp(IR::RegExp *) {} + void visitName(Name *) {} + void visitTemp(Temp *) {} + void visitArgLocal(ArgLocal *) {} + void visitClosure(Closure *) {} + void visitConvert(Convert *e) { check(e->expr); } + void visitUnop(Unop *e) { check(e->expr); } + void visitBinop(Binop *e) { check(e->left); check(e->right); } + void visitCall(Call *e) { check(e->base); for (ExprList *it = e->args; it; it = it->next) check(it->expr); } - virtual void visitNew(New *e) { + void visitNew(New *e) { check(e->base); for (ExprList *it = e->args; it; it = it->next) check(it->expr); } - virtual void visitSubscript(Subscript *e) { check(e->base); check(e->index); } - virtual void visitMember(Member *e) { check(e->base); } - virtual void visitExp(Exp *s) { check(s->expr); } - virtual void visitMove(Move *s) { check(s->target); check(s->source); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { check(s->cond); } - virtual void visitRet(Ret *s) { check(s->expr); } - virtual void visitPhi(Phi *s) { + void visitSubscript(Subscript *e) { check(e->base); check(e->index); } + void visitMember(Member *e) { check(e->base); } + + void visit(Stmt *s) + { + if (auto e = s->asExp()) { + visitExp(e); + } else if (auto m = s->asMove()) { + visitMove(m); + } else if (auto j = s->asJump()) { + visitJump(j); + } else if (auto c = s->asCJump()) { + visitCJump(c); + } else if (auto r = s->asRet()) { + visitRet(r); + } else if (auto p = s->asPhi()) { + visitPhi(p); + } else { + Q_UNREACHABLE(); + } + } + + void visitExp(Exp *s) { check(s->expr); } + void visitMove(Move *s) { check(s->target); check(s->source); } + void visitJump(Jump *) {} + void visitCJump(CJump *s) { check(s->cond); } + void visitRet(Ret *s) { check(s->expr); } + void visitPhi(Phi *s) { for (int i = 0, ei = s->incoming.size(); i != ei; ++i) check(s->incoming[i]); } private: void check(Expr *&e) { - if (equals(e, _toReplace)) + if (equals(e, _toReplace)) { e = clone(_replacement, _function); - else - e->accept(this); + } else { + visit(e); + } } // This only calculates equality for everything needed by constant propagation @@ -4153,7 +4237,8 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df) } //### TODO: use DefUses from the optimizer, because it already has all this information -class InputOutputCollector: protected StmtVisitor, protected ExprVisitor { +class InputOutputCollector +{ void setOutput(Temp *out) { Q_ASSERT(!output); @@ -4170,48 +4255,33 @@ public: void collect(Stmt *s) { inputs.resize(0); output = 0; - s->accept(this); + visit(s); } -protected: - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *e) { - inputs.push_back(e); - } - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { e->base->accept(this); } - virtual void visitExp(Exp *s) { s->expr->accept(this); } - virtual void visitMove(Move *s) { - s->source->accept(this); - if (Temp *t = s->target->asTemp()) { - setOutput(t); +private: + void visit(Expr *e) + { + if (auto t = e->asTemp()) { + inputs.push_back(t); } else { - s->target->accept(this); + EXPR_VISIT_ALL_KINDS(e); } } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitPhi(Phi *) { - // Handled separately + + void visit(Stmt *s) + { + if (auto m = s->asMove()) { + visit(m->source); + if (Temp *t = m->target->asTemp()) { + setOutput(t); + } else { + visit(m->target); + } + } else if (s->asPhi()) { + // Handled separately + } else { + STMT_VISIT_ALL_KINDS(s); + } } }; @@ -4381,7 +4451,7 @@ void removeUnreachleBlocks(IR::Function *function) function->renumberBasicBlocks(); } -class ConvertArgLocals: protected StmtVisitor, protected ExprVisitor +class ConvertArgLocals { public: ConvertArgLocals(IR::Function *function) @@ -4419,10 +4489,13 @@ public: } } - for (BasicBlock *bb : function->basicBlocks()) - if (!bb->isRemoved()) - for (Stmt *s : bb->statements()) - s->accept(this); + for (BasicBlock *bb : function->basicBlocks()) { + if (!bb->isRemoved()) { + for (Stmt *s : bb->statements()) { + visit(s); + } + } + } if (convertArgs && function->formals.size() > 0) function->basicBlock(0)->prependStatements(extraMoves); @@ -4430,39 +4503,45 @@ public: function->locals.clear(); } -protected: - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { check(e->expr); } - virtual void visitUnop(Unop *e) { check(e->expr); } - virtual void visitBinop(Binop *e) { check(e->left); check(e->right); } - virtual void visitCall(Call *e) { - check(e->base); - for (ExprList *it = e->args; it; it = it->next) - check(it->expr); - } - virtual void visitNew(New *e) { - check(e->base); - for (ExprList *it = e->args; it; it = it->next) - check(it->expr); +private: + void visit(Stmt *s) + { + if (auto e = s->asExp()) { + check(e->expr); + } else if (auto m = s->asMove()) { + check(m->target); check(m->source); + } else if (auto c = s->asCJump()) { + check(c->cond); + } else if (auto r = s->asRet()) { + check(r->expr); + } } - virtual void visitSubscript(Subscript *e) { check(e->base); check(e->index); } - virtual void visitMember(Member *e) { check(e->base); } - virtual void visitExp(Exp *s) { check(s->expr); } - virtual void visitMove(Move *s) { check(s->target); check(s->source); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { check(s->cond); } - virtual void visitRet(Ret *s) { check(s->expr); } - virtual void visitPhi(Phi *) { - Q_UNREACHABLE(); + + void visit(Expr *e) + { + if (auto c = e->asConvert()) { + check(c->expr); + } else if (auto u = e->asUnop()) { + check(u->expr); + } else if (auto b = e->asBinop()) { + check(b->left); check(b->right); + } else if (auto c = e->asCall()) { + check(c->base); + for (ExprList *it = c->args; it; it = it->next) { + check(it->expr); + } + } else if (auto n = e->asNew()) { + check(n->base); + for (ExprList *it = n->args; it; it = it->next) { + check(it->expr); + } + } else if (auto s = e->asSubscript()) { + check(s->base); check(s->index); + } else if (auto m = e->asMember()) { + check(m->base); + } } -private: void check(Expr *&e) { if (ArgLocal *al = e->asArgLocal()) { if (al->kind == ArgLocal::Local) { @@ -4475,7 +4554,7 @@ private: e = t; } } else { - e->accept(this); + visit(e); } } @@ -4498,7 +4577,7 @@ private: std::vector tempForLocal; }; -class CloneBasicBlock: protected IR::StmtVisitor, protected CloneExpr +class CloneBasicBlock: protected CloneExpr { public: BasicBlock *operator()(IR::BasicBlock *originalBlock) @@ -4506,38 +4585,37 @@ public: block = new BasicBlock(originalBlock->function, 0); for (Stmt *s : originalBlock->statements()) { - s->accept(this); + visit(s); clonedStmt->location = s->location; } return block; } -protected: - virtual void visitExp(Exp *stmt) - { clonedStmt = block->EXP(clone(stmt->expr)); } - - virtual void visitMove(Move *stmt) - { clonedStmt = block->MOVE(clone(stmt->target), clone(stmt->source)); } - - virtual void visitJump(Jump *stmt) - { clonedStmt = block->JUMP(stmt->target); } - - virtual void visitCJump(CJump *stmt) - { clonedStmt = block->CJUMP(clone(stmt->cond), stmt->iftrue, stmt->iffalse); } - - virtual void visitRet(Ret *stmt) - { clonedStmt = block->RET(clone(stmt->expr)); } - - virtual void visitPhi(Phi *stmt) +private: + void visit(Stmt *s) { - Phi *phi = block->function->NewStmt(); - clonedStmt = phi; - - phi->targetTemp = clone(stmt->targetTemp); - foreach (Expr *in, stmt->incoming) - phi->incoming.append(clone(in)); - block->appendStatement(phi); + if (auto e = s->asExp()) { + clonedStmt = block->EXP(clone(e->expr)); + } else if (auto m = s->asMove()) { + clonedStmt = block->MOVE(clone(m->target), clone(m->source)); + } else if (auto j = s->asJump()) { + clonedStmt = block->JUMP(j->target); + } else if (auto c = s->asCJump()) { + clonedStmt = block->CJUMP(clone(c->cond), c->iftrue, c->iffalse); + } else if (auto r = s->asRet()) { + clonedStmt = block->RET(clone(r->expr)); + } else if (auto p = s->asPhi()) { + Phi *phi = block->function->NewStmt(); + clonedStmt = phi; + + phi->targetTemp = clone(p->targetTemp); + foreach (Expr *in, p->incoming) + phi->incoming.append(clone(in)); + block->appendStatement(phi); + } else { + Q_UNREACHABLE(); + } } private: @@ -4769,7 +4847,7 @@ static void verifyNoPointerSharing(IR::Function *function) if (!DoVerification) return; - class : public StmtVisitor, public ExprVisitor { + class { public: void operator()(IR::Function *f) { @@ -4777,44 +4855,23 @@ static void verifyNoPointerSharing(IR::Function *function) if (bb->isRemoved()) continue; - for (Stmt *s : bb->statements()) - s->accept(this); + for (Stmt *s : bb->statements()) { + visit(s); + } } } - protected: - virtual void visitExp(Exp *s) { check(s); s->expr->accept(this); } - virtual void visitMove(Move *s) { check(s); s->target->accept(this); s->source->accept(this); } - virtual void visitJump(Jump *s) { check(s); } - virtual void visitCJump(CJump *s) { check(s); s->cond->accept(this); } - virtual void visitRet(Ret *s) { check(s); s->expr->accept(this); } - virtual void visitPhi(Phi *s) + private: + void visit(Stmt *s) { check(s); - s->targetTemp->accept(this); - foreach (Expr *e, s->incoming) - e->accept(this); - } - - virtual void visitConst(Const *e) { check(e); } - virtual void visitString(IR::String *e) { check(e); } - virtual void visitRegExp(IR::RegExp *e) { check(e); } - virtual void visitName(Name *e) { check(e); } - virtual void visitTemp(Temp *e) { check(e); } - virtual void visitArgLocal(ArgLocal *e) { check(e); } - virtual void visitClosure(Closure *e) { check(e); } - virtual void visitConvert(Convert *e) { check(e); e->expr->accept(this); } - virtual void visitUnop(Unop *e) { check(e); e->expr->accept(this); } - virtual void visitBinop(Binop *e) { check(e); e->left->accept(this); e->right->accept(this); } - virtual void visitCall(Call *e) { check(e); e->base->accept(this); check(e->args); } - virtual void visitNew(New *e) { check(e); e->base->accept(this); check(e->args); } - virtual void visitSubscript(Subscript *e) { check(e); e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { check(e); e->base->accept(this); } - - void check(ExprList *l) + STMT_VISIT_ALL_KINDS(s); + } + + void visit(Expr *e) { - for (ExprList *it = l; it; it = it->next) - check(it->expr); + check(e); + EXPR_VISIT_ALL_KINDS(e); } private: @@ -4836,7 +4893,7 @@ static void verifyNoPointerSharing(IR::Function *function) V(function); } -class RemoveLineNumbers: public SideEffectsChecker, public StmtVisitor +class RemoveLineNumbers: private SideEffectsChecker { public: static void run(IR::Function *function) @@ -4854,19 +4911,27 @@ public: } private: + ~RemoveLineNumbers() {} + static bool hasSideEffects(Stmt *stmt) { RemoveLineNumbers checker; - stmt->accept(&checker); + if (auto e = stmt->asExp()) { + checker.visit(e->expr); + } else if (auto m = stmt->asMove()) { + checker.visit(m->source); + if (!checker.seenSideEffects()) { + checker.visit(m->target); + } + } else if (auto c = stmt->asCJump()) { + checker.visit(c->cond); + } else if (auto r = stmt->asRet()) { + checker.visit(r->expr); + } return checker.seenSideEffects(); } - void visitExp(Exp *s) Q_DECL_OVERRIDE { s->expr->accept(this); } - void visitMove(Move *s) Q_DECL_OVERRIDE { s->source->accept(this); s->target->accept(this); } - void visitJump(Jump *) Q_DECL_OVERRIDE {} - void visitCJump(CJump *s) Q_DECL_OVERRIDE { s->cond->accept(this); } - void visitRet(Ret *s) Q_DECL_OVERRIDE { s->expr->accept(this); } - void visitPhi(Phi *) Q_DECL_OVERRIDE {} + void visitTemp(Temp *) Q_DECL_OVERRIDE Q_DECL_FINAL {} }; } // anonymous namespace diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 1f5a247a17..d3c624ff60 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -354,7 +354,7 @@ void InstructionSelection::run(int functionIndex) lastLine = s->location.startLine; } } - s->accept(this); + visit(s); } } diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index 6b5b79e458..45c596985d 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -87,7 +87,7 @@ public: {} protected: - void addStmtNr(Stmt *s) + void addStmtNr(Stmt *s) Q_DECL_OVERRIDE Q_DECL_FINAL { addJustifiedNr(intervals->positionForStatement(s)); } @@ -115,7 +115,7 @@ public: } protected: - void visitTemp(Temp *e) + void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL { switch (e->kind) { case Temp::PhysicalRegister: { @@ -184,7 +184,7 @@ public: _currentBB = bb; for (Stmt *s : bb->statements()) { _currentStmt = s; - s->accept(this); + visit(s); } } } @@ -809,7 +809,8 @@ using namespace QT_PREPEND_NAMESPACE(QV4::IR); using namespace QT_PREPEND_NAMESPACE(QV4); namespace { -class ResolutionPhase: protected StmtVisitor, protected ExprVisitor { +class ResolutionPhase +{ Q_DISABLE_COPY(ResolutionPhase) LifeTimeIntervals::Ptr _intervals; @@ -891,7 +892,7 @@ private: addNewIntervals(usePosition(_currentStmt)); else addNewIntervals(defPosition(_currentStmt)); - _currentStmt->accept(this); + visit(_currentStmt); for (Move *load : _loads) newStatements.append(load); if (_currentStmt->asPhi()) @@ -1178,8 +1179,20 @@ private: return load; } -protected: - virtual void visitTemp(Temp *t) +private: + void visit(Expr *e) + { + switch (e->exprKind) { + case Expr::TempExpr: + visitTemp(e->asTemp()); + break; + default: + EXPR_VISIT_ALL_KINDS(e); + break; + } + } + + void visitTemp(Temp *t) { if (t->kind != Temp::VirtualRegister) return; @@ -1209,47 +1222,25 @@ protected: } } - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { e->base->accept(this); } - - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitExp(Exp *s) { s->expr->accept(this); } - - virtual void visitMove(Move *s) + void visit(Stmt *s) { - if (Temp *t = s->target->asTemp()) - maybeGenerateSpill(t); + switch (s->stmtKind) { + case Stmt::MoveStmt: { + auto m = s->asMove(); + if (Temp *t = m->target->asTemp()) + maybeGenerateSpill(t); - s->source->accept(this); - s->target->accept(this); - } - - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitPhi(Phi *s) - { - maybeGenerateSpill(s->targetTemp); + visit(m->source); + visit(m->target); + } break; + case Stmt::PhiStmt: { + auto p = s->asPhi(); + maybeGenerateSpill(p->targetTemp); + } break; + default: + STMT_VISIT_ALL_KINDS(s); + break; + } } }; } // anonymous namespace -- cgit v1.2.3 From aca40a8361996e22ec4f020b803404031a0f0d76 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 8 Jun 2016 15:33:37 +0200 Subject: Fix names of tests The existing names are copy-paste duplication from the test above. Change-Id: Ie50a0ed6530361c321b619549b3e3896472b7242 Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquicklistview/tst_qquicklistview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 658ffa1f57..f5e79614df 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -735,7 +735,7 @@ void tst_QQuickListView::inserted_more_data() << 15 << 1 << 0.0; - QTest::newRow("add 1, at end of visible, content at start") + QTest::newRow("add multiple, at end of visible, content at start") << 0.0 << 15 << 3 << 0.0; @@ -756,7 +756,7 @@ void tst_QQuickListView::inserted_more_data() << 16 << 1 << 0.0; - QTest::newRow("add 1, after visible, content at start") + QTest::newRow("add multiple, after visible, content at start") << 0.0 << 16 << 3 << 0.0; -- cgit v1.2.3 From 4de6c1bc6cc1894f9f60cfef6d403a933d2c18cd Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Wed, 8 Jun 2016 13:52:30 +0300 Subject: Imports, Plugins, Quick: replace QStringLiteral with QL1S .. in such cases: - if there is overloaded function - in QStringBuilder expressions Result: reducing .rodata Change-Id: I972bc0a61df7b5567c40329dd47e77176d9a5b96 Reviewed-by: Ulf Hermann --- src/imports/statemachine/signaltransition.cpp | 2 +- .../qmldbg_debugger/qqmlnativedebugservice.cpp | 54 +++++++------- .../qmltooling/qmldbg_debugger/qv4debugservice.cpp | 84 +++++++++++----------- src/qmltest/quicktestresult.cpp | 2 +- src/quick/items/context2d/qquickcontext2d.cpp | 10 +-- 5 files changed, 76 insertions(+), 76 deletions(-) diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index 4153c70fd0..127494d8d3 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -178,7 +178,7 @@ void SignalTransitionParser::verifyBindings(const QV4::CompiledData::Unit *qmlUn QString propName = qmlUnit->stringAt(binding->propertyNameIndex); - if (propName != QStringLiteral("onTriggered")) { + if (propName != QLatin1String("onTriggered")) { error(props.at(ii), SignalTransition::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); return; } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp index 99f5b760ba..9f44c4ec81 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp @@ -143,15 +143,15 @@ public: void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonObject &arguments) { TRACE_PROTOCOL("SET BREAKPOINT" << arguments); - QString type = arguments.value(QStringLiteral("type")).toString(); + QString type = arguments.value(QLatin1String("type")).toString(); - QString fileName = arguments.value(QStringLiteral("file")).toString(); + QString fileName = arguments.value(QLatin1String("file")).toString(); if (fileName.isEmpty()) { setError(response, QStringLiteral("breakpoint has no file name")); return; } - int line = arguments.value(QStringLiteral("line")).toInt(-1); + int line = arguments.value(QLatin1String("line")).toInt(-1); if (line < 0) { setError(response, QStringLiteral("breakpoint has an invalid line number")); return; @@ -161,9 +161,9 @@ void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonOb bp.id = m_lastBreakpoint++; bp.fileName = fileName.mid(fileName.lastIndexOf('/') + 1); bp.lineNumber = line; - bp.enabled = arguments.value(QStringLiteral("enabled")).toBool(true); - bp.condition = arguments.value(QStringLiteral("condition")).toString(); - bp.ignoreCount = arguments.value(QStringLiteral("ignorecount")).toInt(); + bp.enabled = arguments.value(QLatin1String("enabled")).toBool(true); + bp.condition = arguments.value(QLatin1String("condition")).toString(); + bp.ignoreCount = arguments.value(QLatin1String("ignorecount")).toInt(); m_breakPoints.append(bp); m_haveBreakPoints = true; @@ -174,7 +174,7 @@ void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonOb void BreakPointHandler::handleRemoveBreakpoint(QJsonObject *response, const QJsonObject &arguments) { - int id = arguments.value(QStringLiteral("id")).toInt(); + int id = arguments.value(QLatin1String("id")).toInt(); removeBreakPoint(id); response->insert(QStringLiteral("id"), id); } @@ -301,19 +301,19 @@ void NativeDebugger::signalEmitted(const QString &signal) void NativeDebugger::handleCommand(QJsonObject *response, const QString &cmd, const QJsonObject &arguments) { - if (cmd == QStringLiteral("backtrace")) + if (cmd == QLatin1String("backtrace")) handleBacktrace(response, arguments); - else if (cmd == QStringLiteral("variables")) + else if (cmd == QLatin1String("variables")) handleVariables(response, arguments); - else if (cmd == QStringLiteral("expressions")) + else if (cmd == QLatin1String("expressions")) handleExpressions(response, arguments); - else if (cmd == QStringLiteral("stepin")) + else if (cmd == QLatin1String("stepin")) handleContinue(response, StepIn); - else if (cmd == QStringLiteral("stepout")) + else if (cmd == QLatin1String("stepout")) handleContinue(response, StepOut); - else if (cmd == QStringLiteral("stepover")) + else if (cmd == QLatin1String("stepover")) handleContinue(response, StepOver); - else if (cmd == QStringLiteral("continue")) + else if (cmd == QLatin1String("continue")) handleContinue(response, NotStepping); } @@ -334,7 +334,7 @@ static void decodeContext(const QString &context, QV4::ExecutionContext **execut void NativeDebugger::handleBacktrace(QJsonObject *response, const QJsonObject &arguments) { - int limit = arguments.value(QStringLiteral("limit")).toInt(0); + int limit = arguments.value(QLatin1String("limit")).toInt(0); QJsonArray frameArray; QV4::ExecutionContext *executionContext = m_engine->currentContext; @@ -463,7 +463,7 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a { TRACE_PROTOCOL("Build variables"); QV4::ExecutionContext *executionContext = 0; - decodeContext(arguments.value(QStringLiteral("context")).toString(), &executionContext); + decodeContext(arguments.value(QLatin1String("context")).toString(), &executionContext); if (!executionContext) { setError(response, QStringLiteral("No execution context passed")); return; @@ -478,7 +478,7 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a TRACE_PROTOCOL("Engine: " << engine); Collector collector(engine); - QJsonArray expanded = arguments.value(QStringLiteral("expanded")).toArray(); + QJsonArray expanded = arguments.value(QLatin1String("expanded")).toArray(); foreach (const QJsonValue &ex, expanded) collector.m_expanded.append(ex.toString()); TRACE_PROTOCOL("Expanded: " << collector.m_expanded); @@ -515,7 +515,7 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject { TRACE_PROTOCOL("Evaluate expressions"); QV4::ExecutionContext *executionContext = 0; - decodeContext(arguments.value(QStringLiteral("context")).toString(), &executionContext); + decodeContext(arguments.value(QLatin1String("context")).toString(), &executionContext); if (!executionContext) { setError(response, QStringLiteral("No execution context passed")); return; @@ -530,7 +530,7 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject TRACE_PROTOCOL("Engines: " << engine << m_engine); Collector collector(engine); - QJsonArray expanded = arguments.value(QStringLiteral("expanded")).toArray(); + QJsonArray expanded = arguments.value(QLatin1String("expanded")).toArray(); foreach (const QJsonValue &ex, expanded) collector.m_expanded.append(ex.toString()); TRACE_PROTOCOL("Expanded: " << collector.m_expanded); @@ -538,10 +538,10 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject QJsonArray output; QV4::Scope scope(engine); - QJsonArray expressions = arguments.value(QStringLiteral("expressions")).toArray(); + QJsonArray expressions = arguments.value(QLatin1String("expressions")).toArray(); foreach (const QJsonValue &expr, expressions) { - QString expression = expr.toObject().value(QStringLiteral("expression")).toString(); - QString name = expr.toObject().value(QStringLiteral("name")).toString(); + QString expression = expr.toObject().value(QLatin1String("expression")).toString(); + QString name = expr.toObject().value(QLatin1String("name")).toString(); TRACE_PROTOCOL("Evaluate expression: " << expression); m_runningJob = true; @@ -776,14 +776,14 @@ void QQmlNativeDebugServiceImpl::messageReceived(const QByteArray &message) TRACE_PROTOCOL("Native message received: " << message); QJsonObject request = QJsonDocument::fromJson(message).object(); QJsonObject response; - QJsonObject arguments = request.value(QStringLiteral("arguments")).toObject(); - QString cmd = request.value(QStringLiteral("command")).toString(); + QJsonObject arguments = request.value(QLatin1String("arguments")).toObject(); + QString cmd = request.value(QLatin1String("command")).toString(); - if (cmd == QStringLiteral("setbreakpoint")) { + if (cmd == QLatin1String("setbreakpoint")) { m_breakHandler->handleSetBreakpoint(&response, arguments); - } else if (cmd == QStringLiteral("removebreakpoint")) { + } else if (cmd == QLatin1String("removebreakpoint")) { m_breakHandler->handleRemoveBreakpoint(&response, arguments); - } else if (cmd == QStringLiteral("echo")) { + } else if (cmd == QLatin1String("echo")) { response.insert(QStringLiteral("result"), arguments); } else { foreach (NativeDebugger *debugger, m_debuggers) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index 5ee9e5e9e9..c87ca47c49 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -90,7 +90,7 @@ public: TRACE_PROTOCOL(qDebug() << "handling command" << command() << "..."); req = request; - seq = req.value(QStringLiteral("seq")); + seq = req.value(QLatin1String("seq")); debugService = s; handleRequest(); @@ -128,7 +128,7 @@ protected: void createErrorResponse(const QString &msg) { - QJsonValue command = req.value(QStringLiteral("command")); + QJsonValue command = req.value(QLatin1String("command")); response.insert(QStringLiteral("command"), command); addRequestSequence(); addSuccess(false); @@ -154,9 +154,9 @@ public: virtual void handleRequest() { - QString msg = QStringLiteral("unimplemented command \""); - msg += req.value(QStringLiteral("command")).toString(); - msg += QLatin1Char('"'); + QString msg = QLatin1String("unimplemented command \"") + + req.value(QLatin1String("command")).toString() + + QLatin1Char('"'); createErrorResponse(msg); } }; @@ -189,23 +189,23 @@ public: virtual void handleRequest() { // decypher the payload: - QJsonObject args = req.value(QStringLiteral("arguments")).toObject(); + QJsonObject args = req.value(QLatin1String("arguments")).toObject(); if (args.isEmpty()) return; - QString type = args.value(QStringLiteral("type")).toString(); - if (type != QStringLiteral("scriptRegExp")) { + QString type = args.value(QLatin1String("type")).toString(); + if (type != QLatin1String("scriptRegExp")) { createErrorResponse(QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type)); return; } - QString fileName = args.value(QStringLiteral("target")).toString(); + QString fileName = args.value(QLatin1String("target")).toString(); if (fileName.isEmpty()) { createErrorResponse(QStringLiteral("breakpoint has no file name")); return; } - int line = args.value(QStringLiteral("line")).toInt(-1); + int line = args.value(QLatin1String("line")).toInt(-1); if (line < 0) { createErrorResponse(QStringLiteral("breakpoint has an invalid line number")); return; @@ -240,11 +240,11 @@ public: virtual void handleRequest() { // decypher the payload: - QJsonObject args = req.value(QStringLiteral("arguments")).toObject(); + QJsonObject args = req.value(QLatin1String("arguments")).toObject(); if (args.isEmpty()) return; - int id = args.value(QStringLiteral("breakpoint")).toInt(-1); + int id = args.value(QLatin1String("breakpoint")).toInt(-1); if (id < 0) { createErrorResponse(QStringLiteral("breakpoint has an invalid number")); return; @@ -274,9 +274,9 @@ public: { // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - int fromFrame = arguments.value(QStringLiteral("fromFrame")).toInt(0); - int toFrame = arguments.value(QStringLiteral("toFrame")).toInt(fromFrame + 10); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + int fromFrame = arguments.value(QLatin1String("fromFrame")).toInt(0); + int toFrame = arguments.value(QLatin1String("toFrame")).toInt(fromFrame + 10); // no idea what the bottom property is for, so we'll ignore it. QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); @@ -306,8 +306,8 @@ public: virtual void handleRequest() { // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - const int frameNr = arguments.value(QStringLiteral("number")).toInt( + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + const int frameNr = arguments.value(QLatin1String("number")).toInt( debugService->selectedFrame()); QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); @@ -348,10 +348,10 @@ public: virtual void handleRequest() { // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - const int frameNr = arguments.value(QStringLiteral("frameNumber")).toInt( + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + const int frameNr = arguments.value(QLatin1String("frameNumber")).toInt( debugService->selectedFrame()); - const int scopeNr = arguments.value(QStringLiteral("number")).toInt(0); + const int scopeNr = arguments.value(QLatin1String("number")).toInt(0); QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); if (!debugger) { @@ -393,8 +393,8 @@ public: virtual void handleRequest() { // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QJsonArray handles = arguments.value(QStringLiteral("handles")).toArray(); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + QJsonArray handles = arguments.value(QLatin1String("handles")).toArray(); QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); if (!debugger) { @@ -433,7 +433,7 @@ public: virtual void handleRequest() { // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); if (!debugger) { @@ -445,17 +445,17 @@ public: if (arguments.empty()) { debugger->resume(QV4Debugger::FullThrottle); } else { - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString stepAction = arguments.value(QStringLiteral("stepaction")).toString(); - const int stepcount = arguments.value(QStringLiteral("stepcount")).toInt(1); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + QString stepAction = arguments.value(QLatin1String("stepaction")).toString(); + const int stepcount = arguments.value(QLatin1String("stepcount")).toInt(1); if (stepcount != 1) qWarning() << "Step count other than 1 is not supported."; - if (stepAction == QStringLiteral("in")) { + if (stepAction == QLatin1String("in")) { debugger->resume(QV4Debugger::StepIn); - } else if (stepAction == QStringLiteral("out")) { + } else if (stepAction == QLatin1String("out")) { debugger->resume(QV4Debugger::StepOut); - } else if (stepAction == QStringLiteral("next")) { + } else if (stepAction == QLatin1String("next")) { debugger->resume(QV4Debugger::StepOver); } else { createErrorResponse(QStringLiteral("continue command has invalid stepaction")); @@ -499,13 +499,13 @@ public: bool wasEnabled = debugService->debuggerAgent.breakOnThrow(); //decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString type = arguments.value(QStringLiteral("type")).toString(); - bool enabled = arguments.value(QStringLiteral("number")).toBool(!wasEnabled); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + QString type = arguments.value(QLatin1String("type")).toString(); + bool enabled = arguments.value(QLatin1String("number")).toBool(!wasEnabled); - if (type == QStringLiteral("all")) { + if (type == QLatin1String("all")) { // that's fine - } else if (type == QStringLiteral("uncaught")) { + } else if (type == QLatin1String("uncaught")) { createErrorResponse(QStringLiteral("breaking only on uncaught exceptions is not supported yet")); return; } else { @@ -537,8 +537,8 @@ public: virtual void handleRequest() { //decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - int types = arguments.value(QStringLiteral("types")).toInt(-1); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + int types = arguments.value(QLatin1String("types")).toInt(-1); if (types < 0 || types > 7) { createErrorResponse(QStringLiteral("invalid types value in scripts command")); return; @@ -608,8 +608,8 @@ public: virtual void handleRequest() { - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString expression = arguments.value(QStringLiteral("expression")).toString(); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + QString expression = arguments.value(QLatin1String("expression")).toString(); int frame = -1; QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); @@ -624,7 +624,7 @@ public: } debugger = debuggers.first(); } else { - frame = arguments.value(QStringLiteral("frame")).toInt(0); + frame = arguments.value(QLatin1String("frame")).toInt(0); } ExpressionEvalJob job(debugger->engine(), frame, expression, debugger->collector()); @@ -802,9 +802,9 @@ void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload) QJsonDocument request = QJsonDocument::fromJson(payload); QJsonObject o = request.object(); - QJsonValue type = o.value(QStringLiteral("type")); - if (type.toString() == QStringLiteral("request")) { - QJsonValue command = o.value(QStringLiteral("command")); + QJsonValue type = o.value(QLatin1String("type")); + if (type.toString() == QLatin1String("request")) { + QJsonValue command = o.value(QLatin1String("command")); V8CommandHandler *h = v8CommandHandler(command.toString()); if (h) h->handle(o, this); diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp index 0a820b79db..e05d29bc05 100644 --- a/src/qmltest/quicktestresult.cpp +++ b/src/qmltest/quicktestresult.cpp @@ -248,7 +248,7 @@ void QuickTestResult::setDataTag(const QString &tag) if (!tag.isEmpty()) { QTestData *data = &(QTest::newRow(tag.toUtf8().constData())); QTestResult::setCurrentTestData(data); - QTestPrivate::checkBlackLists((testCaseName() + QStringLiteral("::") + functionName()).toUtf8().constData(), tag.toUtf8().constData()); + QTestPrivate::checkBlackLists((testCaseName() + QLatin1String("::") + functionName()).toUtf8().constData(), tag.toUtf8().constData()); emit dataTagChanged(); } else { QTestResult::setCurrentTestData(0); diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index b2117d3eb9..10b02b1f32 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -2669,15 +2669,15 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(QV4::CallContext *ctx QString textAlign = s->toQString(); QQuickContext2D::TextAlignType ta; - if (textAlign == QStringLiteral("start")) + if (textAlign == QLatin1String("start")) ta = QQuickContext2D::Start; - else if (textAlign == QStringLiteral("end")) + else if (textAlign == QLatin1String("end")) ta = QQuickContext2D::End; - else if (textAlign == QStringLiteral("left")) + else if (textAlign == QLatin1String("left")) ta = QQuickContext2D::Left; - else if (textAlign == QStringLiteral("right")) + else if (textAlign == QLatin1String("right")) ta = QQuickContext2D::Right; - else if (textAlign == QStringLiteral("center")) + else if (textAlign == QLatin1String("center")) ta = QQuickContext2D::Center; else return QV4::Encode::undefined(); -- cgit v1.2.3 From a5febdbba3807e1eb8fff0b0c3bae657d3e906e7 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 9 Jun 2016 10:24:54 +0200 Subject: QmlIncubator: correct documentation snippet about waiting for Ready It makes more sense to process events until the incubator is Ready rather than while it's already Ready. Change-Id: If8b0c5057dd88ffdbb2dd39f53b5b9676d0b9d19 Reviewed-by: Paolo Angelelli Reviewed-by: Matthew Vogt --- src/qml/qml/qqmlincubator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index a568ae440c..e65760a09f 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -434,7 +434,7 @@ example shows a simple use of QQmlIncubator. QQmlIncubator incubator; component->create(incubator); -while (incubator.isReady()) { +while (!incubator.isReady()) { QCoreApplication::processEvents(QEventLoop::AllEvents, 50); } -- cgit v1.2.3 From 56d9505e9d2b7dd9507523693d81f72efaa247c8 Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Fri, 10 Jun 2016 21:13:12 +0200 Subject: Blacklist qmltest::ListView::test_listInteractiveCurrentIndexEnforce() Task-number: QTBUG-54028 Change-Id: I03390ebfb99927851926c0099fb79581f7b9e7a3 Reviewed-by: Liang Qi --- tests/auto/qmltest/BLACKLIST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/qmltest/BLACKLIST b/tests/auto/qmltest/BLACKLIST index 490e20d5f8..628290f2b4 100644 --- a/tests/auto/qmltest/BLACKLIST +++ b/tests/auto/qmltest/BLACKLIST @@ -5,6 +5,8 @@ * [tst_grabImage::test_equals] linux +[ListView::test_listInteractiveCurrentIndexEnforce] +linux [Text::test_linecount] osx windows -- cgit v1.2.3 From 2ac19881f92c94f4e9427bd9ff513210675f259e Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 8 Jun 2016 17:32:32 +0200 Subject: QML: Only release types if they aren't referenced anymore Just checking for references on m_compiledData is not enough. The actual component can also be referenced. Thus it won't be deleted on release(), but cannot be found in the type cache anymore. Task-number: QTBUG-53761 Change-Id: I8567af8e75a078598e4fed31e4717134e1332278 Reviewed-by: Mitch Curtis Reviewed-by: Simon Hausmann --- src/qml/qml/qqmltypeloader.cpp | 3 ++- tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index c684c8602e..01200fd881 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1961,7 +1961,8 @@ void QQmlTypeLoader::trimCache() QList unneededTypes; for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter) { QQmlTypeData *typeData = iter.value(); - if (typeData->m_compiledData && typeData->m_compiledData->count() == 1) { + if (typeData->m_compiledData && typeData->count() == 1 + && typeData->m_compiledData->count() == 1) { // There are no live objects of this type unneededTypes.append(iter); } diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index 7045c7cbd4..a1eaa0567f 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -86,10 +86,19 @@ void tst_QQMLTypeLoader::trimCache() url.setQuery(QString::number(i)); QQmlTypeData *data = loader.getType(url); - if (i % 5 == 0) // keep references to some of them so that they aren't trimmed - data->compiledData()->addref(); + // Run an event loop to receive the callback that release()es. + QTRY_COMPARE(data->count(), 2); - data->release(); + // keep references to some of them so that they aren't trimmed. References to either the + // QQmlTypeData or its compiledData() should prevent the trimming. + if (i % 10 == 0) { + // keep ref on data, don't add ref on data->compiledData() + } else if (i % 5 == 0) { + data->compiledData()->addref(); + data->release(); + } else { + data->release(); + } } for (int i = 0; i < 256; ++i) { -- cgit v1.2.3 From 4f94ddcf295c59dec86c8570c19e3e7427d68b06 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Fri, 10 Jun 2016 13:01:13 +0200 Subject: QAccessibleQuickItem: Add Q_DECL_OVERRIDE Fixes compilation with clang 3.8 Task-number: QTBUG-54009 Change-Id: Ib777bd56d187e3c94de0cda643dac47eb30d8215 Reviewed-by: Frederik Gladhorn --- src/quick/accessible/qaccessiblequickitem_p.h | 68 +++++++++++++-------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/quick/accessible/qaccessiblequickitem_p.h b/src/quick/accessible/qaccessiblequickitem_p.h index af9f8db249..4d36ff5697 100644 --- a/src/quick/accessible/qaccessiblequickitem_p.h +++ b/src/quick/accessible/qaccessiblequickitem_p.h @@ -62,70 +62,70 @@ public: QWindow *window() const Q_DECL_OVERRIDE; - QRect rect() const; + QRect rect() const Q_DECL_OVERRIDE; QRect viewRect() const; bool clipsChildren() const; - QAccessibleInterface *childAt(int x, int y) const; + QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE; - QAccessibleInterface *parent() const; - QAccessibleInterface *child(int index) const; - int childCount() const; - int indexOfChild(const QAccessibleInterface *iface) const; + QAccessibleInterface *parent() const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + int childCount() const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *iface) const Q_DECL_OVERRIDE; QList childItems() const; - QAccessible::State state() const; - QAccessible::Role role() const; - QString text(QAccessible::Text) const; + QAccessible::State state() const Q_DECL_OVERRIDE; + QAccessible::Role role() const Q_DECL_OVERRIDE; + QString text(QAccessible::Text) const Q_DECL_OVERRIDE; bool isAccessible() const; // Action Interface - QStringList actionNames() const; - void doAction(const QString &actionName); - QStringList keyBindingsForAction(const QString &actionName) const; + QStringList actionNames() const Q_DECL_OVERRIDE; + void doAction(const QString &actionName) Q_DECL_OVERRIDE; + QStringList keyBindingsForAction(const QString &actionName) const Q_DECL_OVERRIDE; // Value Interface - QVariant currentValue() const; - void setCurrentValue(const QVariant &value); - QVariant maximumValue() const; - QVariant minimumValue() const; - QVariant minimumStepSize() const; + QVariant currentValue() const Q_DECL_OVERRIDE; + void setCurrentValue(const QVariant &value) Q_DECL_OVERRIDE; + QVariant maximumValue() const Q_DECL_OVERRIDE; + QVariant minimumValue() const Q_DECL_OVERRIDE; + QVariant minimumStepSize() const Q_DECL_OVERRIDE; // Text Interface - void selection(int selectionIndex, int *startOffset, int *endOffset) const; - int selectionCount() const; - void addSelection(int startOffset, int endOffset); - void removeSelection(int selectionIndex); - void setSelection(int selectionIndex, int startOffset, int endOffset); + void selection(int selectionIndex, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + int selectionCount() const Q_DECL_OVERRIDE; + void addSelection(int startOffset, int endOffset) Q_DECL_OVERRIDE; + void removeSelection(int selectionIndex) Q_DECL_OVERRIDE; + void setSelection(int selectionIndex, int startOffset, int endOffset) Q_DECL_OVERRIDE; // cursor - int cursorPosition() const; - void setCursorPosition(int position); + int cursorPosition() const Q_DECL_OVERRIDE; + void setCursorPosition(int position) Q_DECL_OVERRIDE; // text - QString text(int startOffset, int endOffset) const; + QString text(int startOffset, int endOffset) const Q_DECL_OVERRIDE; QString textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType, - int *startOffset, int *endOffset) const; + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; QString textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType, - int *startOffset, int *endOffset) const; + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; QString textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, - int *startOffset, int *endOffset) const; - int characterCount() const; + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + int characterCount() const Q_DECL_OVERRIDE; // character <-> geometry - QRect characterRect(int /* offset */) const { return QRect(); } - int offsetAtPoint(const QPoint & /* point */) const { return -1; } + QRect characterRect(int /* offset */) const Q_DECL_OVERRIDE { return QRect(); } + int offsetAtPoint(const QPoint & /* point */) const Q_DECL_OVERRIDE { return -1; } - void scrollToSubstring(int /* startIndex */, int /* endIndex */) {} - QString attributes(int /* offset */, int *startOffset, int *endOffset) const { *startOffset = 0; *endOffset = 0; return QString(); } + void scrollToSubstring(int /* startIndex */, int /* endIndex */) Q_DECL_OVERRIDE {} + QString attributes(int /* offset */, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE { *startOffset = 0; *endOffset = 0; return QString(); } QTextDocument *textDocument() const; protected: QQuickItem *item() const { return static_cast(object()); } - void *interface_cast(QAccessible::InterfaceType t); + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; private: QTextDocument *m_doc; -- cgit v1.2.3 From 210617de78c78af44efdc251ba7a207c5c26e11c Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 8 Jun 2016 17:32:32 +0200 Subject: QML: Only release types if they aren't referenced anymore Just checking for references on m_compiledData is not enough. The actual component can also be referenced. Thus it won't be deleted on release(), but cannot be found in the type cache anymore. Task-number: QTBUG-53761 (cherry picked from commit 2ac19881f92c94f4e9427bd9ff513210675f259e) Change-Id: If254d5bca1f41ec948ec1438df0d37bf8d531bdf Reviewed-by: Ulf Hermann --- src/qml/qml/qqmltypeloader.cpp | 3 ++- tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 29fdf78797..739a833a30 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1950,7 +1950,8 @@ void QQmlTypeLoader::trimCache() QList unneededTypes; for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter) { QQmlTypeData *typeData = iter.value(); - if (typeData->m_compiledData && typeData->m_compiledData->count() == 1) { + if (typeData->m_compiledData && typeData->count() == 1 + && typeData->m_compiledData->count() == 1) { // There are no live objects of this type unneededTypes.append(iter); } diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index ef1ea3a897..3e8e1d23ea 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -81,10 +81,19 @@ void tst_QQMLTypeLoader::trimCache() url.setQuery(QString::number(i)); QQmlTypeData *data = loader.getType(url); - if (i % 5 == 0) // keep references to some of them so that they aren't trimmed - data->compiledData()->addref(); + // Run an event loop to receive the callback that release()es. + QTRY_COMPARE(data->count(), 2); - data->release(); + // keep references to some of them so that they aren't trimmed. References to either the + // QQmlTypeData or its compiledData() should prevent the trimming. + if (i % 10 == 0) { + // keep ref on data, don't add ref on data->compiledData() + } else if (i % 5 == 0) { + data->compiledData()->addref(); + data->release(); + } else { + data->release(); + } } for (int i = 0; i < 256; ++i) { -- cgit v1.2.3 From 94324bd93fe9f4f61346b8f9e934c01279cb700e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 2 May 2016 11:08:13 +0200 Subject: tst_touchmouse: make use of new QTest::createTouchDevice function So gui-private API isn't necessary anymore. Task-number: QTBUG-44030 Change-Id: If4e77fc99e9f6373b8dd0303c9ac52e71ed43cf7 Reviewed-by: Laszlo Agocs Reviewed-by: Frederik Gladhorn --- tests/auto/quick/touchmouse/touchmouse.pro | 2 +- tests/auto/quick/touchmouse/tst_touchmouse.cpp | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/auto/quick/touchmouse/touchmouse.pro b/tests/auto/quick/touchmouse/touchmouse.pro index 0df9bc53d3..49818bb399 100644 --- a/tests/auto/quick/touchmouse/touchmouse.pro +++ b/tests/auto/quick/touchmouse/touchmouse.pro @@ -1,7 +1,7 @@ CONFIG += testcase TARGET = tst_touchmouse -QT += core-private gui-private qml-private quick-private testlib +QT += core-private qml-private quick-private testlib macx:CONFIG -= app_bundle diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 15d7ffd9d9..d831889a1e 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include @@ -132,7 +131,7 @@ class tst_TouchMouse : public QQmlDataTest Q_OBJECT public: tst_TouchMouse() - :device(0) + :device(QTest::createTouchDevice()) {} private slots: @@ -191,11 +190,6 @@ void tst_TouchMouse::initTestCase() QQmlDataTest::initTestCase(); qmlRegisterType("Qt.test", 1, 0, "EventItem"); - if (!device) { - device = new QTouchDevice; - device->setType(QTouchDevice::TouchScreen); - QWindowSystemInterface::registerTouchDevice(device); - } } void tst_TouchMouse::simpleTouchEvent() -- cgit v1.2.3 From 99770ff277e1f7304a603b00b5a0863e3c77f7e2 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 10 Jun 2016 16:46:00 +0200 Subject: remove unused qsg_render_timing variable GCC started giving warnings, and warnings are errors so this broke the build. Change-Id: I546db5a63dfe52b9487016deb3c9eae0bf7c5885 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp index ce726e342b..15bc32ecb2 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp @@ -58,10 +58,6 @@ #include #include -#ifndef QSG_NO_RENDERER_TIMING -static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty(); -#endif - // Used for very high-level info about the renderering and gl context // Includes GL_VERSION, type of render loop, atlas size, etc. Q_LOGGING_CATEGORY(QSG_RASTER_LOG_INFO, "qt.scenegraph.info") -- cgit v1.2.3 From 000914cf5d933cb6c3842e0baa1de9cd65d45e68 Mon Sep 17 00:00:00 2001 From: Milla Pohjanheimo Date: Wed, 8 Jun 2016 09:32:33 +0300 Subject: Blacklisting tst_QQuickListView::populateTransitions on OpenSUSE 42.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To get QtWayland in the CI, we need to blacklist populateTransitions on OpenSUSE 42.1 Task-number: QTBUG-53863 Change-Id: I7b8b1467757083ea630a0e08e7f27f4b2396e0b1 Reviewed-by: Tony Sarajärvi --- tests/auto/quick/qquicklistview/BLACKLIST | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST index 269696ce8c..d259c11219 100644 --- a/tests/auto/quick/qquicklistview/BLACKLIST +++ b/tests/auto/quick/qquicklistview/BLACKLIST @@ -2,3 +2,6 @@ * [enforceRange_withoutHighlight] osx +#QTBUG-53863 +[populateTransitions] +opensuse-42.1 -- cgit v1.2.3 From e3a6565c3832e6f3001d49c52ca59fa4fa555f1b Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Mon, 13 Jun 2016 15:00:42 +0200 Subject: QQuickTextInput: include padding when reporting anchor rect If a padding is set on QQuickTextInput (like controls 2 does), the anchor selection handle ends up at a wrong location. So ensure that we include padding when calculating achor rect (like we do for QQuickTextInput::cursorRectangle()). Change-Id: I356d38443ad891959f3242e254e37961e4965a4c Reviewed-by: J-P Nurmi --- src/quick/items/qquicktextinput.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 504dece0d1..03c6b892c4 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -1003,6 +1003,7 @@ void QQuickTextInput::q_validatorChanged() QRectF QQuickTextInputPrivate::anchorRectangle() const { + Q_Q(const QQuickTextInput); QRectF rect; int a; // Unfortunately we cannot use selectionStart() and selectionEnd() @@ -1023,8 +1024,8 @@ QRectF QQuickTextInputPrivate::anchorRectangle() const a = 0; QTextLine l = m_textLayout.lineForTextPosition(a); if (l.isValid()) { - qreal x = l.cursorToX(a) - hscroll; - qreal y = l.y() - vscroll; + qreal x = l.cursorToX(a) - hscroll + q->leftPadding(); + qreal y = l.y() - vscroll + q->topPadding(); rect.setRect(x, y, 1, l.height()); } } -- cgit v1.2.3 From 7f8dc9d00c02e0e28cdea92cac8f8e4379bed68b Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Mon, 13 Jun 2016 14:52:45 +0200 Subject: Fix build of textureprovider example with -opengl dynamic Do not link to OpenGL calls directly. Instead, use QOpenGLFunctions. Task-number: QTBUG-53841 Change-Id: Ic87bb78fede1ee9f75b81247406ad858b2bf7342 Reviewed-by: Laszlo Agocs --- examples/quick/textureprovider/etcprovider.cpp | 8 +++++--- examples/quick/textureprovider/etcprovider.h | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/quick/textureprovider/etcprovider.cpp b/examples/quick/textureprovider/etcprovider.cpp index 7eff597a43..b9bef84f70 100644 --- a/examples/quick/textureprovider/etcprovider.cpp +++ b/examples/quick/textureprovider/etcprovider.cpp @@ -81,7 +81,7 @@ unsigned short getPaddedHeight(ETCHeader *pHeader) EtcTexture::EtcTexture() : m_texture_id(0), m_uploaded(false) { - + initializeOpenGLFunctions(); } EtcTexture::~EtcTexture() @@ -92,8 +92,10 @@ EtcTexture::~EtcTexture() int EtcTexture::textureId() const { - if (m_texture_id == 0) - glGenTextures(1, &const_cast(this)->m_texture_id); + if (m_texture_id == 0) { + EtcTexture *texture = const_cast(this); + texture->glGenTextures(1, &texture->m_texture_id); + } return m_texture_id; } diff --git a/examples/quick/textureprovider/etcprovider.h b/examples/quick/textureprovider/etcprovider.h index a9ec5ed606..e9761e3629 100644 --- a/examples/quick/textureprovider/etcprovider.h +++ b/examples/quick/textureprovider/etcprovider.h @@ -34,7 +34,7 @@ #ifndef ETCPROVIDER_H #define ETCPROVIDER_H -#include +#include #include #include #include @@ -54,7 +54,7 @@ private: QUrl m_baseUrl; }; -class EtcTexture : public QSGTexture +class EtcTexture : public QSGTexture, protected QOpenGLFunctions { Q_OBJECT public: -- cgit v1.2.3 From 5046012e07a5eba917893c8b77015ff8f54500b7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 7 Jun 2016 20:02:51 +0200 Subject: Cleanup: generalize type reference collection code For loading compilation units from disk we'll have to scan the unit for type references, in order to correctly load dependencies. In preparation for that this patch template'izes the loops so that they can work on the Qml IR as well as on the CompiledData::Object structures. Change-Id: I32e4e3277e16c5a2d1bf6baf24456c9837c189ff Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder.cpp | 25 ------------------------ src/qml/compiler/qqmlirbuilder_p.h | 37 +++++++++++++++++++++++++++++++++--- src/qml/compiler/qv4compileddata_p.h | 31 ++++++++++++++++++++++++++++++ src/qml/qml/qqmltypeloader.cpp | 5 +++-- src/qml/qml/qqmltypeloader_p.h | 1 + 5 files changed, 69 insertions(+), 30 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 186cd5aac9..3c987c76b9 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -251,31 +251,6 @@ static void replaceWithSpace(QString &str, int idx, int n) *data++ = space; } -void Document::collectTypeReferences() -{ - foreach (Object *obj, objects) { - if (obj->inheritedTypeNameIndex != emptyStringIndex) { - QV4::CompiledData::TypeReference &r = typeReferences.add(obj->inheritedTypeNameIndex, obj->location); - r.needsCreation = true; - r.errorWhenNotFound = true; - } - - for (const Property *prop = obj->firstProperty(); prop; prop = prop->next) { - if (prop->type >= QV4::CompiledData::Property::Custom) { - // ### FIXME: We could report the more accurate location here by using prop->location, but the old - // compiler can't and the tests expect it to be the object location right now. - QV4::CompiledData::TypeReference &r = typeReferences.add(prop->customTypeNameIndex, obj->location); - r.errorWhenNotFound = true; - } - } - - for (const Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) - typeReferences.add(binding->propertyNameIndex, binding->location); - } - } -} - void Document::removeScriptPragmas(QString &script) { const QLatin1String pragma("pragma"); diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index db17c93222..b7e3e883bc 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -161,6 +161,35 @@ struct PoolList } return result; } + + struct Iterator { + T *ptr; + + explicit Iterator(T *p) : ptr(p) {} + + T *operator->() { + return ptr; + } + + const T *operator->() const { + return ptr; + } + + void operator++() { + ptr = ptr->next; + } + + bool operator==(const Iterator &rhs) const { + return ptr == rhs.ptr; + } + + bool operator!=(const Iterator &rhs) const { + return ptr != rhs.ptr; + } + }; + + Iterator begin() { return Iterator(first); } + Iterator end() { return Iterator(nullptr); } }; template @@ -309,6 +338,11 @@ public: const Function *firstFunction() const { return functions->first; } int functionCount() const { return functions->count; } + PoolList::Iterator bindingsBegin() const { return bindings->begin(); } + PoolList::Iterator bindingsEnd() const { return bindings->end(); } + PoolList::Iterator propertiesBegin() const { return properties->begin(); } + PoolList::Iterator propertiesEnd() const { return properties->end(); } + // If set, then declarations for this object (and init bindings for these) should go into the // specified object. Used for declarations inside group properties. Object *declarationsOverride; @@ -370,9 +404,6 @@ struct Q_QML_PRIVATE_EXPORT Document QQmlRefPointer javaScriptCompilationUnit; QHash extraSignalParameters; - QV4::CompiledData::TypeReferenceMap typeReferences; - void collectTypeReferences(); - int registerString(const QString &str) { return jsGenerator.registerString(str); } QString stringAt(int index) const { return jsGenerator.stringForIndex(index); } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 9be45184a7..760f6b6737 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -633,6 +633,37 @@ struct TypeReferenceMap : QHash return *it; return *insert(nameIndex, loc); } + + template + void collectFromObject(const CompiledObject *obj) + { + if (obj->inheritedTypeNameIndex != 0) { + TypeReference &r = this->add(obj->inheritedTypeNameIndex, obj->location); + r.needsCreation = true; + r.errorWhenNotFound = true; + } + + for (auto prop = obj->propertiesBegin(), propEnd = obj->propertiesEnd(); prop != propEnd; ++prop) { + if (prop->type >= QV4::CompiledData::Property::Custom) { + // ### FIXME: We could report the more accurate location here by using prop->location, but the old + // compiler can't and the tests expect it to be the object location right now. + TypeReference &r = this->add(prop->customTypeNameIndex, obj->location); + r.errorWhenNotFound = true; + } + } + + for (auto binding = obj->bindingsBegin(), bindingEnd = obj->bindingsEnd(); binding != bindingEnd; ++binding) { + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) + this->add(binding->propertyNameIndex, binding->location); + } + } + + template + void collectFromObjects(Iterator it, Iterator end) + { + for (; it != end; ++it) + collectFromObject(*it); + } }; // index is per-object binding index diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 3cbbaf4129..847e138c5a 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2133,6 +2133,7 @@ void QQmlTypeData::done() compile(); m_document.reset(); + m_typeReferences.clear(); m_implicitImport = 0; } @@ -2200,7 +2201,7 @@ void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *un void QQmlTypeData::continueLoadFromIR() { - m_document->collectTypeReferences(); + m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); m_importCache.setBaseUrl(finalUrl(), finalUrlString()); // For remote URLs, we don't delay the loading of the implicit import @@ -2357,7 +2358,7 @@ void QQmlTypeData::resolveTypes() } } - for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_document->typeReferences.constBegin(), end = m_document->typeReferences.constEnd(); + for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd(); unresolvedRef != end; ++unresolvedRef) { TypeReference ref; // resolved reference diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index baea9dbd0a..68b4d18a14 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -457,6 +457,7 @@ private: virtual void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace); QScopedPointer m_document; + QV4::CompiledData::TypeReferenceMap m_typeReferences; QList m_scripts; -- cgit v1.2.3 From 5d22723e052480ff3893a21f6bd75142f9f4f6ea Mon Sep 17 00:00:00 2001 From: James McDonnell Date: Tue, 14 Jun 2016 11:56:44 -0400 Subject: Make some QNX only code Dinkum specific The undefines won't be needed for QNX 7.0 with GNU libc++. Change-Id: Ibcea09aad54a956fc056ceaac5db6a0d53cd176c Reviewed-by: Simon Hausmann --- src/3rdparty/masm/wtf/MathExtras.h | 2 +- src/qml/jsruntime/qv4global_p.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/3rdparty/masm/wtf/MathExtras.h b/src/3rdparty/masm/wtf/MathExtras.h index 28a189b6e6..9c38af6b16 100644 --- a/src/3rdparty/masm/wtf/MathExtras.h +++ b/src/3rdparty/masm/wtf/MathExtras.h @@ -43,7 +43,7 @@ #include #endif -#if OS(QNX) +#if OS(QNX) && defined(_CPPLIB_VER) // FIXME: Look into a way to have cmath import its functions into both the standard and global // namespace. For now, we include math.h since the QNX cmath header only imports its functions // into the standard namespace. diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 01a21ea06d..960741d95f 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -122,7 +122,7 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } #define ENABLE_JIT 0 #endif -#if defined(Q_OS_QNX) +#if defined(Q_OS_QNX) && defined(_CPPLIB_VER) #include #undef isnan #undef isfinite -- cgit v1.2.3 From c175beafad259a0c17661e140e745979632fdfdc Mon Sep 17 00:00:00 2001 From: James McDonnell Date: Tue, 7 Jun 2016 17:00:48 -0400 Subject: Add QNX to an x86-64 gcc/g++ list QNX 7.0 will support x86-64. Change-Id: I91d79f41161324fb992e17c2067e0648b476c773 Reviewed-by: Rafael Roquetto --- src/qml/jit/qv4targetplatform_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h index 1e62b23fe4..90ed1f972e 100644 --- a/src/qml/jit/qv4targetplatform_p.h +++ b/src/qml/jit/qv4targetplatform_p.h @@ -145,7 +145,7 @@ public: #endif // Windows on x86 -#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X) || OS(FREEBSD)) +#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X) || OS(FREEBSD) || OS(QNX)) enum { RegAllocIsSupported = 1 }; static const JSC::MacroAssembler::RegisterID StackFrameRegister = JSC::X86Registers::ebp; -- cgit v1.2.3 From 2e950afe90d480be6bc18155b9ffab7e855d0dd9 Mon Sep 17 00:00:00 2001 From: Frank Meerkoetter Date: Mon, 9 May 2016 22:31:42 +0200 Subject: Use the QStringBuilder API to save allocations Change-Id: Iae2b08715157325f031f1c08b393cd682908029f Reviewed-by: Anton Kudryavtsev Reviewed-by: Simon Hausmann --- src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index e08436b7a3..209aa6eda6 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -758,10 +758,8 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth paramStr.append(QString::fromUtf8(paramNames.at(ii))); } - QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr + - QLatin1String(") {"); - jsfunction += body; - jsfunction += QLatin1String("\n})"); + const QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr + + QLatin1String(") {") + body + QLatin1String("\n})"); QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object); Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this -- cgit v1.2.3 From a6c0a864751eb017da63317a67bc61541fddb480 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 13 Jun 2016 10:14:33 +0200 Subject: File naming cleanup Separate out the property cache creation code into a standalone file. Change-Id: Ib67bb1ef72c3de70ebd1ca8cae41947cbad7bfe3 Reviewed-by: Frank Meerkoetter --- src/qml/compiler/compiler.pri | 6 +- src/qml/compiler/qqmlpropertycachecreator.cpp | 495 ++++++++++++++++++++++++++ src/qml/compiler/qqmlpropertycachecreator_p.h | 79 ++++ src/qml/compiler/qqmltypecompiler.cpp | 445 +---------------------- src/qml/compiler/qqmltypecompiler_p.h | 20 -- 5 files changed, 580 insertions(+), 465 deletions(-) create mode 100644 src/qml/compiler/qqmlpropertycachecreator.cpp create mode 100644 src/qml/compiler/qqmlpropertycachecreator_p.h diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 585fef7603..3bcac65f81 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -26,12 +26,14 @@ SOURCES += \ HEADERS += \ $$PWD/qqmltypecompiler_p.h \ $$PWD/qv4isel_moth_p.h \ - $$PWD/qv4instr_moth_p.h + $$PWD/qv4instr_moth_p.h \ + $$PWD/qqmlpropertycachecreator_p.h SOURCES += \ $$PWD/qqmltypecompiler.cpp \ $$PWD/qv4instr_moth.cpp \ - $$PWD/qv4isel_moth.cpp + $$PWD/qv4isel_moth.cpp \ + $$PWD/qqmlpropertycachecreator.cpp } diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp new file mode 100644 index 0000000000..1b886d5efb --- /dev/null +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -0,0 +1,495 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qqmlpropertycachecreator_p.h" + +#include + +#define COMPILE_EXCEPTION(token, desc) \ + { \ + recordError((token)->location, desc); \ + return false; \ + } + +QT_BEGIN_NAMESPACE + +static QAtomicInt classIndexCounter(0); + +QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , enginePrivate(typeCompiler->enginePrivate()) + , qmlObjects(*typeCompiler->qmlObjects()) + , imports(typeCompiler->imports()) + , resolvedTypes(typeCompiler->resolvedTypes()) +{ +} + +QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator() +{ + for (int i = 0; i < propertyCaches.count(); ++i) + if (QQmlPropertyCache *cache = propertyCaches.at(i).data()) + cache->release(); + propertyCaches.clear(); +} + +bool QQmlPropertyCacheCreator::buildMetaObjects() +{ + propertyCaches.resize(qmlObjects.count()); + + if (!buildMetaObjectRecursively(compiler->rootObjectIndex(), /*referencing object*/-1, /*instantiating binding*/0)) + return false; + + compiler->setPropertyCaches(propertyCaches); + propertyCaches.clear(); + + return true; +} + +bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding) +{ + const QmlIR::Object *obj = qmlObjects.at(objectIndex); + + QQmlPropertyCache *baseTypeCache = 0; + QQmlPropertyData *instantiatingProperty = 0; + if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) { + Q_ASSERT(referencingObjectIndex >= 0); + QQmlPropertyCache *parentCache = propertyCaches.at(referencingObjectIndex).data(); + Q_ASSERT(parentCache); + Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); + + bool notInRevision = false; + instantiatingProperty = QmlIR::PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), ¬InRevision); + if (instantiatingProperty) { + if (instantiatingProperty->isQObject()) { + baseTypeCache = enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType); + Q_ASSERT(baseTypeCache); + } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType)) { + baseTypeCache = enginePrivate->cache(vtmo); + Q_ASSERT(baseTypeCache); + } + } + } + + bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; + if (!needVMEMetaObject) { + for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + + // On assignments are implemented using value interceptors, which require a VME meta object. + needVMEMetaObject = true; + + // If the on assignment is inside a group property, we need to distinguish between QObject based + // group properties and value type group properties. For the former the base type is derived from + // the property that references us, for the latter we only need a meta-object on the referencing object + // because interceptors can't go to the shared value type instances. + if (instantiatingProperty && QQmlValueTypeFactory::isValueType(instantiatingProperty->propType)) { + needVMEMetaObject = false; + if (!ensureVMEMetaObject(referencingObjectIndex)) + return false; + } + break; + } + } + } + + if (obj->inheritedTypeNameIndex != 0) { + auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + + if (typeRef->isFullyDynamicType) { + if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { + recordError(obj->location, tr("Fully dynamic types cannot declare new properties.")); + return false; + } + if (obj->signalCount() > 0) { + recordError(obj->location, tr("Fully dynamic types cannot declare new signals.")); + return false; + } + if (obj->functionCount() > 0) { + recordError(obj->location, tr("Fully Dynamic types cannot declare new functions.")); + return false; + } + } + + baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + Q_ASSERT(baseTypeCache); + } else if (instantiatingBinding && instantiatingBinding->isAttachedProperty()) { + auto *typeRef = resolvedTypes->value(instantiatingBinding->propertyNameIndex); + Q_ASSERT(typeRef); + QQmlType *qmltype = typeRef->type; + if (!qmltype) { + QString propertyName = stringAt(instantiatingBinding->propertyNameIndex); + if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) { + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); + + tdata->release(); + } + } + } + + const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; + if (!attachedMo) { + recordError(instantiatingBinding->location, tr("Non-existent attached object")); + return false; + } + baseTypeCache = enginePrivate->cache(attachedMo); + Q_ASSERT(baseTypeCache); + } + + if (baseTypeCache) { + if (needVMEMetaObject) { + if (!createMetaObject(objectIndex, obj, baseTypeCache)) + return false; + } else { + if (QQmlPropertyCache *oldCache = propertyCaches.at(objectIndex).data()) + oldCache->release(); + propertyCaches[objectIndex] = baseTypeCache; + baseTypeCache->addref(); + } + } + + if (propertyCaches.at(objectIndex).data()) { + for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) + if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + if (!buildMetaObjectRecursively(binding->value.objectIndex, objectIndex, binding)) + return false; + } + } + + return true; +} + +bool QQmlPropertyCacheCreator::ensureVMEMetaObject(int objectIndex) +{ + const bool willCreateVMEMetaObject = propertyCaches.at(objectIndex).flag(); + if (willCreateVMEMetaObject) + return true; + const QmlIR::Object *obj = qmlObjects.at(objectIndex); + auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + return createMetaObject(objectIndex, obj, baseTypeCache); +} + +bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache) +{ + QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), + obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), + obj->signalCount() + obj->propertyCount() + obj->aliasCount()); + + if (QQmlPropertyCache *oldCache = propertyCaches.at(objectIndex).data()) + oldCache->release(); + propertyCaches[objectIndex] = cache; + // Indicate that this object also needs a VME meta-object at run-time + propertyCaches[objectIndex].setFlag(); + + struct TypeData { + QV4::CompiledData::Property::Type dtype; + int metaType; + } builtinTypes[] = { + { QV4::CompiledData::Property::Var, QMetaType::QVariant }, + { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, + { QV4::CompiledData::Property::Int, QMetaType::Int }, + { QV4::CompiledData::Property::Bool, QMetaType::Bool }, + { QV4::CompiledData::Property::Real, QMetaType::Double }, + { QV4::CompiledData::Property::String, QMetaType::QString }, + { QV4::CompiledData::Property::Url, QMetaType::QUrl }, + { QV4::CompiledData::Property::Color, QMetaType::QColor }, + { QV4::CompiledData::Property::Font, QMetaType::QFont }, + { QV4::CompiledData::Property::Time, QMetaType::QTime }, + { QV4::CompiledData::Property::Date, QMetaType::QDate }, + { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, + { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, + { QV4::CompiledData::Property::Point, QMetaType::QPointF }, + { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, + { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, + { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, + { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, + { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, + { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } + }; + static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); + + QByteArray newClassName; + + if (objectIndex == compiler->rootObjectIndex()) { + QString path = compiler->url().path(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + if (lastSlash > -1) { + const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); + if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) + newClassName = nameBase.toUtf8() + "_QMLTYPE_" + + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); + } + } + if (newClassName.isEmpty()) { + newClassName = QQmlMetaObject(baseTypeCache).className(); + newClassName.append("_QML_"); + newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); + } + + cache->_dynamicClassName = newClassName; + + int varPropCount = 0; + + QmlIR::PropertyResolver resolver(baseTypeCache); + + for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) { + if (p->type == QV4::CompiledData::Property::Var) + varPropCount++; + + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); + if (d && d->isFinal()) + COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); + } + + for (const QmlIR::Alias *a = obj->firstAlias(); a; a = a->next) { + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); + if (d && d->isFinal()) + COMPILE_EXCEPTION(a, tr("Cannot override FINAL property")); + } + + int effectivePropertyIndex = cache->propertyIndexCacheStart; + int effectiveMethodIndex = cache->methodIndexCacheStart; + + // For property change signal override detection. + // We prepopulate a set of signal names which already exist in the object, + // and throw an error if there is a signal/method defined as an override. + QSet seenSignals; + seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); + QQmlPropertyCache *parentCache = cache; + while ((parentCache = parentCache->parent())) { + if (int pSigCount = parentCache->signalCount()) { + int pSigOffset = parentCache->signalOffset(); + for (int i = pSigOffset; i < pSigCount; ++i) { + QQmlPropertyData *currPSig = parentCache->signal(i); + // XXX TODO: find a better way to get signal name from the property data :-/ + for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); + iter != parentCache->stringCache.end(); ++iter) { + if (currPSig == (*iter).second) { + seenSignals.insert(iter.key()); + break; + } + } + } + } + } + + // Set up notify signals for properties - first normal, then alias + for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) { + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + + QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + + for (const QmlIR::Alias *a = obj->firstAlias(); a; a = a->next) { + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + + QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + + // Dynamic signals + for (const QmlIR::Signal *s = obj->firstSignal(); s; s = s->next) { + const int paramCount = s->parameters->count; + + QList names; + names.reserve(paramCount); + QVarLengthArray paramTypes(paramCount?(paramCount + 1):0); + + if (paramCount) { + paramTypes[0] = paramCount; + + QmlIR::SignalParameter *param = s->parameters->first; + for (int i = 0; i < paramCount; ++i, param = param->next) { + names.append(stringAt(param->nameIndex).toUtf8()); + if (param->type < builtinTypeCount) { + // built-in type + paramTypes[i + 1] = builtinTypes[param->type].metaType; + } else { + // lazily resolved type + Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); + const QString customTypeName = stringAt(param->customTypeNameIndex); + QQmlType *qmltype = 0; + if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) + COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(customTypeName)); + + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + + paramTypes[i + 1] = compilationUnit->metaTypeId; + + tdata->release(); + } else { + paramTypes[i + 1] = qmltype->typeId(); + } + } + } + } + + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + if (paramCount) + flags |= QQmlPropertyData::HasArguments; + + QString signalName = stringAt(s->nameIndex); + if (seenSignals.contains(signalName)) + COMPILE_EXCEPTION(s, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); + seenSignals.insert(signalName); + + cache->appendSignal(signalName, flags, effectiveMethodIndex++, + paramCount?paramTypes.constData():0, names); + } + + + // Dynamic slots + for (const QmlIR::Function *s = obj->firstFunction(); s; s = s->next) { + QQmlJS::AST::FunctionDeclaration *astFunction = s->functionDeclaration; + + quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; + + if (astFunction->formals) + flags |= QQmlPropertyData::HasArguments; + + QString slotName = astFunction->name.toString(); + if (seenSignals.contains(slotName)) + COMPILE_EXCEPTION(s, tr("Duplicate method name: invalid override of property change signal or superclass signal")); + // Note: we don't append slotName to the seenSignals list, since we don't + // protect against overriding change signals or methods with properties. + + QList parameterNames; + QQmlJS::AST::FormalParameterList *param = astFunction->formals; + while (param) { + parameterNames << param->name.toUtf8(); + param = param->next; + } + + cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); + } + + + // Dynamic properties + int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; + int propertyIdx = 0; + for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) { + int propertyType = 0; + quint32 propertyFlags = 0; + + if (p->type == QV4::CompiledData::Property::Var) { + propertyType = QMetaType::QVariant; + propertyFlags = QQmlPropertyData::IsVarProperty; + } else if (p->type < builtinTypeCount) { + propertyType = builtinTypes[p->type].metaType; + + if (p->type == QV4::CompiledData::Property::Variant) + propertyFlags |= QQmlPropertyData::IsQVariant; + } else { + Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || + p->type == QV4::CompiledData::Property::Custom); + + QQmlType *qmltype = 0; + if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { + COMPILE_EXCEPTION(p, tr("Invalid property type")); + } + + Q_ASSERT(qmltype); + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = compilationUnit->metaTypeId; + } else { + propertyType = compilationUnit->listMetaTypeId; + } + + tdata->release(); + } else { + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = qmltype->typeId(); + } else { + propertyType = qmltype->qListTypeId(); + } + } + + if (p->type == QV4::CompiledData::Property::Custom) + propertyFlags |= QQmlPropertyData::IsQObjectDerived; + else + propertyFlags |= QQmlPropertyData::IsQList; + } + + if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) + propertyFlags |= QQmlPropertyData::IsWritable; + + + QString propertyName = stringAt(p->nameIndex); + if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) + cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + propertyType, effectiveSignalIndex); + + effectiveSignalIndex++; + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h new file mode 100644 index 0000000000..1a3a96e4e3 --- /dev/null +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ +#ifndef QQMLPROPERTYCACHECREATOR_P_H +#define QQMLPROPERTYCACHECREATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmltypecompiler_p.h" + +QT_BEGIN_NAMESPACE + +class QQmlPropertyCacheCreator : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator) +public: + QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler); + ~QQmlPropertyCacheCreator(); + + bool buildMetaObjects(); +protected: + bool buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding); + bool ensureVMEMetaObject(int objectIndex); + bool createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache); + + QQmlEnginePrivate *enginePrivate; + const QList &qmlObjects; + const QQmlImports *imports; + QHash *resolvedTypes; + QQmlPropertyCacheVector propertyCaches; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYCACHECREATOR_P_H diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index b9a3f1419c..e48b3eb449 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -47,6 +47,8 @@ #include #include +#include "qqmlpropertycachecreator_p.h" + #define COMPILE_EXCEPTION(token, desc) \ { \ recordError((token)->location, desc); \ @@ -395,449 +397,6 @@ void QQmlCompilePass::recordError(const QV4::CompiledData::Location &location, c compiler->recordError(error); } -static QAtomicInt classIndexCounter(0); - -QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , enginePrivate(typeCompiler->enginePrivate()) - , qmlObjects(*typeCompiler->qmlObjects()) - , imports(typeCompiler->imports()) - , resolvedTypes(typeCompiler->resolvedTypes()) -{ -} - -QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator() -{ - for (int i = 0; i < propertyCaches.count(); ++i) - if (QQmlPropertyCache *cache = propertyCaches.at(i).data()) - cache->release(); - propertyCaches.clear(); -} - -bool QQmlPropertyCacheCreator::buildMetaObjects() -{ - propertyCaches.resize(qmlObjects.count()); - - if (!buildMetaObjectRecursively(compiler->rootObjectIndex(), /*referencing object*/-1, /*instantiating binding*/0)) - return false; - - compiler->setPropertyCaches(propertyCaches); - propertyCaches.clear(); - - return true; -} - -bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding) -{ - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - - QQmlPropertyCache *baseTypeCache = 0; - QQmlPropertyData *instantiatingProperty = 0; - if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) { - Q_ASSERT(referencingObjectIndex >= 0); - QQmlPropertyCache *parentCache = propertyCaches.at(referencingObjectIndex).data(); - Q_ASSERT(parentCache); - Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); - - bool notInRevision = false; - instantiatingProperty = QmlIR::PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), ¬InRevision); - if (instantiatingProperty) { - if (instantiatingProperty->isQObject()) { - baseTypeCache = enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType); - Q_ASSERT(baseTypeCache); - } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType)) { - baseTypeCache = enginePrivate->cache(vtmo); - Q_ASSERT(baseTypeCache); - } - } - } - - bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; - if (!needVMEMetaObject) { - for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { - - // On assignments are implemented using value interceptors, which require a VME meta object. - needVMEMetaObject = true; - - // If the on assignment is inside a group property, we need to distinguish between QObject based - // group properties and value type group properties. For the former the base type is derived from - // the property that references us, for the latter we only need a meta-object on the referencing object - // because interceptors can't go to the shared value type instances. - if (instantiatingProperty && QQmlValueTypeFactory::isValueType(instantiatingProperty->propType)) { - needVMEMetaObject = false; - if (!ensureVMEMetaObject(referencingObjectIndex)) - return false; - } - break; - } - } - } - - if (obj->inheritedTypeNameIndex != 0) { - auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - - if (typeRef->isFullyDynamicType) { - if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { - recordError(obj->location, tr("Fully dynamic types cannot declare new properties.")); - return false; - } - if (obj->signalCount() > 0) { - recordError(obj->location, tr("Fully dynamic types cannot declare new signals.")); - return false; - } - if (obj->functionCount() > 0) { - recordError(obj->location, tr("Fully Dynamic types cannot declare new functions.")); - return false; - } - } - - baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - Q_ASSERT(baseTypeCache); - } else if (instantiatingBinding && instantiatingBinding->isAttachedProperty()) { - auto *typeRef = resolvedTypes->value(instantiatingBinding->propertyNameIndex); - Q_ASSERT(typeRef); - QQmlType *qmltype = typeRef->type; - if (!qmltype) { - QString propertyName = stringAt(instantiatingBinding->propertyNameIndex); - if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) { - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - - tdata->release(); - } - } - } - - const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; - if (!attachedMo) { - recordError(instantiatingBinding->location, tr("Non-existent attached object")); - return false; - } - baseTypeCache = enginePrivate->cache(attachedMo); - Q_ASSERT(baseTypeCache); - } - - if (baseTypeCache) { - if (needVMEMetaObject) { - if (!createMetaObject(objectIndex, obj, baseTypeCache)) - return false; - } else { - if (QQmlPropertyCache *oldCache = propertyCaches.at(objectIndex).data()) - oldCache->release(); - propertyCaches[objectIndex] = baseTypeCache; - baseTypeCache->addref(); - } - } - - if (propertyCaches.at(objectIndex).data()) { - for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - if (!buildMetaObjectRecursively(binding->value.objectIndex, objectIndex, binding)) - return false; - } - } - - return true; -} - -bool QQmlPropertyCacheCreator::ensureVMEMetaObject(int objectIndex) -{ - const bool willCreateVMEMetaObject = propertyCaches.at(objectIndex).flag(); - if (willCreateVMEMetaObject) - return true; - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - return createMetaObject(objectIndex, obj, baseTypeCache); -} - -bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache) -{ - QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), - obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), - obj->signalCount() + obj->propertyCount() + obj->aliasCount()); - - if (QQmlPropertyCache *oldCache = propertyCaches.at(objectIndex).data()) - oldCache->release(); - propertyCaches[objectIndex] = cache; - // Indicate that this object also needs a VME meta-object at run-time - propertyCaches[objectIndex].setFlag(); - - struct TypeData { - QV4::CompiledData::Property::Type dtype; - int metaType; - } builtinTypes[] = { - { QV4::CompiledData::Property::Var, QMetaType::QVariant }, - { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, - { QV4::CompiledData::Property::Int, QMetaType::Int }, - { QV4::CompiledData::Property::Bool, QMetaType::Bool }, - { QV4::CompiledData::Property::Real, QMetaType::Double }, - { QV4::CompiledData::Property::String, QMetaType::QString }, - { QV4::CompiledData::Property::Url, QMetaType::QUrl }, - { QV4::CompiledData::Property::Color, QMetaType::QColor }, - { QV4::CompiledData::Property::Font, QMetaType::QFont }, - { QV4::CompiledData::Property::Time, QMetaType::QTime }, - { QV4::CompiledData::Property::Date, QMetaType::QDate }, - { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, - { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, - { QV4::CompiledData::Property::Point, QMetaType::QPointF }, - { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, - { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, - { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, - { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, - { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, - { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } - }; - static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); - - QByteArray newClassName; - - if (objectIndex == compiler->rootObjectIndex()) { - QString path = compiler->url().path(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - if (lastSlash > -1) { - const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); - if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) - newClassName = nameBase.toUtf8() + "_QMLTYPE_" + - QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); - } - } - if (newClassName.isEmpty()) { - newClassName = QQmlMetaObject(baseTypeCache).className(); - newClassName.append("_QML_"); - newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); - } - - cache->_dynamicClassName = newClassName; - - int varPropCount = 0; - - QmlIR::PropertyResolver resolver(baseTypeCache); - - for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) { - if (p->type == QV4::CompiledData::Property::Var) - varPropCount++; - - bool notInRevision = false; - QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); - if (d && d->isFinal()) - COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); - } - - for (const QmlIR::Alias *a = obj->firstAlias(); a; a = a->next) { - bool notInRevision = false; - QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); - if (d && d->isFinal()) - COMPILE_EXCEPTION(a, tr("Cannot override FINAL property")); - } - - int effectivePropertyIndex = cache->propertyIndexCacheStart; - int effectiveMethodIndex = cache->methodIndexCacheStart; - - // For property change signal override detection. - // We prepopulate a set of signal names which already exist in the object, - // and throw an error if there is a signal/method defined as an override. - QSet seenSignals; - seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); - QQmlPropertyCache *parentCache = cache; - while ((parentCache = parentCache->parent())) { - if (int pSigCount = parentCache->signalCount()) { - int pSigOffset = parentCache->signalOffset(); - for (int i = pSigOffset; i < pSigCount; ++i) { - QQmlPropertyData *currPSig = parentCache->signal(i); - // XXX TODO: find a better way to get signal name from the property data :-/ - for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); - iter != parentCache->stringCache.end(); ++iter) { - if (currPSig == (*iter).second) { - seenSignals.insert(iter.key()); - break; - } - } - } - } - } - - // Set up notify signals for properties - first normal, then alias - for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) { - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; - - QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); - seenSignals.insert(changedSigName); - - cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); - } - - for (const QmlIR::Alias *a = obj->firstAlias(); a; a = a->next) { - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; - - QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); - seenSignals.insert(changedSigName); - - cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); - } - - // Dynamic signals - for (const QmlIR::Signal *s = obj->firstSignal(); s; s = s->next) { - const int paramCount = s->parameters->count; - - QList names; - names.reserve(paramCount); - QVarLengthArray paramTypes(paramCount?(paramCount + 1):0); - - if (paramCount) { - paramTypes[0] = paramCount; - - QmlIR::SignalParameter *param = s->parameters->first; - for (int i = 0; i < paramCount; ++i, param = param->next) { - names.append(stringAt(param->nameIndex).toUtf8()); - if (param->type < builtinTypeCount) { - // built-in type - paramTypes[i + 1] = builtinTypes[param->type].metaType; - } else { - // lazily resolved type - Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); - const QString customTypeName = stringAt(param->customTypeNameIndex); - QQmlType *qmltype = 0; - if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) - COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(customTypeName)); - - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - - paramTypes[i + 1] = compilationUnit->metaTypeId; - - tdata->release(); - } else { - paramTypes[i + 1] = qmltype->typeId(); - } - } - } - } - - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; - if (paramCount) - flags |= QQmlPropertyData::HasArguments; - - QString signalName = stringAt(s->nameIndex); - if (seenSignals.contains(signalName)) - COMPILE_EXCEPTION(s, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); - seenSignals.insert(signalName); - - cache->appendSignal(signalName, flags, effectiveMethodIndex++, - paramCount?paramTypes.constData():0, names); - } - - - // Dynamic slots - for (const QmlIR::Function *s = obj->firstFunction(); s; s = s->next) { - QQmlJS::AST::FunctionDeclaration *astFunction = s->functionDeclaration; - - quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; - - if (astFunction->formals) - flags |= QQmlPropertyData::HasArguments; - - QString slotName = astFunction->name.toString(); - if (seenSignals.contains(slotName)) - COMPILE_EXCEPTION(s, tr("Duplicate method name: invalid override of property change signal or superclass signal")); - // Note: we don't append slotName to the seenSignals list, since we don't - // protect against overriding change signals or methods with properties. - - QList parameterNames; - QQmlJS::AST::FormalParameterList *param = astFunction->formals; - while (param) { - parameterNames << param->name.toUtf8(); - param = param->next; - } - - cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); - } - - - // Dynamic properties - int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; - int propertyIdx = 0; - for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) { - int propertyType = 0; - quint32 propertyFlags = 0; - - if (p->type == QV4::CompiledData::Property::Var) { - propertyType = QMetaType::QVariant; - propertyFlags = QQmlPropertyData::IsVarProperty; - } else if (p->type < builtinTypeCount) { - propertyType = builtinTypes[p->type].metaType; - - if (p->type == QV4::CompiledData::Property::Variant) - propertyFlags |= QQmlPropertyData::IsQVariant; - } else { - Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || - p->type == QV4::CompiledData::Property::Custom); - - QQmlType *qmltype = 0; - if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { - COMPILE_EXCEPTION(p, tr("Invalid property type")); - } - - Q_ASSERT(qmltype); - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = compilationUnit->metaTypeId; - } else { - propertyType = compilationUnit->listMetaTypeId; - } - - tdata->release(); - } else { - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = qmltype->typeId(); - } else { - propertyType = qmltype->qListTypeId(); - } - } - - if (p->type == QV4::CompiledData::Property::Custom) - propertyFlags |= QQmlPropertyData::IsQObjectDerived; - else - propertyFlags |= QQmlPropertyData::IsQList; - } - - if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) - propertyFlags |= QQmlPropertyData::IsWritable; - - - QString propertyName = stringAt(p->nameIndex); - if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) - cache->_defaultPropertyName = propertyName; - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - propertyType, effectiveSignalIndex); - - effectiveSignalIndex++; - } - - return true; -} - SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) , enginePrivate(typeCompiler->enginePrivate()) diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 915626e183..c67137bcbd 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -139,26 +139,6 @@ protected: QQmlTypeCompiler *compiler; }; -class QQmlPropertyCacheCreator : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator) -public: - QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler); - ~QQmlPropertyCacheCreator(); - - bool buildMetaObjects(); -protected: - bool buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding); - bool ensureVMEMetaObject(int objectIndex); - bool createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache); - - QQmlEnginePrivate *enginePrivate; - const QList &qmlObjects; - const QQmlImports *imports; - QHash *resolvedTypes; - QQmlPropertyCacheVector propertyCaches; -}; - // "Converts" signal expressions to full-fleged function declarations with // parameters taken from the signal declarations // It also updates the QV4::CompiledData::Binding objects to set the property name -- cgit v1.2.3 From 1ef97c39c371f469df932236f9ede4da408734cb Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 30 May 2016 14:16:49 +0200 Subject: categorized logging: trace hover events Tracing is a kind of logging designed specifically for use with http://code.qt.io/cgit/qt-labs/umlquick.git, to generate UML Sequence diagrams. Here it is being used to trace the delivery of hover events. Due to use of the QT_MESSAGE_PATTERN backtrace directive, and backtrace splicing, it's not necessary to emit trace messages for every step: it's mainly important to catch the final destinations of hover processing, where state is changed and signals emitted. Change-Id: I5f640732f90990ef9f89767a56f3978b9c28a410 Reviewed-by: Robin Burchell --- src/quick/items/qquickitem.cpp | 2 ++ src/quick/items/qquickmousearea.cpp | 3 +++ src/quick/items/qquicktext.cpp | 2 ++ src/quick/items/qquicktextcontrol.cpp | 3 +++ src/quick/items/qquickwindow.cpp | 3 +++ 5 files changed, 13 insertions(+) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 1bffaf92d0..17081eca17 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -86,6 +86,7 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(DBG_MOUSE_TARGET) +Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) #ifndef QT_NO_DEBUG static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"); @@ -7079,6 +7080,7 @@ void QQuickItemPrivate::setHasHoverInChild(bool hasHover) } } + qCDebug(DBG_HOVER_TRACE) << q << hasHoverInChild << "->" << hasHover; hasHoverInChild = hasHover; QQuickItem *parent = q->parentItem(); if (parent) { diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 0219bc809f..297a57e672 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -54,6 +54,8 @@ QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING) +Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) + QQuickMouseAreaPrivate::QQuickMouseAreaPrivate() : enabled(true), scrollGestureEnabled(true), hovered(false), longPress(false), moved(false), stealMouse(false), doubleClick(false), preventStealing(false), @@ -1129,6 +1131,7 @@ void QQuickMouseArea::setHovered(bool h) { Q_D(QQuickMouseArea); if (d->hovered != h) { + qCDebug(DBG_HOVER_TRACE) << this << d->hovered << "->" << h; d->hovered = h; emit hoveredChanged(); d->hovered ? emit entered() : emit exited(); diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 648929c8cd..33f44520fe 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -69,6 +69,7 @@ QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) const QChar QQuickTextPrivate::elideChar = QChar(0x2026); @@ -2720,6 +2721,7 @@ QString QQuickText::hoveredLink() const void QQuickTextPrivate::processHoverEvent(QHoverEvent *event) { Q_Q(QQuickText); + qCDebug(DBG_HOVER_TRACE) << q; QString link; if (isLinkHoveredConnected()) { if (event->type() != QEvent::HoverLeave) diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index 45238e2d0c..ef7485a8e9 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -58,6 +58,7 @@ #include "qtextlist.h" #include "qtextdocumentwriter.h" #include "private/qtextcursor_p.h" +#include #include #include @@ -76,6 +77,7 @@ const int textCursorWidth = 1; QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) #ifndef QT_NO_CONTEXTMENU #endif @@ -1513,6 +1515,7 @@ void QQuickTextControlPrivate::hoverEvent(QHoverEvent *e, const QPointF &pos) hoveredLink = link; emit q->linkHovered(link); } + qCDebug(DBG_HOVER_TRACE) << q << e->type() << pos << "hoveredLink" << hoveredLink; } bool QQuickTextControl::hasImState() const diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 478e01388e..eb035c571d 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -85,6 +85,7 @@ Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch") Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target") Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse") Q_LOGGING_CATEGORY(DBG_MOUSE_TARGET, "qt.quick.mouse.target") +Q_LOGGING_CATEGORY(DBG_HOVER_TRACE, "qt.quick.hover.trace") Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus") Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty") @@ -1751,6 +1752,7 @@ void QQuickWindow::mouseMoveEvent(QMouseEvent *event) } qCDebug(DBG_MOUSE) << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons(); + qCDebug(DBG_HOVER_TRACE) << this; #ifndef QT_NO_CURSOR d->updateCursor(event->windowPos()); @@ -1787,6 +1789,7 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce return false; } + qCDebug(DBG_HOVER_TRACE) << q << item << scenePos << lastScenePos << "hasHoverInChild" << itemPrivate->hasHoverInChild; if (itemPrivate->hasHoverInChild) { QList children = itemPrivate->paintOrderChildItems(); for (int ii = children.count() - 1; ii >= 0; --ii) { -- cgit v1.2.3 From 7190aa26f65ab97b4f54c156a107ed7748a11df5 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 9 Jun 2016 13:58:40 +0200 Subject: Support glyphs outside em square in distance field cache In the distance field cache, we would assume a max height of glyphs at 64 pixels when the pixel size was 54 (em square size is 54 and then some margin). This is an arbitrary assumption which does not necessarily hold true. Indeed, some fonts would be clipped at the bottom when assuming this. [ChangeLog][QtQuick][Text] Fixed clipping of glyphs that extend beyond font's em square. Task-number: QTBUG-52389 Change-Id: I5f6c9be235d38841e40d1bd60fb380e4712dd41a Reviewed-by: Yoann Lopes --- src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index dcc485ce17..728d8b6541 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -97,8 +97,10 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet &glyph glyph_t glyphIndex = *it; int padding = QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING; - int glyphWidth = qCeil(glyphData(glyphIndex).boundingRect.width()) + distanceFieldRadius() * 2; - QSize glyphSize(glyphWidth + padding * 2, QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()) + padding * 2); + QRectF boundingRect = glyphData(glyphIndex).boundingRect; + int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2; + int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2; + QSize glyphSize(glyphWidth + padding * 2, glyphHeight + padding * 2); QRect alloc = m_areaAllocator->allocate(glyphSize); if (alloc.isNull()) { @@ -107,11 +109,13 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet &glyph glyph_t unusedGlyph = *m_unusedGlyphs.constBegin(); TexCoord unusedCoord = glyphTexCoord(unusedGlyph); - int unusedGlyphWidth = qCeil(glyphData(unusedGlyph).boundingRect.width()) + distanceFieldRadius() * 2; + QRectF unusedGlyphBoundingRect = glyphData(unusedGlyph).boundingRect; + int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width()) + distanceFieldRadius() * 2; + int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height()) + distanceFieldRadius() * 2; m_areaAllocator->deallocate(QRect(unusedCoord.x - padding, unusedCoord.y - padding, padding * 2 + unusedGlyphWidth, - padding * 2 + QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()))); + padding * 2 + unusedGlyphHeight)); m_unusedGlyphs.remove(unusedGlyph); m_glyphsTexture.remove(unusedGlyph); -- cgit v1.2.3 From 5f92f784834f8648857fa6056ac0a93c96c60543 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 14 Jun 2016 22:27:21 +0200 Subject: Remove erronous setTextureSize() as it is already set previously This was causing problems with the FrameBuffer render target when displaying on a HiDpi display as it would end up with a size which was not accounting for the devicePixelRatio. Task-number: QTBUG-52901 Change-Id: I587e2578d670cfecb016aae92c4190a37e7412a1 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/util/qsgdefaultpainternode.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp index 38bcc7a3f3..e3103341cd 100644 --- a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp +++ b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp @@ -310,7 +310,6 @@ void QSGDefaultPainterNode::updateRenderTarget() if (m_texture) delete m_texture; - texture->setTextureSize(m_size); m_texture = texture; } -- cgit v1.2.3 From 79745bf95cfb656b7ea75850c2b378cac3ea18ec Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 15 Jun 2016 16:21:15 +0200 Subject: Expose QQmlComponentPrivate::setInitialProperties() QQuickStackView::push() can be used to push not only URLs or Components, but also plain Items. This patch makes it possible for StackView to initialize properties for plain Items without having to create a useless QQmlComponent instance just to be able to call initializeObjectWithInitialProperties(). Change-Id: Id538028d09c1319da56b26695ebd407f4c0fa27a Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlcomponent.cpp | 6 +++--- src/qml/qml/qqmlcomponent_p.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index e615318636..52f6837842 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1174,7 +1174,7 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent) */ -static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v) +void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v) { QV4::Scope scope(engine); QV4::ScopedObject object(scope); @@ -1263,7 +1263,7 @@ void QQmlComponent::createObject(QQmlV4Function *args) if (!valuemap->isUndefined()) { QV4::Scoped qmlContext(scope, v4->qmlContext()); - setInitialProperties(v4, qmlContext, object, valuemap); + QQmlComponentPrivate::setInitialProperties(v4, qmlContext, object, valuemap); } d->completeCreate(); @@ -1496,7 +1496,7 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o) QV4::Scope scope(v4); QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o)); QV4::Scoped qmlCtxt(scope, d()->qmlContext); - setInitialProperties(v4, qmlCtxt, obj, d()->valuemap); + QQmlComponentPrivate::setInitialProperties(v4, qmlCtxt, obj, d()->valuemap); } } diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index be69986117..3a84e724da 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -86,6 +86,7 @@ public: QObject *beginCreate(QQmlContextData *); void completeCreate(); void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate); + static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v); QQmlTypeData *typeData; virtual void typeDataReady(QQmlTypeData *); -- cgit v1.2.3 From 9b1231ca3d21ade574a8a7cf3f0805a8b520bcd5 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 8 Jun 2016 17:32:32 +0200 Subject: QML: Only release types if they aren't referenced anymore Just checking for references on m_compiledData is not enough. The actual component can also be referenced. Thus it won't be deleted on release(), but cannot be found in the type cache anymore. Task-number: QTBUG-53761 Change-Id: I8567af8e75a078598e4fed31e4717134e1332278 Reviewed-by: Mitch Curtis Reviewed-by: Simon Hausmann (cherry picked from commit 2ac19881f92c94f4e9427bd9ff513210675f259e) --- src/qml/qml/qqmltypeloader.cpp | 3 ++- tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index c684c8602e..01200fd881 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1961,7 +1961,8 @@ void QQmlTypeLoader::trimCache() QList unneededTypes; for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter) { QQmlTypeData *typeData = iter.value(); - if (typeData->m_compiledData && typeData->m_compiledData->count() == 1) { + if (typeData->m_compiledData && typeData->count() == 1 + && typeData->m_compiledData->count() == 1) { // There are no live objects of this type unneededTypes.append(iter); } diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index 7045c7cbd4..a1eaa0567f 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -86,10 +86,19 @@ void tst_QQMLTypeLoader::trimCache() url.setQuery(QString::number(i)); QQmlTypeData *data = loader.getType(url); - if (i % 5 == 0) // keep references to some of them so that they aren't trimmed - data->compiledData()->addref(); + // Run an event loop to receive the callback that release()es. + QTRY_COMPARE(data->count(), 2); - data->release(); + // keep references to some of them so that they aren't trimmed. References to either the + // QQmlTypeData or its compiledData() should prevent the trimming. + if (i % 10 == 0) { + // keep ref on data, don't add ref on data->compiledData() + } else if (i % 5 == 0) { + data->compiledData()->addref(); + data->release(); + } else { + data->release(); + } } for (int i = 0; i < 256; ++i) { -- cgit v1.2.3 From 547d0bdf09c46f361966809f8d7db3a581969799 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 20 May 2016 16:17:59 +0200 Subject: highdpi manual test: include not qDebug.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I24b3ccd23fdb42d44978debeeec96bac179edba6 Reviewed-by: Morten Johan Sørvig --- tests/manual/highdpi/imageprovider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/manual/highdpi/imageprovider.cpp b/tests/manual/highdpi/imageprovider.cpp index 33a69cb87e..069fa5998f 100644 --- a/tests/manual/highdpi/imageprovider.cpp +++ b/tests/manual/highdpi/imageprovider.cpp @@ -45,7 +45,7 @@ #include #include #include -#include +#include class ColorImageProvider : public QQuickImageProvider { -- cgit v1.2.3 From d2f1e081b9de201fd45e67b78567c6d720d0c6bf Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 3 Jun 2016 20:18:35 +0200 Subject: ItemViews: Make the wording of the warning on cache properties slightly sterner. I have seen far too many cases of someone simply setting cacheBuffer to a massive number in an attempt to workaround a slow delegate. It should be explicit that cacheBuffer is not a solution for these problems. Change-Id: I09416d06ff7faf51a104e09ca5f6b3593ddc53c7 Reviewed-by: Simon Hausmann --- src/quick/items/qquickgridview.cpp | 10 ++++++---- src/quick/items/qquicklistview.cpp | 10 ++++++---- src/quick/items/qquickpathview.cpp | 10 ++++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 8d84144519..6e5710a97b 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -1524,10 +1524,12 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight) Note that cacheBuffer is not a pixel buffer - it only maintains additional instantiated delegates. - Setting this value can make scrolling the list smoother at the expense - of additional memory usage. It is not a substitute for creating efficient - delegates; the fewer objects and bindings in a delegate, the faster a view may be - scrolled. + \note Setting this property is not a replacement for creating efficient delegates. + It can improve the smoothness of scrolling behavior at the expense of additional + memory usage. The fewer objects and bindings in a delegate, the faster a + view can be scrolled. It is important to realize that setting a cacheBuffer + will only postpone issues caused by slow-loading delegates, it is not a + solution for this scenario. The cacheBuffer operates outside of any display margins specified by displayMarginBeginning or displayMarginEnd. diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 242fdd2cfa..78a64c5df1 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -2197,10 +2197,12 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation) Note that cacheBuffer is not a pixel buffer - it only maintains additional instantiated delegates. - Setting this value can improve the smoothness of scrolling behavior at the expense - of additional memory usage. It is not a substitute for creating efficient - delegates; the fewer objects and bindings in a delegate, the faster a view can be - scrolled. + \note Setting this property is not a replacement for creating efficient delegates. + It can improve the smoothness of scrolling behavior at the expense of additional + memory usage. The fewer objects and bindings in a delegate, the faster a + view can be scrolled. It is important to realize that setting a cacheBuffer + will only postpone issues caused by slow-loading delegates, it is not a + solution for this scenario. The cacheBuffer operates outside of any display margins specified by displayMarginBeginning or displayMarginEnd. diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 73146f015e..7e1fa95692 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1306,10 +1306,12 @@ void QQuickPathView::resetPathItemCount() allowing creation to occur across multiple frames and reducing the likelihood of skipping frames. - Setting this value can improve the smoothness of scrolling behavior at the expense - of additional memory usage. It is not a substitute for creating efficient - delegates; the fewer objects and bindings in a delegate, the faster a view can be - moved. + \note Setting this property is not a replacement for creating efficient delegates. + It can improve the smoothness of scrolling behavior at the expense of additional + memory usage. The fewer objects and bindings in a delegate, the faster a + view can be scrolled. It is important to realize that setting cacheItemCount + will only postpone issues caused by slow-loading delegates, it is not a + solution for this scenario. \sa pathItemCount */ -- cgit v1.2.3 From aa7c3b35ef9b737c574f436ea35452019a2ff29c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 16 Jun 2016 13:39:57 +0200 Subject: V4: Always set the tag when boxing a pointer in QV4::Value. All setters now store tags, so no-one can play loosy-goosy with the boxed values (and accidentally forget to "tag" a value, resulting in random garbage). Change-Id: Ia0b78aa038d3ff46d5292b14bd593de310da16a0 Reviewed-by: Simon Hausmann --- .../qmldbg_debugger/qqmlnativedebugservice.cpp | 2 +- src/qml/jsruntime/qv4arraydata.cpp | 10 +-- src/qml/jsruntime/qv4objectiterator.cpp | 5 -- src/qml/jsruntime/qv4persistent.cpp | 9 +-- src/qml/jsruntime/qv4scopedvalue_p.h | 16 +--- src/qml/jsruntime/qv4value_p.h | 94 ++++++++++++---------- 6 files changed, 63 insertions(+), 73 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp index f5cc78e77f..24d2a82413 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp @@ -549,7 +549,7 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject dict[QStringLiteral("name")] = name; dict[QStringLiteral("valueencoded")] = QStringLiteral("undefined"); output.append(dict); - } else if (result.ptr && result.ptr->_val) { + } else if (result.ptr && result.ptr->rawValue()) { collector.collect(&output, QString(), name, *result); } else { QJsonObject dict; diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index bd736d775e..ec0e6111ab 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -93,8 +93,8 @@ Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SparseArrayData)); static Q_ALWAYS_INLINE void storeValue(ReturnedValue *target, uint value) { - Value v = Value::fromReturnedValue(*target); - v.setValue(value); + Value v; + v.setTagValue(Value::fromReturnedValue(*target).tag(), value); *target = v.asReturnedValue(); } @@ -189,7 +189,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt n->value = i; } else { storeValue(lastFree, i); - sparse->arrayData[i].setTag(Value::Empty_Type); + sparse->arrayData[i].setEmpty(); lastFree = &sparse->arrayData[i].rawValueRef(); } } @@ -198,7 +198,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt if (toCopy < sparse->alloc) { for (uint i = toCopy; i < sparse->alloc; ++i) { storeValue(lastFree, i); - sparse->arrayData[i].setTag(Value::Empty_Type); + sparse->arrayData[i].setEmpty(); lastFree = &sparse->arrayData[i].rawValueRef(); } storeValue(lastFree, UINT_MAX); @@ -396,7 +396,7 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot) // found two slots in a row uint idx = Value::fromReturnedValue(*last).uint_32(); Value lastV = Value::fromReturnedValue(*last); - lastV.setValue(dd->arrayData[lastV.value() + 1].value()); + lastV.setTagValue(lastV.tag(), dd->arrayData[lastV.value() + 1].value()); *last = lastV.rawValue(); dd->attrs[idx] = Attr_Accessor; return idx; diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 1413f439b1..d4587df79b 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -68,11 +68,6 @@ void ObjectIterator::init(const Object *o) object->setM(o ? o->m() : 0); current->setM(o ? o->m() : 0); -#ifndef QV4_USE_64_BIT_VALUE_ENCODING - object->setTag(QV4::Value::Managed_Type); - current->setTag(QV4::Value::Managed_Type); -#endif - if (object->as()) { Scope scope(engine); Scoped (scope, object->asReturnedValue())->fullyCreate(); diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 1b0d6383e0..fd3bd1f660 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -79,11 +79,9 @@ Page *allocatePage(PersistentValueStorage *storage) if (p->header.next) p->header.next->header.prev = &p->header.next; for (int i = 0; i < kEntriesPerPage - 1; ++i) { - p->values[i].setTag(QV4::Value::Empty_Type); - p->values[i].setInt_32(i + 1); + p->values[i].setEmpty(i + 1); } - p->values[kEntriesPerPage - 1].setTag(QV4::Value::Empty_Type); - p->values[kEntriesPerPage - 1].setInt_32(-1); + p->values[kEntriesPerPage - 1].setEmpty(-1); storage->firstPage = p; @@ -205,8 +203,7 @@ void PersistentValueStorage::free(Value *v) Page *p = getPage(v); - v->setTag(QV4::Value::Empty_Type); - v->setInt_32(p->header.freeList); + v->setEmpty(p->header.freeList); p->header.freeList = v - p->values; if (!--p->header.refCount) freePage(p); diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index ca7efb1e79..0b063ee4b8 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -120,9 +120,6 @@ struct ScopedValue { ptr = scope.engine->jsStackTop++; ptr->setM(o); -#ifndef QV4_USE_64_BIT_VALUE_ENCODING - ptr->setTag(QV4::Value::Managed_Type); -#endif } ScopedValue(const Scope &scope, Managed *m) @@ -144,9 +141,6 @@ struct ScopedValue ScopedValue &operator=(Heap::Base *o) { ptr->setM(o); -#ifndef QV4_USE_64_BIT_VALUE_ENCODING - ptr->setTag(QV4::Value::Managed_Type); -#endif return *this; } @@ -186,18 +180,12 @@ struct Scoped inline void setPointer(const Managed *p) { ptr->setM(p ? p->m() : 0); -#ifndef QV4_USE_64_BIT_VALUE_ENCODING - ptr->setTag(QV4::Value::Managed_Type); -#endif } Scoped(const Scope &scope) { ptr = scope.engine->jsStackTop++; ptr->setM(0); -#ifndef QV4_USE_64_BIT_VALUE_ENCODING - ptr->setTag(QV4::Value::Managed_Type); -#endif } Scoped(const Scope &scope, const Value &v) @@ -339,14 +327,14 @@ struct ScopedCallData { inline Value &Value::operator =(const ScopedValue &v) { - _val = v.ptr->val(); + _val = v.ptr->rawValue(); return *this; } template inline Value &Value::operator=(const Scoped &t) { - _val = t.ptr->val(); + _val = t.ptr->rawValue(); return *this; } diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 6bd39355c5..7c2bb31a00 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -66,6 +66,7 @@ typedef uint Bool; struct Q_QML_PRIVATE_EXPORT Value { +private: /* We use two different ways of encoding JS values. One for 32bit and one for 64bit systems. @@ -90,10 +91,10 @@ struct Q_QML_PRIVATE_EXPORT Value quint64 _val; - Q_ALWAYS_INLINE quint64 val() const { return _val; } - Q_ALWAYS_INLINE void setVal(quint64 v) { _val = v; } - Q_ALWAYS_INLINE void setValue(quint32 v) { memcpy(&_val, &v, 4); } - Q_ALWAYS_INLINE void setTag(quint32 t) { memcpy(4 + (quint8 *)&_val, &t, 4); } +public: + Q_ALWAYS_INLINE quint64 &rawValueRef() { return _val; } + Q_ALWAYS_INLINE quint64 rawValue() const { return _val; } + Q_ALWAYS_INLINE void setRawValue(quint64 raw) { _val = raw; } #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN static inline int valueOffset() { return 0; } @@ -113,17 +114,53 @@ struct Q_QML_PRIVATE_EXPORT Value Q_ALWAYS_INLINE Heap::Base *m() const { Q_UNREACHABLE(); return Q_NULLPTR; } Q_ALWAYS_INLINE void setM(Heap::Base *b) { Q_UNUSED(b); Q_UNREACHABLE(); } #elif defined(QV4_USE_64_BIT_VALUE_ENCODING) - Q_ALWAYS_INLINE Heap::Base *m() const { Heap::Base *b; memcpy(&b, &_val, 8); return b; } - Q_ALWAYS_INLINE void setM(Heap::Base *b) { memcpy(&_val, &b, 8); } + Q_ALWAYS_INLINE Heap::Base *m() const + { + Heap::Base *b; + memcpy(&b, &_val, 8); + return b; + } + Q_ALWAYS_INLINE void setM(Heap::Base *b) + { + memcpy(&_val, &b, 8); + } #else // !QV4_USE_64_BIT_VALUE_ENCODING - Q_ALWAYS_INLINE Heap::Base *m() const { Q_STATIC_ASSERT(sizeof(Heap::Base*) == sizeof(quint32)); Heap::Base *b; quint32 v = value(); memcpy(&b, &v, 4); return b; } - Q_ALWAYS_INLINE void setM(Heap::Base *b) { quint32 v; memcpy(&v, &b, 4); setValue(v); } + Q_ALWAYS_INLINE Heap::Base *m() const + { + Q_STATIC_ASSERT(sizeof(Heap::Base*) == sizeof(quint32)); + Heap::Base *b; + quint32 v = value(); + memcpy(&b, &v, 4); + return b; + } + Q_ALWAYS_INLINE void setM(Heap::Base *b) + { + quint32 v; + memcpy(&v, &b, 4); + setTagValue(Managed_Type, v); + } #endif - Q_ALWAYS_INLINE int int_32() const { int i; quint32 v = value(); memcpy(&i, &v, 4); return i; } - Q_ALWAYS_INLINE void setInt_32(int i) { quint32 u; memcpy(&u, &i, 4); setValue(u); } + Q_ALWAYS_INLINE int int_32() const + { + return int(value()); + } + Q_ALWAYS_INLINE void setInt_32(int i) + { + setTagValue(Integer_Type_Internal, quint32(i)); + } Q_ALWAYS_INLINE uint uint_32() const { return value(); } + Q_ALWAYS_INLINE void setEmpty() + { + setTagValue(Empty_Type, value()); + } + + Q_ALWAYS_INLINE void setEmpty(int i) + { + setTagValue(Empty_Type, quint32(i)); + } + #ifndef QV4_USE_64_BIT_VALUE_ENCODING enum Masks { SilentNaNBit = 0x00040000, @@ -260,7 +297,6 @@ struct Q_QML_PRIVATE_EXPORT Value int i = (int)d; if (i == d) { setInt_32(i); - setTag(Integer_Type_Internal); return true; } } @@ -292,22 +328,10 @@ struct Q_QML_PRIVATE_EXPORT Value return m(); } - Q_ALWAYS_INLINE quint64 &rawValueRef() { - return _val; - } - Q_ALWAYS_INLINE quint64 rawValue() const { - return _val; - } - Q_ALWAYS_INLINE void setRawValue(quint64 raw) { _val = raw; } - static inline Value fromHeapObject(Heap::Base *m) { Value v; - v.setRawValue(0); v.setM(m); -#ifndef QV4_USE_64_BIT_VALUE_ENCODING - v.setTag(Managed_Type); -#endif return v; } @@ -328,7 +352,7 @@ struct Q_QML_PRIVATE_EXPORT Value inline bool tryIntegerConversion() { bool b = integerCompatible(); if (b) - setTag(Integer_Type_Internal); + setTagValue(Integer_Type_Internal, value()); return b; } @@ -378,7 +402,7 @@ struct Q_QML_PRIVATE_EXPORT Value Value &operator=(ReturnedValue v) { _val = v; return *this; } Value &operator=(Managed *m) { if (!m) { - setTagValue(Undefined_Type, 0); + setM(0); } else { _val = reinterpret_cast(m)->_val; } @@ -386,9 +410,6 @@ struct Q_QML_PRIVATE_EXPORT Value } Value &operator=(Heap::Base *o) { setM(o); -#ifndef QV4_USE_64_BIT_VALUE_ENCODING - setTag(Managed_Type); -#endif return *this; } @@ -479,13 +500,7 @@ struct Q_QML_PRIVATE_EXPORT Primitive : public Value inline Primitive Primitive::undefinedValue() { Primitive v; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - v.setRawValue(quint64(Undefined_Type) << Tag_Shift); -#else - v.setRawValue(0); - v.setTag(Undefined_Type); - v.setValue(0); -#endif + v.setTagValue(Undefined_Type, 0); return v; } @@ -499,11 +514,7 @@ inline Primitive Primitive::emptyValue() inline Primitive Primitive::nullValue() { Primitive v; -#ifndef QV4_USE_64_BIT_VALUE_ENCODING - v.setRawValue(quint64(Null_Type_Internal) << Tag_Shift); -#else v.setTagValue(Null_Type_Internal, 0); -#endif return v; } @@ -524,7 +535,7 @@ inline Primitive Primitive::fromDouble(double d) inline Primitive Primitive::fromInt32(int i) { Primitive v; - v.setTagValue(Integer_Type_Internal, 0); // For mingw482, because it complains, and for VS9, because of internal compiler errors. + v.setTagValue(Integer_Type_Internal, 0); v.setInt_32(i); return v; } @@ -533,8 +544,7 @@ inline Primitive Primitive::fromUInt32(uint i) { Primitive v; if (i < INT_MAX) { - v.setTagValue(Integer_Type_Internal, 0); // For mingw482, because it complains, and for VS9, because of internal compiler errors. - v.setInt_32((int)i); + v.setTagValue(Integer_Type_Internal, i); } else { v.setDouble(i); } -- cgit v1.2.3 From 458b4f0d37955b44c4f129541df2203cb8426296 Mon Sep 17 00:00:00 2001 From: Sze Howe Koh Date: Wed, 15 Jun 2016 08:37:45 +0800 Subject: Expand license scope from "Qt GUI Toolkit" to "Qt Toolkit" See http://comments.gmane.org/gmane.comp.lib.qt.devel/25771 Change-Id: I92dc562e5896b11bdeafb5d603c39cdc5053a3b2 Reviewed-by: Lars Knoll --- LICENSE.GPLv3 | 2 +- LICENSE.LGPLv21 | 2 +- LICENSE.LGPLv3 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE.GPLv3 b/LICENSE.GPLv3 index 4e49b122ae..71c4ad49c3 100644 --- a/LICENSE.GPLv3 +++ b/LICENSE.GPLv3 @@ -3,7 +3,7 @@ The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd. Contact: http://www.qt.io/licensing/ - You may use, distribute and copy the Qt GUI Toolkit under the terms of + You may use, distribute and copy the Qt Toolkit under the terms of GNU Lesser General Public License version 3. That license references the General Public License version 3, that is displayed below. Other portions of the Qt Toolkit may be licensed directly under this license. diff --git a/LICENSE.LGPLv21 b/LICENSE.LGPLv21 index 6e18461125..dfcab5e29b 100644 --- a/LICENSE.LGPLv21 +++ b/LICENSE.LGPLv21 @@ -3,7 +3,7 @@ The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd. Contact: http://www.qt.io/licensing/ - You may use, distribute and copy the Qt GUI Toolkit under the terms of + You may use, distribute and copy the Qt Toolkit under the terms of GNU Lesser General Public License version 2.1, which is displayed below. ------------------------------------------------------------------------- diff --git a/LICENSE.LGPLv3 b/LICENSE.LGPLv3 index 4d67bac0b4..6bf924cd15 100644 --- a/LICENSE.LGPLv3 +++ b/LICENSE.LGPLv3 @@ -3,7 +3,7 @@ The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd. Contact: http://www.qt.io/licensing/ - You may use, distribute and copy the Qt GUI Toolkit under the terms of + You may use, distribute and copy the Qt Toolkit under the terms of GNU Lesser General Public License version 3, which is displayed below. This license makes reference to the version 3 of the GNU General Public License, which you can find in the LICENSE.GPLv3 file. -- cgit v1.2.3 From 6358e1365539d2ea26ec20617b58c0fed11d647e Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Mon, 13 Jun 2016 16:52:22 -0500 Subject: Fix positioning of inline images in StyledText. Fix horizontal positioning generally, and also address RTL specifically. Change-Id: I8b75c11bc38e39da5cda302ee9b475bf0dc464e8 Task-number: QTBUG-54075 Reviewed-by: Andrew den Exter --- src/quick/items/qquicktext.cpp | 5 ++++- src/quick/util/qquickstyledtext.cpp | 15 ++++++++++----- src/quick/util/qquickstyledtext_p.h | 3 ++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index aa8f04e55d..e67e2cee9c 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -1138,7 +1138,10 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal foreach (QQuickStyledTextImgTag *image, imagesInLine) { totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height()); - image->pos.setX(line.cursorToX(image->position)); + const int leadX = line.cursorToX(image->position); + const int trailX = line.cursorToX(image->position, QTextLine::Trailing); + const bool rtl = trailX < leadX; + image->pos.setX(leadX + (rtl ? (-image->offset - image->size.width()) : image->offset)); image->pos.setY(image->pos.y() + height + textTop); extra->visibleImgTags << image; } diff --git a/src/quick/util/qquickstyledtext.cpp b/src/quick/util/qquickstyledtext.cpp index c411207121..6dd9ca882b 100644 --- a/src/quick/util/qquickstyledtext.cpp +++ b/src/quick/util/qquickstyledtext.cpp @@ -652,10 +652,13 @@ bool QQuickStyledTextPrivate::parseAnchorAttributes(const QChar *&ch, const QStr void QQuickStyledTextPrivate::parseImageAttributes(const QChar *&ch, const QString &textIn, QString &textOut) { qreal imgWidth = 0.0; + QFontMetricsF fm(layout.font()); + const qreal spaceWidth = fm.width(QChar::Nbsp); + const bool trailingSpace = textOut.endsWith(space); if (!updateImagePositions) { QQuickStyledTextImgTag *image = new QQuickStyledTextImgTag; - image->position = textOut.length() + 1; + image->position = textOut.length() + (trailingSpace ? 0 : 1); QPair attr; do { @@ -692,14 +695,16 @@ void QQuickStyledTextPrivate::parseImageAttributes(const QChar *&ch, const QStri } imgWidth = image->size.width(); + image->offset = -std::fmod(imgWidth, spaceWidth) / 2.0; imgTags->append(image); } else { // if we already have a list of img tags for this text // we only want to update the positions of these tags. QQuickStyledTextImgTag *image = imgTags->value(nbImages); - image->position = textOut.length() + 1; + image->position = textOut.length() + (trailingSpace ? 0 : 1); imgWidth = image->size.width(); + image->offset = -std::fmod(imgWidth, spaceWidth) / 2.0; QPair attr; do { attr = parseAttribute(ch, textIn); @@ -707,9 +712,9 @@ void QQuickStyledTextPrivate::parseImageAttributes(const QChar *&ch, const QStri nbImages++; } - QFontMetricsF fm(layout.font()); - QString padding(qFloor(imgWidth / fm.width(QChar::Nbsp)), QChar::Nbsp); - textOut += QLatin1Char(' '); + QString padding(qFloor(imgWidth / spaceWidth), QChar::Nbsp); + if (!trailingSpace) + textOut += QLatin1Char(' '); textOut += padding; textOut += QLatin1Char(' '); } diff --git a/src/quick/util/qquickstyledtext_p.h b/src/quick/util/qquickstyledtext_p.h index cd6710ff26..d9f1a71739 100644 --- a/src/quick/util/qquickstyledtext_p.h +++ b/src/quick/util/qquickstyledtext_p.h @@ -62,7 +62,7 @@ class Q_AUTOTEST_EXPORT QQuickStyledTextImgTag { public: QQuickStyledTextImgTag() - : position(0), align(QQuickStyledTextImgTag::Bottom), pix(0) + : position(0), offset(0.0), align(QQuickStyledTextImgTag::Bottom), pix(0) { } ~QQuickStyledTextImgTag() { delete pix; } @@ -77,6 +77,7 @@ public: QPointF pos; QSize size; int position; + qreal offset; // this offset allows us to compensate for flooring reserved space Align align; QQuickPixmap *pix; }; -- cgit v1.2.3 From c2c13cacd450d866e60c58c87ff1ab16ae736804 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 14 Jun 2016 11:11:18 +0200 Subject: Clean up property cache creation code * Reduce the complexity of the recursive tree traversal by moving the base type property cache creation into a helper function and using a context to encapsulate the origin of the current traversal * ensureVMEMetaObject() had only one call site and it's easiest to inline that for now. * Transition to a new state-less error handling, so that in the future this code can be used without the QQmlTypeCompiler dependency, which will be needed for loading of compilation units from disk. * A few missing consts. Change-Id: Ibe7209c357a3c7e101fac6960ece40a033e55f72 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder.cpp | 4 +- src/qml/compiler/qqmlirbuilder_p.h | 10 +- src/qml/compiler/qqmlpropertycachecreator.cpp | 206 ++++++++++++++------------ src/qml/compiler/qqmlpropertycachecreator_p.h | 16 +- src/qml/compiler/qqmltypecompiler_p.h | 13 ++ 5 files changed, 144 insertions(+), 105 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 3c987c76b9..ce879a874a 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1965,7 +1965,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int #ifndef V4_BOOTSTRAP -QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) +QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const { if (notInRevision) *notInRevision = false; @@ -1984,7 +1984,7 @@ QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRev } -QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) +QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) const { if (notInRevision) *notInRevision = false; diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index b7e3e883bc..8d671961c6 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -523,11 +523,11 @@ private: #ifndef V4_BOOTSTRAP struct Q_QML_EXPORT PropertyResolver { - PropertyResolver(QQmlPropertyCache *cache) + PropertyResolver(const QQmlPropertyCache *cache) : cache(cache) {} - QQmlPropertyData *property(int index) + QQmlPropertyData *property(int index) const { return cache->property(index); } @@ -537,12 +537,12 @@ struct Q_QML_EXPORT PropertyResolver IgnoreRevision }; - QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, RevisionCheck check = CheckRevision); + QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, RevisionCheck check = CheckRevision) const; // This code must match the semantics of QQmlPropertyPrivate::findSignalByName - QQmlPropertyData *signal(const QString &name, bool *notInRevision); + QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; - QQmlPropertyCache *cache; + const QQmlPropertyCache *cache; }; #endif diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index 1b886d5efb..d5b42752a4 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -41,16 +41,33 @@ #include -#define COMPILE_EXCEPTION(token, desc) \ - { \ - recordError((token)->location, desc); \ - return false; \ - } - QT_BEGIN_NAMESPACE static QAtomicInt classIndexCounter(0); +QQmlPropertyCacheCreator::InstantiationContext::InstantiationContext() + : referencingObjectIndex(-1) + , instantiatingBinding(nullptr) + , instantiatingProperty(nullptr) +{ + +} + +QQmlPropertyCacheCreator::InstantiationContext::InstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache) + : referencingObjectIndex(referencingObjectIndex) + , instantiatingBinding(instantiatingBinding) + , instantiatingProperty(nullptr) +{ + if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) { + Q_ASSERT(referencingObjectIndex >= 0); + Q_ASSERT(referencingObjectPropertyCache); + Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); + + bool notInRevision = false; + instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, ¬InRevision); + } +} + QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) , enginePrivate(typeCompiler->enginePrivate()) @@ -72,8 +89,13 @@ bool QQmlPropertyCacheCreator::buildMetaObjects() { propertyCaches.resize(qmlObjects.count()); - if (!buildMetaObjectRecursively(compiler->rootObjectIndex(), /*referencing object*/-1, /*instantiating binding*/0)) + InstantiationContext context; + + QQmlCompileError error = buildMetaObjectRecursively(compiler->rootObjectIndex(), context); + if (error.isSet()) { + recordError(error); return false; + } compiler->setPropertyCaches(propertyCaches); propertyCaches.clear(); @@ -81,80 +103,107 @@ bool QQmlPropertyCacheCreator::buildMetaObjects() return true; } -bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding) +QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, const InstantiationContext &context) { const QmlIR::Object *obj = qmlObjects.at(objectIndex); - QQmlPropertyCache *baseTypeCache = 0; - QQmlPropertyData *instantiatingProperty = 0; - if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) { - Q_ASSERT(referencingObjectIndex >= 0); - QQmlPropertyCache *parentCache = propertyCaches.at(referencingObjectIndex).data(); - Q_ASSERT(parentCache); - Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); - - bool notInRevision = false; - instantiatingProperty = QmlIR::PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), ¬InRevision); - if (instantiatingProperty) { - if (instantiatingProperty->isQObject()) { - baseTypeCache = enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType); - Q_ASSERT(baseTypeCache); - } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType)) { - baseTypeCache = enginePrivate->cache(vtmo); - Q_ASSERT(baseTypeCache); - } - } - } - bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; if (!needVMEMetaObject) { for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { - - // On assignments are implemented using value interceptors, which require a VME meta object. - needVMEMetaObject = true; - // If the on assignment is inside a group property, we need to distinguish between QObject based // group properties and value type group properties. For the former the base type is derived from // the property that references us, for the latter we only need a meta-object on the referencing object // because interceptors can't go to the shared value type instances. - if (instantiatingProperty && QQmlValueTypeFactory::isValueType(instantiatingProperty->propType)) { - needVMEMetaObject = false; - if (!ensureVMEMetaObject(referencingObjectIndex)) - return false; + if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType)) { + const bool willCreateVMEMetaObject = propertyCaches.at(context.referencingObjectIndex).flag(); + if (!willCreateVMEMetaObject) { + const QmlIR::Object *obj = qmlObjects.at(context.referencingObjectIndex); + auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); + if (error.isSet()) + return error; + } + } else { + // On assignments are implemented using value interceptors, which require a VME meta object. + needVMEMetaObject = true; } break; } } } - if (obj->inheritedTypeNameIndex != 0) { + QQmlPropertyCache *baseTypeCache; + { + QQmlCompileError error; + baseTypeCache = propertyCacheForObject(obj, context, &error); + if (error.isSet()) + return error; + } + + if (baseTypeCache) { + if (needVMEMetaObject) { + QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache); + if (error.isSet()) + return error; + } else { + if (QQmlPropertyCache *oldCache = propertyCaches.at(objectIndex).data()) + oldCache->release(); + propertyCaches[objectIndex] = baseTypeCache; + baseTypeCache->addref(); + } + } + + if (QQmlPropertyCache *thisCache = propertyCaches.at(objectIndex).data()) { + for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) + if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + InstantiationContext context(objectIndex, binding, stringAt(binding->propertyNameIndex), thisCache); + QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context); + if (error.isSet()) + return error; + } + } + + QQmlCompileError noError; + return noError; +} + +QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR::Object *obj, const QQmlPropertyCacheCreator::InstantiationContext &context, QQmlCompileError *error) const +{ + if (context.instantiatingProperty) { + if (context.instantiatingProperty->isQObject()) { + return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType); + } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType)) { + return enginePrivate->cache(vtmo); + } + } else if (obj->inheritedTypeNameIndex != 0) { auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); if (typeRef->isFullyDynamicType) { if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { - recordError(obj->location, tr("Fully dynamic types cannot declare new properties.")); - return false; + *error = QQmlCompileError(obj->location, tr("Fully dynamic types cannot declare new properties.")); + return nullptr; } if (obj->signalCount() > 0) { - recordError(obj->location, tr("Fully dynamic types cannot declare new signals.")); - return false; + *error = QQmlCompileError(obj->location, tr("Fully dynamic types cannot declare new signals.")); + return nullptr; } if (obj->functionCount() > 0) { - recordError(obj->location, tr("Fully Dynamic types cannot declare new functions.")); - return false; + *error = QQmlCompileError(obj->location, tr("Fully Dynamic types cannot declare new functions.")); + return nullptr; } } - baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - Q_ASSERT(baseTypeCache); - } else if (instantiatingBinding && instantiatingBinding->isAttachedProperty()) { - auto *typeRef = resolvedTypes->value(instantiatingBinding->propertyNameIndex); + return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { + auto *typeRef = resolvedTypes->value(context.instantiatingBinding->propertyNameIndex); Q_ASSERT(typeRef); QQmlType *qmltype = typeRef->type; if (!qmltype) { - QString propertyName = stringAt(instantiatingBinding->propertyNameIndex); + QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex); if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) { if (qmltype->isComposite()) { QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); @@ -171,49 +220,15 @@ bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int r const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; if (!attachedMo) { - recordError(instantiatingBinding->location, tr("Non-existent attached object")); - return false; - } - baseTypeCache = enginePrivate->cache(attachedMo); - Q_ASSERT(baseTypeCache); - } - - if (baseTypeCache) { - if (needVMEMetaObject) { - if (!createMetaObject(objectIndex, obj, baseTypeCache)) - return false; - } else { - if (QQmlPropertyCache *oldCache = propertyCaches.at(objectIndex).data()) - oldCache->release(); - propertyCaches[objectIndex] = baseTypeCache; - baseTypeCache->addref(); + *error = QQmlCompileError(context.instantiatingBinding->location, tr("Non-existent attached object")); + return nullptr; } + return enginePrivate->cache(attachedMo); } - - if (propertyCaches.at(objectIndex).data()) { - for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - if (!buildMetaObjectRecursively(binding->value.objectIndex, objectIndex, binding)) - return false; - } - } - - return true; + return nullptr; } -bool QQmlPropertyCacheCreator::ensureVMEMetaObject(int objectIndex) -{ - const bool willCreateVMEMetaObject = propertyCaches.at(objectIndex).flag(); - if (willCreateVMEMetaObject) - return true; - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - return createMetaObject(objectIndex, obj, baseTypeCache); -} - -bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache) +QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache) { QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), @@ -283,14 +298,14 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob bool notInRevision = false; QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); if (d && d->isFinal()) - COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); + return QQmlCompileError(p->location, tr("Cannot override FINAL property")); } for (const QmlIR::Alias *a = obj->firstAlias(); a; a = a->next) { bool notInRevision = false; QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); if (d && d->isFinal()) - COMPILE_EXCEPTION(a, tr("Cannot override FINAL property")); + return QQmlCompileError(a->location, tr("Cannot override FINAL property")); } int effectivePropertyIndex = cache->propertyIndexCacheStart; @@ -363,7 +378,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob const QString customTypeName = stringAt(param->customTypeNameIndex); QQmlType *qmltype = 0; if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) - COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(customTypeName)); + return QQmlCompileError(s->location, tr("Invalid signal parameter type: %1").arg(customTypeName)); if (qmltype->isComposite()) { QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); @@ -389,7 +404,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob QString signalName = stringAt(s->nameIndex); if (seenSignals.contains(signalName)) - COMPILE_EXCEPTION(s, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); + return QQmlCompileError(s->location, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); seenSignals.insert(signalName); cache->appendSignal(signalName, flags, effectiveMethodIndex++, @@ -408,7 +423,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob QString slotName = astFunction->name.toString(); if (seenSignals.contains(slotName)) - COMPILE_EXCEPTION(s, tr("Duplicate method name: invalid override of property change signal or superclass signal")); + return QQmlCompileError(s->location, tr("Duplicate method name: invalid override of property change signal or superclass signal")); // Note: we don't append slotName to the seenSignals list, since we don't // protect against overriding change signals or methods with properties. @@ -444,7 +459,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob QQmlType *qmltype = 0; if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { - COMPILE_EXCEPTION(p, tr("Invalid property type")); + return QQmlCompileError(p->location, tr("Invalid property type")); } Q_ASSERT(qmltype); @@ -489,7 +504,8 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob effectiveSignalIndex++; } - return true; + QQmlCompileError noError; + return noError; } QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 1a3a96e4e3..5f49e6d973 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -63,9 +63,19 @@ public: bool buildMetaObjects(); protected: - bool buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding); - bool ensureVMEMetaObject(int objectIndex); - bool createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache); + + struct InstantiationContext { + InstantiationContext(); + InstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache); + int referencingObjectIndex; + const QV4::CompiledData::Binding *instantiatingBinding; + QQmlPropertyData *instantiatingProperty; + }; + + QQmlCompileError buildMetaObjectRecursively(int objectIndex, const InstantiationContext &context); + QQmlPropertyCache *propertyCacheForObject(const QmlIR::Object *obj, const InstantiationContext &context, QQmlCompileError *error) const; + QQmlCompileError createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache); + QQmlEnginePrivate *enginePrivate; const QList &qmlObjects; diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index c67137bcbd..5e8bf4538e 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -126,6 +126,17 @@ private: QQmlPropertyCacheVector m_propertyCaches; }; +struct QQmlCompileError +{ + QQmlCompileError() {} + QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description) + : location(location), description(description) {} + QV4::CompiledData::Location location; + QString description; + + bool isSet() const { return !description.isEmpty(); } +}; + struct QQmlCompilePass { virtual ~QQmlCompilePass() {} @@ -135,6 +146,8 @@ struct QQmlCompilePass QString stringAt(int idx) const { return compiler->stringAt(idx); } protected: void recordError(const QV4::CompiledData::Location &location, const QString &description) const; + void recordError(const QQmlCompileError &error) + { recordError(error.location, error.description); } QQmlTypeCompiler *compiler; }; -- cgit v1.2.3 From 0058ac5f28c64979f359972e03ee91ae146b0cd3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 14 Jun 2016 15:44:09 +0200 Subject: Clean up property cache array handling The QQmlPropertyCacheVector in the CompilationUnit encapsulates the property caches for the objects declared in the QML tree as well as the bits indicating whether a VME meta-object is needed. The ref-counting for the caches in that vector was done "manually" and thus error prone. This patch replaces the vector with a wrapper container that has explicit move semantics and takes care of the addref() and release() calls upon insertion, replacement and destruction. Change-Id: If805fe016f1a1c70e56f8a90909ab87b653ea026 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlpropertycachecreator.cpp | 33 +++++--------- src/qml/compiler/qqmltypecompiler.cpp | 63 ++++++++++++++------------- src/qml/compiler/qqmltypecompiler_p.h | 23 +++++----- src/qml/compiler/qv4compileddata.cpp | 5 +-- src/qml/compiler/qv4compileddata_p.h | 7 +-- src/qml/qml/qqmlobjectcreator.cpp | 11 +++-- src/qml/qml/qqmlobjectcreator_p.h | 2 +- src/qml/qml/qqmlpropertycache_p.h | 43 ++++++++++++++++++ 8 files changed, 107 insertions(+), 80 deletions(-) diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index d5b42752a4..5e696652bd 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -79,10 +79,6 @@ QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompile QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator() { - for (int i = 0; i < propertyCaches.count(); ++i) - if (QQmlPropertyCache *cache = propertyCaches.at(i).data()) - cache->release(); - propertyCaches.clear(); } bool QQmlPropertyCacheCreator::buildMetaObjects() @@ -97,8 +93,7 @@ bool QQmlPropertyCacheCreator::buildMetaObjects() return false; } - compiler->setPropertyCaches(propertyCaches); - propertyCaches.clear(); + compiler->setPropertyCaches(std::move(propertyCaches)); return true; } @@ -116,8 +111,7 @@ QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int object // the property that references us, for the latter we only need a meta-object on the referencing object // because interceptors can't go to the shared value type instances. if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType)) { - const bool willCreateVMEMetaObject = propertyCaches.at(context.referencingObjectIndex).flag(); - if (!willCreateVMEMetaObject) { + if (!propertyCaches.needsVMEMetaObject(context.referencingObjectIndex)) { const QmlIR::Object *obj = qmlObjects.at(context.referencingObjectIndex); auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); @@ -149,14 +143,11 @@ QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int object if (error.isSet()) return error; } else { - if (QQmlPropertyCache *oldCache = propertyCaches.at(objectIndex).data()) - oldCache->release(); - propertyCaches[objectIndex] = baseTypeCache; - baseTypeCache->addref(); + propertyCaches.set(objectIndex, baseTypeCache); } } - if (QQmlPropertyCache *thisCache = propertyCaches.at(objectIndex).data()) { + if (QQmlPropertyCache *thisCache = propertyCaches.at(objectIndex)) { for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) if (binding->type >= QV4::CompiledData::Binding::Type_Object) { InstantiationContext context(objectIndex, binding, stringAt(binding->propertyNameIndex), thisCache); @@ -230,15 +221,13 @@ QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR: QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache) { - QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), - obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), - obj->signalCount() + obj->propertyCount() + obj->aliasCount()); - - if (QQmlPropertyCache *oldCache = propertyCaches.at(objectIndex).data()) - oldCache->release(); - propertyCaches[objectIndex] = cache; - // Indicate that this object also needs a VME meta-object at run-time - propertyCaches[objectIndex].setFlag(); + QQmlRefPointer cache; + cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), + obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), + obj->signalCount() + obj->propertyCount() + obj->aliasCount())); + + propertyCaches.set(objectIndex, cache); + propertyCaches.setNeedsVMEMetaObject(objectIndex); struct TypeData { QV4::CompiledData::Property::Type dtype; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index e48b3eb449..3e7fde871c 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -241,13 +241,14 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit; compilationUnit = document->javaScriptCompilationUnit; - compilationUnit->propertyCaches = m_propertyCaches; compilationUnit->importCache = importCache; compilationUnit->dependentScripts = dependentScripts; compilationUnit->resolvedTypes = m_resolvedTypes; + compilationUnit->propertyCaches = std::move(m_propertyCaches); + Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast(compilationUnit->data->nObjects)); // Add to type registry of composites - if (compilationUnit->isCompositeType()) + if (compilationUnit->propertyCaches.needsVMEMetaObject(qmlUnit->indexOfRootObject)) engine->registerInternalCompositeType(compilationUnit); else { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject); @@ -263,7 +264,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() } // Sanity check property bindings - QQmlPropertyValidator validator(this); + QQmlPropertyValidator validator(this, &compilationUnit->propertyCaches); if (!validator.validate()) return nullptr; @@ -292,8 +293,6 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() compilationUnit->totalParserStatusCount = parserStatusCount; compilationUnit->totalObjectCount = objectCount; - Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast(compilationUnit->data->nObjects)); - if (errors.isEmpty()) return compilationUnit; else @@ -347,15 +346,20 @@ int QQmlTypeCompiler::rootObjectIndex() const return document->indexOfRootObject; } -void QQmlTypeCompiler::setPropertyCaches(const QQmlPropertyCacheVector &caches) +void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches) +{ + m_propertyCaches = std::move(caches); + Q_ASSERT(m_propertyCaches.count() >= document->indexOfRootObject); +} + +const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const { - m_propertyCaches = caches; - Q_ASSERT(caches.count() >= document->indexOfRootObject); + return &m_propertyCaches; } -const QQmlPropertyCacheVector &QQmlTypeCompiler::propertyCaches() const +QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches() { - return m_propertyCaches; + return std::move(m_propertyCaches); } QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool() @@ -413,7 +417,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio { for (int objectIndex = 0; objectIndex < qmlObjects.count(); ++objectIndex) { const QmlIR::Object * const obj = qmlObjects.at(objectIndex); - QQmlPropertyCache *cache = propertyCaches.at(objectIndex).data(); + QQmlPropertyCache *cache = propertyCaches->at(objectIndex); if (!cache) continue; if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) { @@ -621,7 +625,7 @@ QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler) bool QQmlEnumTypeResolver::resolveEnumBindings() { for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches.at(i).data(); + QQmlPropertyCache *propertyCache = propertyCaches->at(i); if (!propertyCache) continue; const QmlIR::Object *obj = qmlObjects.at(i); @@ -807,7 +811,7 @@ QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler) void QQmlAliasAnnotator::annotateBindingsToAliases() { for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches.at(i).data(); + QQmlPropertyCache *propertyCache = propertyCaches->at(i); if (!propertyCache) continue; @@ -839,7 +843,7 @@ void QQmlScriptStringScanner::scan() { const int scriptStringMetaType = qMetaTypeId(); for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches.at(i).data(); + QQmlPropertyCache *propertyCache = propertyCaches->at(i); if (!propertyCache) continue; @@ -874,7 +878,7 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t , indexOfRootObject(typeCompiler->rootObjectIndex()) , _componentIndex(-1) , resolvedTypes(typeCompiler->resolvedTypes()) - , propertyCaches(typeCompiler->propertyCaches()) + , propertyCaches(std::move(typeCompiler->takePropertyCaches())) { } @@ -942,7 +946,6 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI const int componentIndex = qmlObjects->count() - 1; // Keep property caches symmetric QQmlPropertyCache *componentCache = enginePrivate->cache(&QQmlComponent::staticMetaObject); - componentCache->addref(); propertyCaches.append(componentCache); QmlIR::Binding *syntheticBinding = pool->New(); @@ -967,7 +970,7 @@ bool QQmlComponentAndAliasResolver::resolve() const int objCountWithoutSynthesizedComponents = qmlObjects->count(); for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) { QmlIR::Object *obj = qmlObjects->at(i); - QQmlPropertyCache *cache = propertyCaches.at(i).data(); + QQmlPropertyCache *cache = propertyCaches.at(i); if (obj->inheritedTypeNameIndex == 0 && !cache) continue; @@ -1046,7 +1049,7 @@ bool QQmlComponentAndAliasResolver::resolve() // Implicit component insertion may have added objects and thus we also need // to extend the symmetric propertyCaches. - compiler->setPropertyCaches(propertyCaches); + compiler->setPropertyCaches(std::move(propertyCaches)); compiler->setComponentRoots(componentRoots); return true; @@ -1090,7 +1093,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases() foreach (int objectIndex, _objectsWithAliases) { const QmlIR::Object *obj = qmlObjects->at(objectIndex); - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex).data(); + QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); Q_ASSERT(propertyCache); int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); @@ -1142,7 +1145,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases() alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; propertyFlags |= QQmlPropertyData::IsQObjectDerived; } else { - QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex).data(); + QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); Q_ASSERT(targetCache); QmlIR::PropertyResolver resolver(targetCache); @@ -1247,7 +1250,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) return scanObject(componentBinding->value.objectIndex); } - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex).data(); + QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); if (!propertyCache) return true; @@ -1340,13 +1343,13 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) } -QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler) +QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, const QQmlPropertyCacheVector *propertyCaches) : QQmlCompilePass(typeCompiler) , enginePrivate(typeCompiler->enginePrivate()) , qmlUnit(typeCompiler->qmlUnit()) , resolvedTypes(*typeCompiler->resolvedTypes()) , customParsers(typeCompiler->customParserCache()) - , propertyCaches(typeCompiler->propertyCaches()) + , propertyCaches(propertyCaches) { } @@ -1393,7 +1396,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD return validateObject(componentBinding->value.objectIndex, componentBinding); } - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex).data(); + QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); if (!propertyCache) return true; @@ -1962,7 +1965,7 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co } else if (property->isQList()) { const int listType = enginePrivate->listType(property->propType); if (!QQmlMetaType::isInterface(listType)) { - QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex).data(); + QQmlPropertyCache *source = propertyCaches->at(binding->value.objectIndex); if (!canCoerce(listType, source)) { recordError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); return false; @@ -1989,7 +1992,7 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co bool isAssignable = false; // Determine isAssignable value if (propertyMetaObject) { - QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex).data(); + QQmlPropertyCache *c = propertyCaches->at(binding->value.objectIndex); while (c && !isAssignable) { isAssignable |= c == propertyMetaObject; c = c->parent(); @@ -2043,7 +2046,7 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject) const QmlIR::Object *obj = qmlObjects.at(objectIndex); m.name = stringAt(obj->idNameIndex); m.idIndex = obj->id; - m.type = propertyCaches.at(objectIndex).data(); + m.type = propertyCaches->at(objectIndex); auto *tref = resolvedTypes.value(obj->inheritedTypeNameIndex); if (tref && tref->isFullyDynamicType) @@ -2051,7 +2054,7 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject) idMapping << m; } - v4CodeGen->beginContextScope(idMapping, propertyCaches.at(contextObject).data()); + v4CodeGen->beginContextScope(idMapping, propertyCaches->at(contextObject)); if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject)) return false; @@ -2066,7 +2069,7 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn return true; if (object->functionsAndExpressions->count > 0) { - QQmlPropertyCache *scopeObject = propertyCaches.at(scopeObjectIndex).data(); + QQmlPropertyCache *scopeObject = propertyCaches->at(scopeObjectIndex); v4CodeGen->beginObjectScope(scopeObject); QList functionsToCompile; @@ -2118,7 +2121,7 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties() void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex) { - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex).data(); + QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); if (!propertyCache) return; diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 5e8bf4538e..07ad65d473 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -98,8 +98,9 @@ public: QHash *resolvedTypes(); QList *qmlObjects(); int rootObjectIndex() const; - void setPropertyCaches(const QQmlPropertyCacheVector &caches); - const QQmlPropertyCacheVector &propertyCaches() const; + void setPropertyCaches(QQmlPropertyCacheVector &&caches); + const QQmlPropertyCacheVector *propertyCaches() const; + QQmlPropertyCacheVector &&takePropertyCaches(); void setComponentRoots(const QVector &roots) { m_componentRoots = roots; } const QVector &componentRoots() const { return m_componentRoots; } QQmlJS::MemoryPool *memoryPool(); @@ -173,7 +174,7 @@ private: const QHash &customParsers; const QHash &resolvedTypes; const QSet &illegalNames; - const QQmlPropertyCacheVector &propertyCaches; + const QQmlPropertyCacheVector * const propertyCaches; }; // ### This will go away when the codegen resolves all enums to constant expressions @@ -200,7 +201,7 @@ private: const QList &qmlObjects; - const QQmlPropertyCacheVector propertyCaches; + const QQmlPropertyCacheVector * const propertyCaches; const QQmlImports *imports; QHash *resolvedTypes; }; @@ -228,7 +229,7 @@ public: void annotateBindingsToAliases(); private: const QList &qmlObjects; - const QQmlPropertyCacheVector propertyCaches; + const QQmlPropertyCacheVector * const propertyCaches; }; class QQmlScriptStringScanner : public QQmlCompilePass @@ -240,7 +241,7 @@ public: private: const QList &qmlObjects; - const QQmlPropertyCacheVector propertyCaches; + const QQmlPropertyCacheVector * const propertyCaches; }; class QQmlComponentAndAliasResolver : public QQmlCompilePass @@ -284,7 +285,7 @@ private: bool scanObject(int objectIndex); QList *qmlObjects; - QQmlPropertyCacheVector propertyCaches; + const QQmlPropertyCacheVector * const propertyCaches; const QHash &customParsers; bool _seenObjectWithId; @@ -294,7 +295,7 @@ class QQmlPropertyValidator : public QQmlCompilePass { Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) public: - QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler); + QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, const QQmlPropertyCacheVector *propertyCaches); bool validate(); @@ -312,7 +313,7 @@ private: const QV4::CompiledData::Unit *qmlUnit; const QHash &resolvedTypes; const QHash &customParsers; - const QQmlPropertyCacheVector &propertyCaches; + const QQmlPropertyCacheVector * const propertyCaches; // collected state variables, essentially write-only mutable QVector _bindingPropertyDataPerObject; @@ -333,7 +334,7 @@ private: const QHash &resolvedTypes; const QHash &customParsers; const QList &qmlObjects; - const QQmlPropertyCacheVector &propertyCaches; + const QQmlPropertyCacheVector * const propertyCaches; QmlIR::JSCodeGen * const v4CodeGen; }; @@ -348,7 +349,7 @@ private: void mergeDefaultProperties(int objectIndex); const QList &qmlObjects; - const QQmlPropertyCacheVector &propertyCaches; + const QQmlPropertyCacheVector * const propertyCaches; }; class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index df29930aad..db25e265d0 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -170,15 +170,12 @@ void CompilationUnit::unlink() engine->compilationUnits.erase(engine->compilationUnits.find(this)); if (isRegisteredWithEngine) { - Q_ASSERT(data && quint32(propertyCaches.count()) > data->indexOfRootObject && !propertyCaches.at(data->indexOfRootObject).isNull()); + Q_ASSERT(data && quint32(propertyCaches.count()) > data->indexOfRootObject && propertyCaches.at(data->indexOfRootObject)); QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(propertyCaches.at(data->indexOfRootObject)->engine); qmlEngine->unregisterInternalCompositeType(this); isRegisteredWithEngine = false; } - for (int ii = 0; ii < propertyCaches.count(); ++ii) - if (propertyCaches.at(ii).data()) - propertyCaches.at(ii)->release(); propertyCaches.clear(); for (int ii = 0; ii < dependentScripts.count(); ++ii) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 760f6b6737..b24d93d995 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -76,10 +76,6 @@ class QQmlScriptData; class QQmlType; class QQmlEngine; -// The vector is indexed by QV4::CompiledData::Object index and the flag -// indicates whether instantiation of the object requires a VME meta-object. -typedef QVector> QQmlPropertyCacheVector; - namespace QmlIR { struct Document; } @@ -706,8 +702,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount // QML specific fields QQmlPropertyCacheVector propertyCaches; - QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject).data(); } - bool isCompositeType() const { return propertyCaches.at(data->indexOfRootObject).flag(); } + QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject); } QQmlRefPointer importCache; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 9e2040469b..6a54555f3f 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -73,7 +73,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::Compil : phase(Startup) , compilationUnit(compilationUnit) , resolvedTypes(compilationUnit->resolvedTypes) - , propertyCaches(compilationUnit->propertyCaches) + , propertyCaches(&compilationUnit->propertyCaches) , activeVMEDataForRootContext(activeVMEDataForRootContext) { init(parentContext); @@ -97,7 +97,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::Compil : phase(Startup) , compilationUnit(compilationUnit) , resolvedTypes(compilationUnit->resolvedTypes) - , propertyCaches(compilationUnit->propertyCaches) + , propertyCaches(&compilationUnit->propertyCaches) , activeVMEDataForRootContext(0) { init(parentContext); @@ -1147,7 +1147,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo return instance; } - QQmlRefPointer cache = propertyCaches.at(index).data(); + QQmlRefPointer cache = propertyCaches->at(index); Q_ASSERT(!cache.isNull()); if (installPropertyCache) { if (ddata->propertyCache) @@ -1278,11 +1278,10 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * QV4::Scope valueScope(v4); QV4::ScopedValue scopeObjectProtector(valueScope); - QQmlRefPointer cache = propertyCaches.at(_compiledObjectIndex).data(); + QQmlRefPointer cache = propertyCaches->at(_compiledObjectIndex); QQmlVMEMetaObject *vmeMetaObject = 0; - const bool needVMEMetaObject = propertyCaches.at(_compiledObjectIndex).flag(); - if (needVMEMetaObject) { + if (propertyCaches->needsVMEMetaObject(_compiledObjectIndex)) { Q_ASSERT(!cache.isNull()); // install on _object vmeMetaObject = new QQmlVMEMetaObject(_qobject, cache, compilationUnit, _compiledObjectIndex); diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index b4e706941b..2320edf809 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -140,7 +140,7 @@ private: QQmlGuardedContextData parentContext; QQmlContextData *context; const QHash &resolvedTypes; - const QQmlPropertyCacheVector &propertyCaches; + const QQmlPropertyCacheVector *propertyCaches; QExplicitlySharedDataPointer sharedState; bool topLevelCreator; void *activeVMEDataForRootContext; diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 3e84fb3070..34b6b96ebe 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -655,6 +655,49 @@ const QMetaObject *QQmlMetaObject::metaObject() const else return _m.asT2(); } +class QQmlPropertyCacheVector +{ +public: + QQmlPropertyCacheVector() {} + QQmlPropertyCacheVector(QQmlPropertyCacheVector &&other) + : data(std::move(other.data)) {} + QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&other) { + QVector> moved(std::move(other.data)); + data.swap(moved); + return *this; + } + + ~QQmlPropertyCacheVector() { clear(); } + void resize(int size) { return data.resize(size); } + int count() const { return data.count(); } + void clear() + { + for (int i = 0; i < data.count(); ++i) { + if (QQmlPropertyCache *cache = data.at(i).data()) + cache->release(); + } + data.clear(); + } + + void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); } + QQmlPropertyCache *at(int index) const { return data.at(index).data(); } + void set(int index, QQmlPropertyCache *replacement) { + if (QQmlPropertyCache *oldCache = data.at(index).data()) { + if (replacement == oldCache) + return; + oldCache->release(); + } + data[index] = replacement; + replacement->addref(); + } + + void setNeedsVMEMetaObject(int index) { data[index].setFlag(); } + bool needsVMEMetaObject(int index) const { return data.at(index).flag(); } +private: + Q_DISABLE_COPY(QQmlPropertyCacheVector) + QVector> data; +}; + QT_END_NAMESPACE #endif // QQMLPROPERTYCACHE_P_H -- cgit v1.2.3 From 12d2b60218542e900076aebfce73a229a8b29ae1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 10:12:07 +0200 Subject: Small data structure cleanup Changed the QList for IR object storage to use a QVector instead, to secure the guarantee of contiguous storage in memory. This will later be useful for direct indexing. Change-Id: I3c0a5f2c346627c8436971ec1d69160865eeb22e Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder_p.h | 4 ++-- src/qml/compiler/qqmlpropertycachecreator_p.h | 2 +- src/qml/compiler/qqmltypecompiler.cpp | 2 +- src/qml/compiler/qqmltypecompiler_p.h | 22 +++++++++++----------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 8d671961c6..2409050f7d 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -397,7 +397,7 @@ struct Q_QML_PRIVATE_EXPORT Document QList pragmas; QQmlJS::AST::UiProgram *program; int indexOfRootObject; - QList objects; + QVector objects; QV4::Compiler::JSUnitGenerator jsGenerator; quint32 unitFlags; @@ -499,7 +499,7 @@ public: QList _imports; QList _pragmas; - QList _objects; + QVector _objects; QV4::CompiledData::TypeReferenceMap _typeReferences; diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 5f49e6d973..126bdbb017 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -78,7 +78,7 @@ protected: QQmlEnginePrivate *enginePrivate; - const QList &qmlObjects; + const QVector &qmlObjects; const QQmlImports *imports; QHash *resolvedTypes; QQmlPropertyCacheVector propertyCaches; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 3e7fde871c..c75f951adb 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -336,7 +336,7 @@ QHash *QQmlType return &m_resolvedTypes; } -QList *QQmlTypeCompiler::qmlObjects() +QVector *QQmlTypeCompiler::qmlObjects() { return &document->objects; } diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 07ad65d473..1e01f72228 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -96,7 +96,7 @@ public: QQmlEnginePrivate *enginePrivate() const { return engine; } const QQmlImports *imports() const; QHash *resolvedTypes(); - QList *qmlObjects(); + QVector *qmlObjects(); int rootObjectIndex() const; void setPropertyCaches(QQmlPropertyCacheVector &&caches); const QQmlPropertyCacheVector *propertyCaches() const; @@ -169,7 +169,7 @@ private: bool convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache); QQmlEnginePrivate *enginePrivate; - const QList &qmlObjects; + const QVector &qmlObjects; const QQmlImports *imports; const QHash &customParsers; const QHash &resolvedTypes; @@ -200,7 +200,7 @@ private: int evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const; - const QList &qmlObjects; + const QVector &qmlObjects; const QQmlPropertyCacheVector * const propertyCaches; const QQmlImports *imports; QHash *resolvedTypes; @@ -216,7 +216,7 @@ public: private: void scanObjectRecursively(int objectIndex, bool annotateScriptBindings = false); - const QList &qmlObjects; + const QVector &qmlObjects; const QHash &customParsers; }; @@ -228,7 +228,7 @@ public: void annotateBindingsToAliases(); private: - const QList &qmlObjects; + const QVector &qmlObjects; const QQmlPropertyCacheVector * const propertyCaches; }; @@ -240,7 +240,7 @@ public: void scan(); private: - const QList &qmlObjects; + const QVector &qmlObjects; const QQmlPropertyCacheVector * const propertyCaches; }; @@ -260,7 +260,7 @@ protected: QQmlEnginePrivate *enginePrivate; QQmlJS::MemoryPool *pool; - QList *qmlObjects; + QVector *qmlObjects; const int indexOfRootObject; // indices of the objects that are actually Component {} @@ -284,7 +284,7 @@ public: private: bool scanObject(int objectIndex); - QList *qmlObjects; + QVector *qmlObjects; const QQmlPropertyCacheVector * const propertyCaches; const QHash &customParsers; @@ -333,7 +333,7 @@ private: const QHash &resolvedTypes; const QHash &customParsers; - const QList &qmlObjects; + const QVector &qmlObjects; const QQmlPropertyCacheVector * const propertyCaches; QmlIR::JSCodeGen * const v4CodeGen; }; @@ -348,7 +348,7 @@ public: private: void mergeDefaultProperties(int objectIndex); - const QList &qmlObjects; + const QVector &qmlObjects; const QQmlPropertyCacheVector * const propertyCaches; }; @@ -394,7 +394,7 @@ private: bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding); bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding); - const QList &qmlObjects; + const QVector &qmlObjects; QV4::IR::Module *jsModule; bool _canSimplify; -- cgit v1.2.3 From f2c97b4e307a62b831f68a0278551d4375170336 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 13:27:20 +0200 Subject: PropertyCacheCreator traversal cleanup Replace the direct linked list object traversal with iterators. This will allow for re-use of the code against the QV4::CompiledData structures when they get the same interface. Change-Id: I901fd3377ef0f0317e5d9278cface37d80f93abf Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder.cpp | 10 +++++++ src/qml/compiler/qqmlirbuilder_p.h | 34 ++++++++++++++++++++++ src/qml/compiler/qqmlpropertycachecreator.cpp | 42 ++++++++++++--------------- src/qml/compiler/qqmltypecompiler_p.h | 7 ++++- 4 files changed, 68 insertions(+), 25 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index ce879a874a..4fa63e3c84 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -911,6 +911,16 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) f->location.column = loc.startColumn; f->index = index; f->nameIndex = registerString(funDecl->name.toString()); + + int formalsCount = 0; + for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next) + ++formalsCount; + f->formals.allocate(pool, formalsCount); + + int i = 0; + for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next, ++i) + f->formals[i] = registerString(it->name.toString()); + _object->appendFunction(f); } else { recordError(node->firstSourceLocation(), QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element")); diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 2409050f7d..a29727fc9e 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -175,6 +175,14 @@ struct PoolList return ptr; } + T &operator*() { + return *ptr; + } + + const T &operator*() const { + return *ptr; + } + void operator++() { ptr = ptr->next; } @@ -204,6 +212,12 @@ public: , count(0) {} + void allocate(QQmlJS::MemoryPool *pool, int size) + { + count = size; + data = reinterpret_cast(pool->allocate(count * sizeof(T))); + } + void allocate(QQmlJS::MemoryPool *pool, const QVector &vector) { count = vector.count(); @@ -244,6 +258,9 @@ public: return i; return -1; } + + const T *begin() const { return data; } + const T *end() const { return data + count; } }; struct Object; @@ -261,6 +278,10 @@ struct Signal QStringList parameterStringList(const QV4::Compiler::StringTableGenerator *stringPool) const; + int parameterCount() const { return parameters->count; } + PoolList::Iterator parametersBegin() const { return parameters->begin(); } + PoolList::Iterator parametersEnd() const { return parameters->end(); } + Signal *next; }; @@ -290,6 +311,13 @@ struct Function QV4::CompiledData::Location location; int nameIndex; quint32 index; // index in parsedQML::functions + FixedPoolArray formals; + + // --- QQmlPropertyCacheCreator interface + const int *formalsBegin() const { return formals.begin(); } + const int *formalsEnd() const { return formals.end(); } + // --- + Function *next; }; @@ -342,6 +370,12 @@ public: PoolList::Iterator bindingsEnd() const { return bindings->end(); } PoolList::Iterator propertiesBegin() const { return properties->begin(); } PoolList::Iterator propertiesEnd() const { return properties->end(); } + PoolList::Iterator aliasesBegin() const { return aliases->begin(); } + PoolList::Iterator aliasesEnd() const { return aliases->end(); } + PoolList::Iterator signalsBegin() const { return qmlSignals->begin(); } + PoolList::Iterator signalsEnd() const { return qmlSignals->end(); } + PoolList::Iterator functionsBegin() const { return functions->begin(); } + PoolList::Iterator functionsEnd() const { return functions->end(); } // If set, then declarations for this object (and init bindings for these) should go into the // specified object. Used for declarations inside group properties. diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index 5e696652bd..a0bcba01ce 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -104,7 +104,7 @@ QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int object bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; if (!needVMEMetaObject) { - for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) { if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { // If the on assignment is inside a group property, we need to distinguish between QObject based // group properties and value type group properties. For the former the base type is derived from @@ -148,9 +148,9 @@ QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int object } if (QQmlPropertyCache *thisCache = propertyCaches.at(objectIndex)) { - for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) + for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - InstantiationContext context(objectIndex, binding, stringAt(binding->propertyNameIndex), thisCache); + InstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context); if (error.isSet()) return error; @@ -280,7 +280,7 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con QmlIR::PropertyResolver resolver(baseTypeCache); - for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) { + for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) { if (p->type == QV4::CompiledData::Property::Var) varPropCount++; @@ -290,7 +290,7 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con return QQmlCompileError(p->location, tr("Cannot override FINAL property")); } - for (const QmlIR::Alias *a = obj->firstAlias(); a; a = a->next) { + for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) { bool notInRevision = false; QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); if (d && d->isFinal()) @@ -324,7 +324,7 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con } // Set up notify signals for properties - first normal, then alias - for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) { + for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) { quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMESignal; @@ -334,7 +334,7 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); } - for (const QmlIR::Alias *a = obj->firstAlias(); a; a = a->next) { + for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) { quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMESignal; @@ -345,8 +345,8 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con } // Dynamic signals - for (const QmlIR::Signal *s = obj->firstSignal(); s; s = s->next) { - const int paramCount = s->parameters->count; + for (auto s = obj->signalsBegin(), end = obj->signalsEnd(); s != end; ++s) { + const int paramCount = s->parameterCount(); QList names; names.reserve(paramCount); @@ -355,8 +355,8 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con if (paramCount) { paramTypes[0] = paramCount; - QmlIR::SignalParameter *param = s->parameters->first; - for (int i = 0; i < paramCount; ++i, param = param->next) { + int i = 0; + for (auto param = s->parametersBegin(), end = s->parametersEnd(); param != end; ++param, ++i) { names.append(stringAt(param->nameIndex).toUtf8()); if (param->type < builtinTypeCount) { // built-in type @@ -402,25 +402,19 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con // Dynamic slots - for (const QmlIR::Function *s = obj->firstFunction(); s; s = s->next) { - QQmlJS::AST::FunctionDeclaration *astFunction = s->functionDeclaration; - + for (auto function = compiler->objectFunctionsBegin(obj), end = compiler->objectFunctionsEnd(obj); function != end; ++function) { quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; - if (astFunction->formals) - flags |= QQmlPropertyData::HasArguments; - - QString slotName = astFunction->name.toString(); + const QString slotName = stringAt(function->nameIndex); if (seenSignals.contains(slotName)) - return QQmlCompileError(s->location, tr("Duplicate method name: invalid override of property change signal or superclass signal")); + return QQmlCompileError(function->location, tr("Duplicate method name: invalid override of property change signal or superclass signal")); // Note: we don't append slotName to the seenSignals list, since we don't // protect against overriding change signals or methods with properties. QList parameterNames; - QQmlJS::AST::FormalParameterList *param = astFunction->formals; - while (param) { - parameterNames << param->name.toUtf8(); - param = param->next; + for (auto formal = function->formalsBegin(), end = function->formalsEnd(); formal != end; ++formal) { + flags |= QQmlPropertyData::HasArguments; + parameterNames << stringAt(*formal).toUtf8(); } cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); @@ -430,7 +424,7 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con // Dynamic properties int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; int propertyIdx = 0; - for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) { + for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p, ++propertyIdx) { int propertyType = 0; quint32 propertyFlags = 0; diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 1e01f72228..351c4f7294 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -80,12 +80,17 @@ struct QQmlTypeCompiler public: QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document); + // --- interface used by QQmlPropertyCacheCreator + QString stringAt(int idx) const; + QmlIR::PoolList::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } + QmlIR::PoolList::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } + // --- + QV4::CompiledData::CompilationUnit *compile(); QList compilationErrors() const { return errors; } void recordError(const QQmlError &error); - QString stringAt(int idx) const; int registerString(const QString &str); QV4::IR::Module *jsIRModule() const; -- cgit v1.2.3 From 4da7ca32bb271355ae3e707499a3f5627de069d4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 08:57:33 +0200 Subject: Minor QQmlPropertyCacheCreator API cleanup As part of removing the QQmlTypeCompiler dependency, the property cache vector output of the creator is now passed explicitly as an out parameter to the constructor. Similarly the error handling is made explicit in the API instead of implicitly passing any generated errors to the QQmlTypeCompiler. Change-Id: Ia3013011518f59f57f2eecb526f64dec7d82cb91 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlpropertycachecreator.cpp | 29 +++++--------- src/qml/compiler/qqmlpropertycachecreator_p.h | 8 ++-- src/qml/compiler/qqmltypecompiler.cpp | 57 ++++++++++++--------------- src/qml/compiler/qqmltypecompiler_p.h | 31 ++++++++------- 4 files changed, 57 insertions(+), 68 deletions(-) diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index a0bcba01ce..bd5bb3620d 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -68,34 +68,25 @@ QQmlPropertyCacheCreator::InstantiationContext::InstantiationContext(int referen } } -QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler) +QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler, QQmlPropertyCacheVector *propertyCaches) : QQmlCompilePass(typeCompiler) , enginePrivate(typeCompiler->enginePrivate()) , qmlObjects(*typeCompiler->qmlObjects()) , imports(typeCompiler->imports()) , resolvedTypes(typeCompiler->resolvedTypes()) + , propertyCaches(propertyCaches) { + propertyCaches->resize(qmlObjects.count()); } QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator() { } -bool QQmlPropertyCacheCreator::buildMetaObjects() +QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjects() { - propertyCaches.resize(qmlObjects.count()); - InstantiationContext context; - - QQmlCompileError error = buildMetaObjectRecursively(compiler->rootObjectIndex(), context); - if (error.isSet()) { - recordError(error); - return false; - } - - compiler->setPropertyCaches(std::move(propertyCaches)); - - return true; + return buildMetaObjectRecursively(compiler->rootObjectIndex(), context); } QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, const InstantiationContext &context) @@ -111,7 +102,7 @@ QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int object // the property that references us, for the latter we only need a meta-object on the referencing object // because interceptors can't go to the shared value type instances. if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType)) { - if (!propertyCaches.needsVMEMetaObject(context.referencingObjectIndex)) { + if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { const QmlIR::Object *obj = qmlObjects.at(context.referencingObjectIndex); auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); @@ -143,11 +134,11 @@ QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int object if (error.isSet()) return error; } else { - propertyCaches.set(objectIndex, baseTypeCache); + propertyCaches->set(objectIndex, baseTypeCache); } } - if (QQmlPropertyCache *thisCache = propertyCaches.at(objectIndex)) { + if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) { for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) if (binding->type >= QV4::CompiledData::Binding::Type_Object) { InstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); @@ -226,8 +217,8 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), obj->signalCount() + obj->propertyCount() + obj->aliasCount())); - propertyCaches.set(objectIndex, cache); - propertyCaches.setNeedsVMEMetaObject(objectIndex); + propertyCaches->set(objectIndex, cache); + propertyCaches->setNeedsVMEMetaObject(objectIndex); struct TypeData { QV4::CompiledData::Property::Type dtype; diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 126bdbb017..8dd4b2bc0f 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -58,12 +58,12 @@ class QQmlPropertyCacheCreator : public QQmlCompilePass { Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator) public: - QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler); + QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler, QQmlPropertyCacheVector *propertyCaches); ~QQmlPropertyCacheCreator(); - bool buildMetaObjects(); -protected: + QQmlCompileError buildMetaObjects(); +protected: struct InstantiationContext { InstantiationContext(); InstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache); @@ -81,7 +81,7 @@ protected: const QVector &qmlObjects; const QQmlImports *imports; QHash *resolvedTypes; - QQmlPropertyCacheVector propertyCaches; + QQmlPropertyCacheVector *propertyCaches; }; QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index c75f951adb..f2a8538b4c 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -84,12 +84,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() QQmlType *qmlType = resolvedType->type; if (resolvedType->typeData) { if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) { - QQmlError error; - QString reason = tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName()); - error.setDescription(reason); - error.setColumn(resolvedType->location.column); - error.setLine(resolvedType->location.line); - recordError(error); + recordError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName())); return nullptr; } ref->compilationUnit = resolvedType->typeData->compilationUnit(); @@ -102,23 +97,13 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() QString reason = ref->type->noCreationReason(); if (reason.isEmpty()) reason = tr("Element is not creatable."); - error.setDescription(reason); - error.setColumn(resolvedType->location.column); - error.setLine(resolvedType->location.line); - recordError(error); + recordError(resolvedType->location, reason); return nullptr; } if (ref->type->containsRevisionedAttributes()) { ref->typePropertyCache = engine->cache(ref->type, resolvedType->minorVersion); - if (!ref->typePropertyCache) { - QQmlError cacheError; - cacheError.setColumn(resolvedType->location.column); - cacheError.setLine(resolvedType->location.line); - recordError(cacheError); - return nullptr; - } } } ref->majorVersion = resolvedType->majorVersion; @@ -137,9 +122,12 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() } { - QQmlPropertyCacheCreator propertyCacheBuilder(this); - if (!propertyCacheBuilder.buildMetaObjects()) + QQmlPropertyCacheCreator propertyCacheBuilder(this, &m_propertyCaches); + QQmlCompileError error = propertyCacheBuilder.buildMetaObjects(); + if (error.isSet()) { + recordError(error); return nullptr; + } } { @@ -299,11 +287,25 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() return nullptr; } -void QQmlTypeCompiler::recordError(const QQmlError &error) +void QQmlTypeCompiler::recordError(QQmlError error) +{ + error.setUrl(url()); + errors << error; +} + +void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description) +{ + QQmlError error; + error.setLine(location.line); + error.setColumn(location.column); + error.setDescription(description); + error.setUrl(url()); + errors << error; +} + +void QQmlTypeCompiler::recordError(const QQmlCompileError &error) { - QQmlError e = error; - e.setUrl(url()); - errors << e; + recordError(error.location, error.description); } QString QQmlTypeCompiler::stringAt(int idx) const @@ -392,14 +394,7 @@ QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler) { } -void QQmlCompilePass::recordError(const QV4::CompiledData::Location &location, const QString &description) const -{ - QQmlError error; - error.setLine(location.line); - error.setColumn(location.column); - error.setDescription(description); - compiler->recordError(error); -} + SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 351c4f7294..29712e73e7 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -74,6 +74,17 @@ struct Location; } } +struct QQmlCompileError +{ + QQmlCompileError() {} + QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description) + : location(location), description(description) {} + QV4::CompiledData::Location location; + QString description; + + bool isSet() const { return !description.isEmpty(); } +}; + struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) @@ -89,7 +100,9 @@ public: QV4::CompiledData::CompilationUnit *compile(); QList compilationErrors() const { return errors; } - void recordError(const QQmlError &error); + void recordError(QQmlError error); + void recordError(const QV4::CompiledData::Location &location, const QString &description); + void recordError(const QQmlCompileError &error); int registerString(const QString &str); @@ -132,17 +145,6 @@ private: QQmlPropertyCacheVector m_propertyCaches; }; -struct QQmlCompileError -{ - QQmlCompileError() {} - QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description) - : location(location), description(description) {} - QV4::CompiledData::Location location; - QString description; - - bool isSet() const { return !description.isEmpty(); } -}; - struct QQmlCompilePass { virtual ~QQmlCompilePass() {} @@ -151,9 +153,10 @@ struct QQmlCompilePass QString stringAt(int idx) const { return compiler->stringAt(idx); } protected: - void recordError(const QV4::CompiledData::Location &location, const QString &description) const; + void recordError(const QV4::CompiledData::Location &location, const QString &description) const + { compiler->recordError(location, description); } void recordError(const QQmlCompileError &error) - { recordError(error.location, error.description); } + { compiler->recordError(error); } QQmlTypeCompiler *compiler; }; -- cgit v1.2.3 From ebe51b92a353ba17e6293c280b77aab3f2a02a90 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 09:14:04 +0200 Subject: Remove base-class dependency of QQmlPropertyCacheCreator By not depending on QQmlCompilePass the code will be re-usable also outside of the type compiler in the future. Change-Id: I55c637886cf557d6d130c4f9325d6b0790cb2806 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlpropertycachecreator.cpp | 19 ++++++++------- src/qml/compiler/qqmlpropertycachecreator_p.h | 11 +++++---- src/qml/compiler/qqmltypecompiler.cpp | 33 ++++++++++++--------------- src/qml/compiler/qqmltypecompiler_p.h | 7 ++---- 4 files changed, 31 insertions(+), 39 deletions(-) diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index bd5bb3620d..c6cd29a317 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -68,12 +68,11 @@ QQmlPropertyCacheCreator::InstantiationContext::InstantiationContext(int referen } } -QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler, QQmlPropertyCacheVector *propertyCaches) - : QQmlCompilePass(typeCompiler) - , enginePrivate(typeCompiler->enginePrivate()) - , qmlObjects(*typeCompiler->qmlObjects()) - , imports(typeCompiler->imports()) - , resolvedTypes(typeCompiler->resolvedTypes()) +QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const QQmlTypeCompiler *compiler, const QQmlImports *imports) + : enginePrivate(enginePrivate) + , compiler(compiler) + , qmlObjects(*compiler->qmlObjects()) + , imports(imports) , propertyCaches(propertyCaches) { propertyCaches->resize(qmlObjects.count()); @@ -104,7 +103,7 @@ QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int object if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType)) { if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { const QmlIR::Object *obj = qmlObjects.at(context.referencingObjectIndex); - auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + auto *typeRef = compiler->resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); @@ -161,7 +160,7 @@ QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR: return enginePrivate->cache(vtmo); } } else if (obj->inheritedTypeNameIndex != 0) { - auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + auto *typeRef = compiler->resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); if (typeRef->isFullyDynamicType) { @@ -181,7 +180,7 @@ QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR: return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { - auto *typeRef = resolvedTypes->value(context.instantiatingBinding->propertyNameIndex); + auto *typeRef = compiler->resolvedTypes.value(context.instantiatingBinding->propertyNameIndex); Q_ASSERT(typeRef); QQmlType *qmltype = typeRef->type; if (!qmltype) { @@ -250,7 +249,7 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con QByteArray newClassName; if (objectIndex == compiler->rootObjectIndex()) { - QString path = compiler->url().path(); + const QString path = compiler->url().path(); int lastSlash = path.lastIndexOf(QLatin1Char('/')); if (lastSlash > -1) { const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 8dd4b2bc0f..6d575c6a99 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -54,11 +54,11 @@ QT_BEGIN_NAMESPACE -class QQmlPropertyCacheCreator : public QQmlCompilePass +class QQmlPropertyCacheCreator { Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator) public: - QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler, QQmlPropertyCacheVector *propertyCaches); + QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const QQmlTypeCompiler *compiler, const QQmlImports *imports); ~QQmlPropertyCacheCreator(); QQmlCompileError buildMetaObjects(); @@ -76,11 +76,12 @@ protected: QQmlPropertyCache *propertyCacheForObject(const QmlIR::Object *obj, const InstantiationContext &context, QQmlCompileError *error) const; QQmlCompileError createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache); + QString stringAt(int index) const { return compiler->stringAt(index); } - QQmlEnginePrivate *enginePrivate; + QQmlEnginePrivate * const enginePrivate; + const QQmlTypeCompiler *compiler; const QVector &qmlObjects; - const QQmlImports *imports; - QHash *resolvedTypes; + const QQmlImports * const imports; QQmlPropertyCacheVector *propertyCaches; }; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index f2a8538b4c..e6a71c6e15 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -77,8 +77,8 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() typeData->imports().populateCache(importCache.data()); - const QHash &resolvedTypes = typeData->resolvedTypeRefs(); - for (QHash::ConstIterator resolvedType = resolvedTypes.constBegin(), end = resolvedTypes.constEnd(); + const QHash &resolvedTypeRefs = typeData->resolvedTypeRefs(); + for (QHash::ConstIterator resolvedType = resolvedTypeRefs.constBegin(), end = resolvedTypeRefs.constEnd(); resolvedType != end; ++resolvedType) { QScopedPointer ref(new QV4::CompiledData::CompilationUnit::ResolvedTypeReference); QQmlType *qmlType = resolvedType->type; @@ -109,12 +109,12 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() ref->majorVersion = resolvedType->majorVersion; ref->minorVersion = resolvedType->minorVersion; ref->doDynamicTypeCheck(); - m_resolvedTypes.insert(resolvedType.key(), ref.take()); + resolvedTypes.insert(resolvedType.key(), ref.take()); } // Build property caches and VME meta object data - for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); + for (auto it = resolvedTypes.constBegin(), end = resolvedTypes.constEnd(); it != end; ++it) { QQmlCustomParser *customParser = (*it)->type ? (*it)->type->customParser() : 0; if (customParser) @@ -122,7 +122,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() } { - QQmlPropertyCacheCreator propertyCacheBuilder(this, &m_propertyCaches); + QQmlPropertyCacheCreator propertyCacheBuilder(&m_propertyCaches, engine, this, imports()); QQmlCompileError error = propertyCacheBuilder.buildMetaObjects(); if (error.isSet()) { recordError(error); @@ -231,7 +231,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() compilationUnit = document->javaScriptCompilationUnit; compilationUnit->importCache = importCache; compilationUnit->dependentScripts = dependentScripts; - compilationUnit->resolvedTypes = m_resolvedTypes; + compilationUnit->resolvedTypes = resolvedTypes; compilationUnit->propertyCaches = std::move(m_propertyCaches); Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast(compilationUnit->data->nObjects)); @@ -240,7 +240,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() engine->registerInternalCompositeType(compilationUnit); else { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject); - auto *typeRef = m_resolvedTypes.value(obj->inheritedTypeNameIndex); + auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); if (typeRef->compilationUnit) { compilationUnit->metaTypeId = typeRef->compilationUnit->metaTypeId; @@ -263,7 +263,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() for (quint32 i = 0; i < qmlUnit->nObjects; ++i) { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); bindingCount += obj->nBindings; - if (auto *typeRef = m_resolvedTypes.value(obj->inheritedTypeNameIndex)) { + if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { if (QQmlType *qmlType = typeRef->type) { if (qmlType->parserStatusCast() != -1) ++parserStatusCount; @@ -333,12 +333,7 @@ const QQmlImports *QQmlTypeCompiler::imports() const return &typeData->imports(); } -QHash *QQmlTypeCompiler::resolvedTypes() -{ - return &m_resolvedTypes; -} - -QVector *QQmlTypeCompiler::qmlObjects() +QVector *QQmlTypeCompiler::qmlObjects() const { return &document->objects; } @@ -402,7 +397,7 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) , qmlObjects(*typeCompiler->qmlObjects()) , imports(typeCompiler->imports()) , customParsers(typeCompiler->customParserCache()) - , resolvedTypes(*typeCompiler->resolvedTypes()) + , resolvedTypes(typeCompiler->resolvedTypes) , illegalNames(QV8Engine::get(QQmlEnginePrivate::get(typeCompiler->enginePrivate()))->illegalNames()) , propertyCaches(typeCompiler->propertyCaches()) { @@ -613,7 +608,7 @@ QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler) , qmlObjects(*typeCompiler->qmlObjects()) , propertyCaches(typeCompiler->propertyCaches()) , imports(typeCompiler->imports()) - , resolvedTypes(typeCompiler->resolvedTypes()) + , resolvedTypes(&typeCompiler->resolvedTypes) { } @@ -872,7 +867,7 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t , qmlObjects(typeCompiler->qmlObjects()) , indexOfRootObject(typeCompiler->rootObjectIndex()) , _componentIndex(-1) - , resolvedTypes(typeCompiler->resolvedTypes()) + , resolvedTypes(&typeCompiler->resolvedTypes) , propertyCaches(std::move(typeCompiler->takePropertyCaches())) { } @@ -1342,7 +1337,7 @@ QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, con : QQmlCompilePass(typeCompiler) , enginePrivate(typeCompiler->enginePrivate()) , qmlUnit(typeCompiler->qmlUnit()) - , resolvedTypes(*typeCompiler->resolvedTypes()) + , resolvedTypes(typeCompiler->resolvedTypes) , customParsers(typeCompiler->customParserCache()) , propertyCaches(propertyCaches) { @@ -2004,7 +1999,7 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen) : QQmlCompilePass(typeCompiler) - , resolvedTypes(*typeCompiler->resolvedTypes()) + , resolvedTypes(typeCompiler->resolvedTypes) , customParsers(typeCompiler->customParserCache()) , qmlObjects(*typeCompiler->qmlObjects()) , propertyCaches(typeCompiler->propertyCaches()) diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 29712e73e7..71d2d2337e 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -95,6 +95,7 @@ public: QString stringAt(int idx) const; QmlIR::PoolList::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } QmlIR::PoolList::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } + QHash resolvedTypes; // --- QV4::CompiledData::CompilationUnit *compile(); @@ -113,8 +114,7 @@ public: QUrl url() const { return typeData->finalUrl(); } QQmlEnginePrivate *enginePrivate() const { return engine; } const QQmlImports *imports() const; - QHash *resolvedTypes(); - QVector *qmlObjects(); + QVector *qmlObjects() const; int rootObjectIndex() const; void setPropertyCaches(QQmlPropertyCacheVector &&caches); const QQmlPropertyCacheVector *propertyCaches() const; @@ -138,7 +138,6 @@ private: QmlIR::Document *document; // index is string index of type name (use obj->inheritedTypeNameIndex) QHash customParsers; - QHash m_resolvedTypes; // index in first hash is component index, vector inside contains object indices of objects with id property QVector m_componentRoots; @@ -147,8 +146,6 @@ private: struct QQmlCompilePass { - virtual ~QQmlCompilePass() {} - QQmlCompilePass(QQmlTypeCompiler *typeCompiler); QString stringAt(int idx) const { return compiler->stringAt(idx); } -- cgit v1.2.3 From 54d92bbcbf9cde4d66cfea9053e66e7b072a7ada Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 09:24:51 +0200 Subject: Separate non-generic code out of QQmlPropertyCacheCreator Some code is not suitable for a future conversion of the class to a template, which is now moved out. This includes the tr() handling as well as the instantiation context, which does not require any parameterization. Change-Id: Ib40e24ebb7166c8649ca218f02558a737d39fe53 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlpropertycachecreator.cpp | 34 +++++++++++++-------------- src/qml/compiler/qqmlpropertycachecreator_p.h | 30 +++++++++++++---------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index c6cd29a317..47b9dad19a 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -43,9 +43,9 @@ QT_BEGIN_NAMESPACE -static QAtomicInt classIndexCounter(0); +QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0); -QQmlPropertyCacheCreator::InstantiationContext::InstantiationContext() +QQmlBindingInstantiationContext::QQmlBindingInstantiationContext() : referencingObjectIndex(-1) , instantiatingBinding(nullptr) , instantiatingProperty(nullptr) @@ -53,7 +53,7 @@ QQmlPropertyCacheCreator::InstantiationContext::InstantiationContext() } -QQmlPropertyCacheCreator::InstantiationContext::InstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache) +QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache) : referencingObjectIndex(referencingObjectIndex) , instantiatingBinding(instantiatingBinding) , instantiatingProperty(nullptr) @@ -84,11 +84,11 @@ QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator() QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjects() { - InstantiationContext context; + QQmlBindingInstantiationContext context; return buildMetaObjectRecursively(compiler->rootObjectIndex(), context); } -QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, const InstantiationContext &context) +QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) { const QmlIR::Object *obj = qmlObjects.at(objectIndex); @@ -140,7 +140,7 @@ QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int object if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) { for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - InstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); + QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context); if (error.isSet()) return error; @@ -151,7 +151,7 @@ QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int object return noError; } -QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR::Object *obj, const QQmlPropertyCacheCreator::InstantiationContext &context, QQmlCompileError *error) const +QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR::Object *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const { if (context.instantiatingProperty) { if (context.instantiatingProperty->isQObject()) { @@ -165,15 +165,15 @@ QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR: if (typeRef->isFullyDynamicType) { if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { - *error = QQmlCompileError(obj->location, tr("Fully dynamic types cannot declare new properties.")); + *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties.")); return nullptr; } if (obj->signalCount() > 0) { - *error = QQmlCompileError(obj->location, tr("Fully dynamic types cannot declare new signals.")); + *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals.")); return nullptr; } if (obj->functionCount() > 0) { - *error = QQmlCompileError(obj->location, tr("Fully Dynamic types cannot declare new functions.")); + *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions.")); return nullptr; } } @@ -201,7 +201,7 @@ QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR: const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; if (!attachedMo) { - *error = QQmlCompileError(context.instantiatingBinding->location, tr("Non-existent attached object")); + *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object")); return nullptr; } return enginePrivate->cache(attachedMo); @@ -277,14 +277,14 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con bool notInRevision = false; QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); if (d && d->isFinal()) - return QQmlCompileError(p->location, tr("Cannot override FINAL property")); + return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); } for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) { bool notInRevision = false; QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); if (d && d->isFinal()) - return QQmlCompileError(a->location, tr("Cannot override FINAL property")); + return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); } int effectivePropertyIndex = cache->propertyIndexCacheStart; @@ -357,7 +357,7 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con const QString customTypeName = stringAt(param->customTypeNameIndex); QQmlType *qmltype = 0; if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) - return QQmlCompileError(s->location, tr("Invalid signal parameter type: %1").arg(customTypeName)); + return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); if (qmltype->isComposite()) { QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); @@ -383,7 +383,7 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con QString signalName = stringAt(s->nameIndex); if (seenSignals.contains(signalName)) - return QQmlCompileError(s->location, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); + return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal")); seenSignals.insert(signalName); cache->appendSignal(signalName, flags, effectiveMethodIndex++, @@ -397,7 +397,7 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con const QString slotName = stringAt(function->nameIndex); if (seenSignals.contains(slotName)) - return QQmlCompileError(function->location, tr("Duplicate method name: invalid override of property change signal or superclass signal")); + return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal")); // Note: we don't append slotName to the seenSignals list, since we don't // protect against overriding change signals or methods with properties. @@ -432,7 +432,7 @@ QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, con QQmlType *qmltype = 0; if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { - return QQmlCompileError(p->location, tr("Invalid property type")); + return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); } Q_ASSERT(qmltype); diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 6d575c6a99..f78aa69cfe 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -54,9 +54,23 @@ QT_BEGIN_NAMESPACE -class QQmlPropertyCacheCreator +struct QQmlBindingInstantiationContext { + QQmlBindingInstantiationContext(); + QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache); + int referencingObjectIndex; + const QV4::CompiledData::Binding *instantiatingBinding; + QQmlPropertyData *instantiatingProperty; +}; + +struct QQmlPropertyCacheCreatorBase +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase) +public: + static QAtomicInt classIndexCounter; +}; + +class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase { - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator) public: QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const QQmlTypeCompiler *compiler, const QQmlImports *imports); ~QQmlPropertyCacheCreator(); @@ -64,16 +78,8 @@ public: QQmlCompileError buildMetaObjects(); protected: - struct InstantiationContext { - InstantiationContext(); - InstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache); - int referencingObjectIndex; - const QV4::CompiledData::Binding *instantiatingBinding; - QQmlPropertyData *instantiatingProperty; - }; - - QQmlCompileError buildMetaObjectRecursively(int objectIndex, const InstantiationContext &context); - QQmlPropertyCache *propertyCacheForObject(const QmlIR::Object *obj, const InstantiationContext &context, QQmlCompileError *error) const; + QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context); + QQmlPropertyCache *propertyCacheForObject(const QmlIR::Object *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; QQmlCompileError createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache); QString stringAt(int index) const { return compiler->stringAt(index); } -- cgit v1.2.3 From 9cab81204c506b9a70377bd79773920f3c445d44 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 09:48:34 +0200 Subject: Move QQmlPropertyCacheCreator implementation into the header file When the class becomes a template, it will have to live there anyway. By moving it separately now the diff of the template conversion becomes much easier to read. Change-Id: I0fbda26d908c928d96262bc55da20a36391a8092 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlpropertycachecreator.cpp | 413 -------------------------- src/qml/compiler/qqmlpropertycachecreator_p.h | 412 ++++++++++++++++++++++++- 2 files changed, 411 insertions(+), 414 deletions(-) diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index 47b9dad19a..f8d63ec634 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -68,417 +68,4 @@ QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencing } } -QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const QQmlTypeCompiler *compiler, const QQmlImports *imports) - : enginePrivate(enginePrivate) - , compiler(compiler) - , qmlObjects(*compiler->qmlObjects()) - , imports(imports) - , propertyCaches(propertyCaches) -{ - propertyCaches->resize(qmlObjects.count()); -} - -QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator() -{ -} - -QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjects() -{ - QQmlBindingInstantiationContext context; - return buildMetaObjectRecursively(compiler->rootObjectIndex(), context); -} - -QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) -{ - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - - bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; - if (!needVMEMetaObject) { - for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) { - if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { - // If the on assignment is inside a group property, we need to distinguish between QObject based - // group properties and value type group properties. For the former the base type is derived from - // the property that references us, for the latter we only need a meta-object on the referencing object - // because interceptors can't go to the shared value type instances. - if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType)) { - if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { - const QmlIR::Object *obj = qmlObjects.at(context.referencingObjectIndex); - auto *typeRef = compiler->resolvedTypes.value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); - if (error.isSet()) - return error; - } - } else { - // On assignments are implemented using value interceptors, which require a VME meta object. - needVMEMetaObject = true; - } - break; - } - } - } - - QQmlPropertyCache *baseTypeCache; - { - QQmlCompileError error; - baseTypeCache = propertyCacheForObject(obj, context, &error); - if (error.isSet()) - return error; - } - - if (baseTypeCache) { - if (needVMEMetaObject) { - QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache); - if (error.isSet()) - return error; - } else { - propertyCaches->set(objectIndex, baseTypeCache); - } - } - - if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) { - for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); - QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context); - if (error.isSet()) - return error; - } - } - - QQmlCompileError noError; - return noError; -} - -QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR::Object *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const -{ - if (context.instantiatingProperty) { - if (context.instantiatingProperty->isQObject()) { - return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType); - } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType)) { - return enginePrivate->cache(vtmo); - } - } else if (obj->inheritedTypeNameIndex != 0) { - auto *typeRef = compiler->resolvedTypes.value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - - if (typeRef->isFullyDynamicType) { - if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties.")); - return nullptr; - } - if (obj->signalCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals.")); - return nullptr; - } - if (obj->functionCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions.")); - return nullptr; - } - } - - return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { - auto *typeRef = compiler->resolvedTypes.value(context.instantiatingBinding->propertyNameIndex); - Q_ASSERT(typeRef); - QQmlType *qmltype = typeRef->type; - if (!qmltype) { - QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex); - if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) { - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - - tdata->release(); - } - } - } - - const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; - if (!attachedMo) { - *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object")); - return nullptr; - } - return enginePrivate->cache(attachedMo); - } - return nullptr; -} - -QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache) -{ - QQmlRefPointer cache; - cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), - obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), - obj->signalCount() + obj->propertyCount() + obj->aliasCount())); - - propertyCaches->set(objectIndex, cache); - propertyCaches->setNeedsVMEMetaObject(objectIndex); - - struct TypeData { - QV4::CompiledData::Property::Type dtype; - int metaType; - } builtinTypes[] = { - { QV4::CompiledData::Property::Var, QMetaType::QVariant }, - { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, - { QV4::CompiledData::Property::Int, QMetaType::Int }, - { QV4::CompiledData::Property::Bool, QMetaType::Bool }, - { QV4::CompiledData::Property::Real, QMetaType::Double }, - { QV4::CompiledData::Property::String, QMetaType::QString }, - { QV4::CompiledData::Property::Url, QMetaType::QUrl }, - { QV4::CompiledData::Property::Color, QMetaType::QColor }, - { QV4::CompiledData::Property::Font, QMetaType::QFont }, - { QV4::CompiledData::Property::Time, QMetaType::QTime }, - { QV4::CompiledData::Property::Date, QMetaType::QDate }, - { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, - { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, - { QV4::CompiledData::Property::Point, QMetaType::QPointF }, - { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, - { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, - { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, - { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, - { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, - { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } - }; - static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); - - QByteArray newClassName; - - if (objectIndex == compiler->rootObjectIndex()) { - const QString path = compiler->url().path(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - if (lastSlash > -1) { - const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); - if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) - newClassName = nameBase.toUtf8() + "_QMLTYPE_" + - QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); - } - } - if (newClassName.isEmpty()) { - newClassName = QQmlMetaObject(baseTypeCache).className(); - newClassName.append("_QML_"); - newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); - } - - cache->_dynamicClassName = newClassName; - - int varPropCount = 0; - - QmlIR::PropertyResolver resolver(baseTypeCache); - - for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) { - if (p->type == QV4::CompiledData::Property::Var) - varPropCount++; - - bool notInRevision = false; - QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); - if (d && d->isFinal()) - return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); - } - - for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) { - bool notInRevision = false; - QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); - if (d && d->isFinal()) - return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); - } - - int effectivePropertyIndex = cache->propertyIndexCacheStart; - int effectiveMethodIndex = cache->methodIndexCacheStart; - - // For property change signal override detection. - // We prepopulate a set of signal names which already exist in the object, - // and throw an error if there is a signal/method defined as an override. - QSet seenSignals; - seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); - QQmlPropertyCache *parentCache = cache; - while ((parentCache = parentCache->parent())) { - if (int pSigCount = parentCache->signalCount()) { - int pSigOffset = parentCache->signalOffset(); - for (int i = pSigOffset; i < pSigCount; ++i) { - QQmlPropertyData *currPSig = parentCache->signal(i); - // XXX TODO: find a better way to get signal name from the property data :-/ - for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); - iter != parentCache->stringCache.end(); ++iter) { - if (currPSig == (*iter).second) { - seenSignals.insert(iter.key()); - break; - } - } - } - } - } - - // Set up notify signals for properties - first normal, then alias - for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) { - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; - - QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); - seenSignals.insert(changedSigName); - - cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); - } - - for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) { - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; - - QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); - seenSignals.insert(changedSigName); - - cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); - } - - // Dynamic signals - for (auto s = obj->signalsBegin(), end = obj->signalsEnd(); s != end; ++s) { - const int paramCount = s->parameterCount(); - - QList names; - names.reserve(paramCount); - QVarLengthArray paramTypes(paramCount?(paramCount + 1):0); - - if (paramCount) { - paramTypes[0] = paramCount; - - int i = 0; - for (auto param = s->parametersBegin(), end = s->parametersEnd(); param != end; ++param, ++i) { - names.append(stringAt(param->nameIndex).toUtf8()); - if (param->type < builtinTypeCount) { - // built-in type - paramTypes[i + 1] = builtinTypes[param->type].metaType; - } else { - // lazily resolved type - Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); - const QString customTypeName = stringAt(param->customTypeNameIndex); - QQmlType *qmltype = 0; - if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) - return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); - - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - - paramTypes[i + 1] = compilationUnit->metaTypeId; - - tdata->release(); - } else { - paramTypes[i + 1] = qmltype->typeId(); - } - } - } - } - - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; - if (paramCount) - flags |= QQmlPropertyData::HasArguments; - - QString signalName = stringAt(s->nameIndex); - if (seenSignals.contains(signalName)) - return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal")); - seenSignals.insert(signalName); - - cache->appendSignal(signalName, flags, effectiveMethodIndex++, - paramCount?paramTypes.constData():0, names); - } - - - // Dynamic slots - for (auto function = compiler->objectFunctionsBegin(obj), end = compiler->objectFunctionsEnd(obj); function != end; ++function) { - quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; - - const QString slotName = stringAt(function->nameIndex); - if (seenSignals.contains(slotName)) - return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal")); - // Note: we don't append slotName to the seenSignals list, since we don't - // protect against overriding change signals or methods with properties. - - QList parameterNames; - for (auto formal = function->formalsBegin(), end = function->formalsEnd(); formal != end; ++formal) { - flags |= QQmlPropertyData::HasArguments; - parameterNames << stringAt(*formal).toUtf8(); - } - - cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); - } - - - // Dynamic properties - int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; - int propertyIdx = 0; - for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p, ++propertyIdx) { - int propertyType = 0; - quint32 propertyFlags = 0; - - if (p->type == QV4::CompiledData::Property::Var) { - propertyType = QMetaType::QVariant; - propertyFlags = QQmlPropertyData::IsVarProperty; - } else if (p->type < builtinTypeCount) { - propertyType = builtinTypes[p->type].metaType; - - if (p->type == QV4::CompiledData::Property::Variant) - propertyFlags |= QQmlPropertyData::IsQVariant; - } else { - Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || - p->type == QV4::CompiledData::Property::Custom); - - QQmlType *qmltype = 0; - if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { - return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); - } - - Q_ASSERT(qmltype); - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = compilationUnit->metaTypeId; - } else { - propertyType = compilationUnit->listMetaTypeId; - } - - tdata->release(); - } else { - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = qmltype->typeId(); - } else { - propertyType = qmltype->qListTypeId(); - } - } - - if (p->type == QV4::CompiledData::Property::Custom) - propertyFlags |= QQmlPropertyData::IsQObjectDerived; - else - propertyFlags |= QQmlPropertyData::IsQList; - } - - if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) - propertyFlags |= QQmlPropertyData::IsWritable; - - - QString propertyName = stringAt(p->nameIndex); - if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) - cache->_defaultPropertyName = propertyName; - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - propertyType, effectiveSignalIndex); - - effectiveSignalIndex++; - } - - QQmlCompileError noError; - return noError; -} - QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index f78aa69cfe..2a82d3746c 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -51,6 +51,8 @@ // #include "qqmltypecompiler_p.h" +#include +#include QT_BEGIN_NAMESPACE @@ -73,7 +75,6 @@ class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase { public: QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const QQmlTypeCompiler *compiler, const QQmlImports *imports); - ~QQmlPropertyCacheCreator(); QQmlCompileError buildMetaObjects(); @@ -91,6 +92,415 @@ protected: QQmlPropertyCacheVector *propertyCaches; }; +inline QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const QQmlTypeCompiler *compiler, const QQmlImports *imports) + : enginePrivate(enginePrivate) + , compiler(compiler) + , qmlObjects(*compiler->qmlObjects()) + , imports(imports) + , propertyCaches(propertyCaches) +{ + propertyCaches->resize(qmlObjects.count()); +} + +inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjects() +{ + QQmlBindingInstantiationContext context; + return buildMetaObjectRecursively(compiler->rootObjectIndex(), context); +} + +inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) +{ + const QmlIR::Object *obj = qmlObjects.at(objectIndex); + + bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; + if (!needVMEMetaObject) { + for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) { + if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + // If the on assignment is inside a group property, we need to distinguish between QObject based + // group properties and value type group properties. For the former the base type is derived from + // the property that references us, for the latter we only need a meta-object on the referencing object + // because interceptors can't go to the shared value type instances. + if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType)) { + if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { + const QmlIR::Object *obj = qmlObjects.at(context.referencingObjectIndex); + auto *typeRef = compiler->resolvedTypes.value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); + if (error.isSet()) + return error; + } + } else { + // On assignments are implemented using value interceptors, which require a VME meta object. + needVMEMetaObject = true; + } + break; + } + } + } + + QQmlPropertyCache *baseTypeCache; + { + QQmlCompileError error; + baseTypeCache = propertyCacheForObject(obj, context, &error); + if (error.isSet()) + return error; + } + + if (baseTypeCache) { + if (needVMEMetaObject) { + QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache); + if (error.isSet()) + return error; + } else { + propertyCaches->set(objectIndex, baseTypeCache); + } + } + + if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) { + for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) + if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); + QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context); + if (error.isSet()) + return error; + } + } + + QQmlCompileError noError; + return noError; +} + +inline QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR::Object *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const +{ + if (context.instantiatingProperty) { + if (context.instantiatingProperty->isQObject()) { + return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType); + } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType)) { + return enginePrivate->cache(vtmo); + } + } else if (obj->inheritedTypeNameIndex != 0) { + auto *typeRef = compiler->resolvedTypes.value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + + if (typeRef->isFullyDynamicType) { + if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { + *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties.")); + return nullptr; + } + if (obj->signalCount() > 0) { + *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals.")); + return nullptr; + } + if (obj->functionCount() > 0) { + *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions.")); + return nullptr; + } + } + + return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { + auto *typeRef = compiler->resolvedTypes.value(context.instantiatingBinding->propertyNameIndex); + Q_ASSERT(typeRef); + QQmlType *qmltype = typeRef->type; + if (!qmltype) { + QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex); + if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) { + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); + + tdata->release(); + } + } + } + + const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; + if (!attachedMo) { + *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object")); + return nullptr; + } + return enginePrivate->cache(attachedMo); + } + return nullptr; +} + +inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache) +{ + QQmlRefPointer cache; + cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), + obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), + obj->signalCount() + obj->propertyCount() + obj->aliasCount())); + + propertyCaches->set(objectIndex, cache); + propertyCaches->setNeedsVMEMetaObject(objectIndex); + + struct TypeData { + QV4::CompiledData::Property::Type dtype; + int metaType; + } builtinTypes[] = { + { QV4::CompiledData::Property::Var, QMetaType::QVariant }, + { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, + { QV4::CompiledData::Property::Int, QMetaType::Int }, + { QV4::CompiledData::Property::Bool, QMetaType::Bool }, + { QV4::CompiledData::Property::Real, QMetaType::Double }, + { QV4::CompiledData::Property::String, QMetaType::QString }, + { QV4::CompiledData::Property::Url, QMetaType::QUrl }, + { QV4::CompiledData::Property::Color, QMetaType::QColor }, + { QV4::CompiledData::Property::Font, QMetaType::QFont }, + { QV4::CompiledData::Property::Time, QMetaType::QTime }, + { QV4::CompiledData::Property::Date, QMetaType::QDate }, + { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, + { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, + { QV4::CompiledData::Property::Point, QMetaType::QPointF }, + { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, + { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, + { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, + { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, + { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, + { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } +}; + static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); + + QByteArray newClassName; + + if (objectIndex == compiler->rootObjectIndex()) { + const QString path = compiler->url().path(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + if (lastSlash > -1) { + const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); + if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) + newClassName = nameBase.toUtf8() + "_QMLTYPE_" + + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); + } + } + if (newClassName.isEmpty()) { + newClassName = QQmlMetaObject(baseTypeCache).className(); + newClassName.append("_QML_"); + newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); + } + + cache->_dynamicClassName = newClassName; + + int varPropCount = 0; + + QmlIR::PropertyResolver resolver(baseTypeCache); + + for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) { + if (p->type == QV4::CompiledData::Property::Var) + varPropCount++; + + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); + if (d && d->isFinal()) + return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); + } + + for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) { + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); + if (d && d->isFinal()) + return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); + } + + int effectivePropertyIndex = cache->propertyIndexCacheStart; + int effectiveMethodIndex = cache->methodIndexCacheStart; + + // For property change signal override detection. + // We prepopulate a set of signal names which already exist in the object, + // and throw an error if there is a signal/method defined as an override. + QSet seenSignals; + seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); + QQmlPropertyCache *parentCache = cache; + while ((parentCache = parentCache->parent())) { + if (int pSigCount = parentCache->signalCount()) { + int pSigOffset = parentCache->signalOffset(); + for (int i = pSigOffset; i < pSigCount; ++i) { + QQmlPropertyData *currPSig = parentCache->signal(i); + // XXX TODO: find a better way to get signal name from the property data :-/ + for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); + iter != parentCache->stringCache.end(); ++iter) { + if (currPSig == (*iter).second) { + seenSignals.insert(iter.key()); + break; + } + } + } + } + } + + // Set up notify signals for properties - first normal, then alias + for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) { + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + + QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + + for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) { + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + + QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + + // Dynamic signals + for (auto s = obj->signalsBegin(), end = obj->signalsEnd(); s != end; ++s) { + const int paramCount = s->parameterCount(); + + QList names; + names.reserve(paramCount); + QVarLengthArray paramTypes(paramCount?(paramCount + 1):0); + + if (paramCount) { + paramTypes[0] = paramCount; + + int i = 0; + for (auto param = s->parametersBegin(), end = s->parametersEnd(); param != end; ++param, ++i) { + names.append(stringAt(param->nameIndex).toUtf8()); + if (param->type < builtinTypeCount) { + // built-in type + paramTypes[i + 1] = builtinTypes[param->type].metaType; + } else { + // lazily resolved type + Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); + const QString customTypeName = stringAt(param->customTypeNameIndex); + QQmlType *qmltype = 0; + if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) + return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); + + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + + paramTypes[i + 1] = compilationUnit->metaTypeId; + + tdata->release(); + } else { + paramTypes[i + 1] = qmltype->typeId(); + } + } + } + } + + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + if (paramCount) + flags |= QQmlPropertyData::HasArguments; + + QString signalName = stringAt(s->nameIndex); + if (seenSignals.contains(signalName)) + return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal")); + seenSignals.insert(signalName); + + cache->appendSignal(signalName, flags, effectiveMethodIndex++, + paramCount?paramTypes.constData():0, names); + } + + + // Dynamic slots + for (auto function = compiler->objectFunctionsBegin(obj), end = compiler->objectFunctionsEnd(obj); function != end; ++function) { + quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; + + const QString slotName = stringAt(function->nameIndex); + if (seenSignals.contains(slotName)) + return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal")); + // Note: we don't append slotName to the seenSignals list, since we don't + // protect against overriding change signals or methods with properties. + + QList parameterNames; + for (auto formal = function->formalsBegin(), end = function->formalsEnd(); formal != end; ++formal) { + flags |= QQmlPropertyData::HasArguments; + parameterNames << stringAt(*formal).toUtf8(); + } + + cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); + } + + + // Dynamic properties + int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; + int propertyIdx = 0; + for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p, ++propertyIdx) { + int propertyType = 0; + quint32 propertyFlags = 0; + + if (p->type == QV4::CompiledData::Property::Var) { + propertyType = QMetaType::QVariant; + propertyFlags = QQmlPropertyData::IsVarProperty; + } else if (p->type < builtinTypeCount) { + propertyType = builtinTypes[p->type].metaType; + + if (p->type == QV4::CompiledData::Property::Variant) + propertyFlags |= QQmlPropertyData::IsQVariant; + } else { + Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || + p->type == QV4::CompiledData::Property::Custom); + + QQmlType *qmltype = 0; + if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { + return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); + } + + Q_ASSERT(qmltype); + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = compilationUnit->metaTypeId; + } else { + propertyType = compilationUnit->listMetaTypeId; + } + + tdata->release(); + } else { + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = qmltype->typeId(); + } else { + propertyType = qmltype->qListTypeId(); + } + } + + if (p->type == QV4::CompiledData::Property::Custom) + propertyFlags |= QQmlPropertyData::IsQObjectDerived; + else + propertyFlags |= QQmlPropertyData::IsQList; + } + + if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) + propertyFlags |= QQmlPropertyData::IsWritable; + + + QString propertyName = stringAt(p->nameIndex); + if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) + cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + propertyType, effectiveSignalIndex); + + effectiveSignalIndex++; + } + + QQmlCompileError noError; + return noError; +} + QT_END_NAMESPACE #endif // QQMLPROPERTYCACHECREATOR_P_H -- cgit v1.2.3 From cc297f8eadd4decf640a8bf1d09d14d3e3fac48e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 10:55:42 +0200 Subject: Convert QQmlPropertyCacheCreator to a template This will allow the use of it also against a QV4::CompilationUnit and assorted data structures loaded from the disk. Change-Id: I4f337d1fcf319e80e422448671476db907de6d9e Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlpropertycachecreator_p.h | 52 +++++++++++++++------------ src/qml/compiler/qqmltypecompiler.cpp | 2 +- src/qml/compiler/qqmltypecompiler_p.h | 3 ++ src/qml/qml/qqmlpropertycache_p.h | 4 +-- 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 2a82d3746c..ff8bc49e6f 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -71,46 +71,50 @@ public: static QAtomicInt classIndexCounter; }; +template class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase { public: - QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const QQmlTypeCompiler *compiler, const QQmlImports *imports); + typedef typename ObjectContainer::CompiledObject CompiledObject; + + QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports); QQmlCompileError buildMetaObjects(); protected: QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context); - QQmlPropertyCache *propertyCacheForObject(const QmlIR::Object *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; - QQmlCompileError createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache); + QQmlPropertyCache *propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; + QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache); - QString stringAt(int index) const { return compiler->stringAt(index); } + QString stringAt(int index) const { return objectContainer->stringAt(index); } QQmlEnginePrivate * const enginePrivate; - const QQmlTypeCompiler *compiler; - const QVector &qmlObjects; + const ObjectContainer * const objectContainer; const QQmlImports * const imports; QQmlPropertyCacheVector *propertyCaches; }; -inline QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const QQmlTypeCompiler *compiler, const QQmlImports *imports) +template +inline QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports) : enginePrivate(enginePrivate) - , compiler(compiler) - , qmlObjects(*compiler->qmlObjects()) + , objectContainer(objectContainer) , imports(imports) , propertyCaches(propertyCaches) { - propertyCaches->resize(qmlObjects.count()); + propertyCaches->resize(objectContainer->objectCount()); } -inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjects() +template +inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjects() { QQmlBindingInstantiationContext context; - return buildMetaObjectRecursively(compiler->rootObjectIndex(), context); + return buildMetaObjectRecursively(objectContainer->rootObjectIndex(), context); } -inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) +template +inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) { - const QmlIR::Object *obj = qmlObjects.at(objectIndex); + const CompiledObject *obj = objectContainer->objectAt(objectIndex); bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; if (!needVMEMetaObject) { @@ -122,8 +126,8 @@ inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int // because interceptors can't go to the shared value type instances. if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType)) { if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { - const QmlIR::Object *obj = qmlObjects.at(context.referencingObjectIndex); - auto *typeRef = compiler->resolvedTypes.value(obj->inheritedTypeNameIndex); + const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex); + auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); @@ -171,7 +175,8 @@ inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjectRecursively(int return noError; } -inline QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const QmlIR::Object *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const +template +inline QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const { if (context.instantiatingProperty) { if (context.instantiatingProperty->isQObject()) { @@ -180,7 +185,7 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const return enginePrivate->cache(vtmo); } } else if (obj->inheritedTypeNameIndex != 0) { - auto *typeRef = compiler->resolvedTypes.value(obj->inheritedTypeNameIndex); + auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); if (typeRef->isFullyDynamicType) { @@ -200,7 +205,7 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { - auto *typeRef = compiler->resolvedTypes.value(context.instantiatingBinding->propertyNameIndex); + auto *typeRef = objectContainer->resolvedTypes.value(context.instantiatingBinding->propertyNameIndex); Q_ASSERT(typeRef); QQmlType *qmltype = typeRef->type; if (!qmltype) { @@ -229,7 +234,8 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCacheForObject(const return nullptr; } -inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache) +template +inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache) { QQmlRefPointer cache; cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), @@ -268,8 +274,8 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectInd QByteArray newClassName; - if (objectIndex == compiler->rootObjectIndex()) { - const QString path = compiler->url().path(); + if (objectIndex == objectContainer->rootObjectIndex()) { + const QString path = objectContainer->url().path(); int lastSlash = path.lastIndexOf(QLatin1Char('/')); if (lastSlash > -1) { const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); @@ -412,7 +418,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectInd // Dynamic slots - for (auto function = compiler->objectFunctionsBegin(obj), end = compiler->objectFunctionsEnd(obj); function != end; ++function) { + for (auto function = objectContainer->objectFunctionsBegin(obj), end = objectContainer->objectFunctionsEnd(obj); function != end; ++function) { quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; const QString slotName = stringAt(function->nameIndex); diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index e6a71c6e15..67cd969d82 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -122,7 +122,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() } { - QQmlPropertyCacheCreator propertyCacheBuilder(&m_propertyCaches, engine, this, imports()); + QQmlPropertyCacheCreator propertyCacheBuilder(&m_propertyCaches, engine, this, imports()); QQmlCompileError error = propertyCacheBuilder.buildMetaObjects(); if (error.isSet()) { recordError(error); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 71d2d2337e..f00a940808 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -92,6 +92,9 @@ public: QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document); // --- interface used by QQmlPropertyCacheCreator + typedef QmlIR::Object CompiledObject; + const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); } + int objectCount() const { return document->objects.count(); } QString stringAt(int idx) const; QmlIR::PoolList::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } QmlIR::PoolList::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 34b6b96ebe..6b2e0d6d9b 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -72,7 +72,7 @@ class QQmlAccessors; class QMetaObjectBuilder; class QQmlPropertyCacheMethodArguments; class QQmlVMEMetaObject; -class QQmlPropertyCacheCreator; +template class QQmlPropertyCacheCreator; // We have this somewhat awful split between RawData and Data so that RawData can be // used in unions. In normal code, you should always use Data which initializes RawData @@ -337,7 +337,7 @@ protected: private: friend class QQmlEnginePrivate; friend class QQmlCompiler; - friend class QQmlPropertyCacheCreator; + template friend class QQmlPropertyCacheCreator; friend class QQmlComponentAndAliasResolver; friend class QQmlMetaObject; -- cgit v1.2.3 From 1591d0e0f62a48d2ef45cc8af9b5d39158e169d5 Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Thu, 16 Jun 2016 04:30:49 -0700 Subject: Fix reference to Windows RT which actually meant Windows Runtime. Change-Id: I701b25bc73cf84b1daae481eebfd2dc19003ce78 Reviewed-by: Maurice Kalinowski --- src/qml/qml/qqmlengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 2cfe468134..9a06f6b055 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -402,7 +402,7 @@ The following functions are also on the Qt object. \li \c "unix" - Other Unix-based OS \li \c "windows" - Windows \li \c "wince" - Windows CE - \li \c "winrt" - Windows RT + \li \c "winrt" - Windows Runtime \li \c "winphone" - Windows Phone \endlist \endtable -- cgit v1.2.3 From 2997b7ea99906801c9cb92a7e095c8044c86dc0a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 13:22:27 +0200 Subject: Added iterator interfaces to QV4::CompiledData::* These interfaces are required in order to be able to use the QQmlPropertyCacheCreator template. Change-Id: I371e7102aac00fd607fc6a8ad7ffc18996608758 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata_p.h | 67 ++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index b24d93d995..fb82b2bcbb 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -95,6 +95,19 @@ struct Lookup; struct RegExp; struct Unit; +template +struct TableIterator +{ + TableIterator(const Container *container, int index) : container(container), index(index) {} + const Container *container; + int index; + + const ItemType *operator->() { return (container->*IndexedGetter)(index); } + void operator++() { ++index; } + bool operator==(const TableIterator &rhs) const { return index == rhs.index; } + bool operator!=(const TableIterator &rhs) const { return index != rhs.index; } +}; + #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) #pragma pack(push, 1) #endif @@ -205,6 +218,11 @@ struct Function const quint32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingContextPropertiesOffset); } const quint32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingScopePropertiesOffset); } + // --- QQmlPropertyCacheCreator interface + const quint32 *formalsBegin() const { return formalsTable(); } + const quint32 *formalsEnd() const { return formalsTable() + nFormals; } + // --- + inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; } static int calculateSize(int nFormals, int nLocals, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies) { @@ -355,6 +373,12 @@ struct Signal + nParameters * sizeof(Parameter) + 7) & ~0x7; } + + // --- QQmlPropertyCacheCceatorInterface + const Parameter *parametersBegin() const { return parameterAt(0); } + const Parameter *parametersEnd() const { return parameterAt(nParameters); } + int parameterCount() const { return nParameters; } + // --- }; struct Property @@ -481,6 +505,26 @@ struct Object { return reinterpret_cast(reinterpret_cast(this) + offsetToNamedObjectsInComponent); } + + // --- QQmlPropertyCacheCreator interface + int propertyCount() const { return nProperties; } + int aliasCount() const { return nAliases; } + int signalCount() const { return nSignals; } + int functionCount() const { return nFunctions; } + + const Binding *bindingsBegin() const { return bindingTable(); } + const Binding *bindingsEnd() const { return bindingTable() + nBindings; } + + const Property *propertiesBegin() const { return propertyTable(); } + const Property *propertiesEnd() const { return propertyTable() + nProperties; } + + const Alias *aliasesBegin() const { return aliasTable(); } + const Alias *aliasesEnd() const { return aliasTable() + nAliases; } + + typedef TableIterator SignalIterator; + SignalIterator signalsBegin() const { return SignalIterator(this, 0); } + SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); } + // --- }; struct Import @@ -754,6 +798,29 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount bool isRegisteredWithEngine; + // --- interface for QQmlPropertyCacheCreator + typedef Object CompiledObject; + int objectCount() const { return data->nObjects; } + int rootObjectIndex() const { return data->indexOfRootObject; } + const Object *objectAt(int index) const { return data->objectAt(index); } + QString stringAt(int index) const { return data->stringAt(index); } + + struct FunctionIterator + { + FunctionIterator(const Unit *unit, const Object *object, int index) : unit(unit), object(object), index(index) {} + const Unit *unit; + const Object *object; + int index; + + const Function *operator->() const { return unit->functionAt(object->functionOffsetTable()[index]); } + void operator++() { ++index; } + bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; } + bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; } + }; + FunctionIterator objectFunctionsBegin(const Object *object) const { return FunctionIterator(data, object, 0); } + FunctionIterator objectFunctionsEnd(const Object *object) const { return FunctionIterator(data, object, object->nFunctions); } + // --- + QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); void unlink(); -- cgit v1.2.3 From 88af61180be2636e9b2f0714bed448d7d9270adc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 16:00:39 +0200 Subject: Minor QQmlPropertyValidator/QQmlCustomParser cleanup The QQmlCustomParser already has an imports bi-pointer, so there's no need for the property validator to implement an interface anymore. Change-Id: Iabe9810ff26afdbc0c51efde3b20383c8f4f2348 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 18 +++++++++--------- src/qml/compiler/qqmltypecompiler_p.h | 5 +---- src/qml/qml/qqmlcustomparser.cpp | 8 +++++--- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 67cd969d82..19c221c766 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -1336,9 +1336,9 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, const QQmlPropertyCacheVector *propertyCaches) : QQmlCompilePass(typeCompiler) , enginePrivate(typeCompiler->enginePrivate()) + , imports(*typeCompiler->imports()) , qmlUnit(typeCompiler->qmlUnit()) , resolvedTypes(typeCompiler->resolvedTypes) - , customParsers(typeCompiler->customParserCache()) , propertyCaches(propertyCaches) { } @@ -1352,11 +1352,6 @@ bool QQmlPropertyValidator::validate() return true; } -const QQmlImports &QQmlPropertyValidator::imports() const -{ - return *compiler->imports(); -} - typedef QVarLengthArray GroupPropertyVector; struct BindingFinder @@ -1400,7 +1395,12 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD } } - QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex); + QQmlCustomParser *customParser = 0; + if (auto typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { + if (typeRef->type) + customParser = typeRef->type->customParser(); + } + QList customBindings; // Collect group properties first for sanity checking @@ -1491,7 +1491,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD if (name.constData()->isUpper() && !binding->isAttachedProperty()) { QQmlType *type = 0; QQmlImportNamespace *typeNamespace = 0; - compiler->imports()->resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace); + imports.resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace); if (typeNamespace) recordError(binding->location, tr("Invalid use of namespace")); else @@ -1606,7 +1606,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD customParser->clearErrors(); customParser->validator = this; customParser->engine = enginePrivate; - customParser->imports = compiler->imports(); + customParser->imports = &imports; customParser->verifyBindings(qmlUnit, customBindings); customParser->validator = 0; customParser->engine = 0; diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index f00a940808..f0909d56fa 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -307,9 +307,6 @@ public: bool validate(); - const QQmlImports &imports() const; - QQmlEnginePrivate *engine() const { return enginePrivate; } - private: bool validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; bool validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; @@ -318,9 +315,9 @@ private: bool canCoerce(int to, QQmlPropertyCache *fromMo) const; QQmlEnginePrivate *enginePrivate; + const QQmlImports &imports; const QV4::CompiledData::Unit *qmlUnit; const QHash &resolvedTypes; - const QHash &customParsers; const QQmlPropertyCacheVector * const propertyCaches; // collected state variables, essentially write-only diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index e9f747a440..30eed4d551 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -165,11 +165,13 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const */ const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const { + if (!imports.isT1()) + return nullptr; QQmlType *qmltype = 0; - if (!validator->imports().resolveType(name, &qmltype, 0, 0, 0)) - return 0; + if (!imports.asT1()->resolveType(name, &qmltype, 0, 0, 0)) + return nullptr; if (!qmltype) - return 0; + return nullptr; return qmltype->metaObject(); } -- cgit v1.2.3 From b75988f57498436e1e4417324172f73cb0fc3b80 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 16:18:21 +0200 Subject: Reduce dependency of QQmlPropertyValidator on QQmlTypeCompiler This is in preparation for future use beyond the type compiler when loading compilation units off disk. Change-Id: Ic1ea89518cec32b336153ecf32641fc7f8b7db0c Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 30 ++++++++++++++---------------- src/qml/compiler/qqmltypecompiler_p.h | 7 +++---- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 19c221c766..d36c7f21ac 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -252,7 +252,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() } // Sanity check property bindings - QQmlPropertyValidator validator(this, &compilationUnit->propertyCaches); + QQmlPropertyValidator validator(this, engine, *imports(), compilationUnit); if (!validator.validate()) return nullptr; @@ -1333,23 +1333,21 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) } -QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, const QQmlPropertyCacheVector *propertyCaches) +QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit) : QQmlCompilePass(typeCompiler) - , enginePrivate(typeCompiler->enginePrivate()) - , imports(*typeCompiler->imports()) - , qmlUnit(typeCompiler->qmlUnit()) - , resolvedTypes(typeCompiler->resolvedTypes) - , propertyCaches(propertyCaches) + , enginePrivate(enginePrivate) + , imports(imports) + , qmlUnit(compilationUnit->data) + , resolvedTypes(compilationUnit->resolvedTypes) + , propertyCaches(compilationUnit->propertyCaches) + , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject) { + bindingPropertyDataPerObject->resize(qmlUnit->nObjects); } bool QQmlPropertyValidator::validate() { - _bindingPropertyDataPerObject.resize(qmlUnit->nObjects); - if (!validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0)) - return false; - compiler->setBindingPropertyDataPerObject(_bindingPropertyDataPerObject); - return true; + return validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0); } typedef QVarLengthArray GroupPropertyVector; @@ -1381,7 +1379,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD return validateObject(componentBinding->value.objectIndex, componentBinding); } - QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); + QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); if (!propertyCache) return true; @@ -1619,7 +1617,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD } } - _bindingPropertyDataPerObject[objectIndex] = collectedBindingPropertyData; + (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; return true; } @@ -1955,7 +1953,7 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co } else if (property->isQList()) { const int listType = enginePrivate->listType(property->propType); if (!QQmlMetaType::isInterface(listType)) { - QQmlPropertyCache *source = propertyCaches->at(binding->value.objectIndex); + QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); if (!canCoerce(listType, source)) { recordError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); return false; @@ -1982,7 +1980,7 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co bool isAssignable = false; // Determine isAssignable value if (propertyMetaObject) { - QQmlPropertyCache *c = propertyCaches->at(binding->value.objectIndex); + QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); while (c && !isAssignable) { isAssignable |= c == propertyMetaObject; c = c->parent(); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index f0909d56fa..17125a69f0 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -303,7 +303,7 @@ class QQmlPropertyValidator : public QQmlCompilePass { Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) public: - QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, const QQmlPropertyCacheVector *propertyCaches); + QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit); bool validate(); @@ -318,10 +318,9 @@ private: const QQmlImports &imports; const QV4::CompiledData::Unit *qmlUnit; const QHash &resolvedTypes; - const QQmlPropertyCacheVector * const propertyCaches; + const QQmlPropertyCacheVector &propertyCaches; - // collected state variables, essentially write-only - mutable QVector _bindingPropertyDataPerObject; + QVector * const bindingPropertyDataPerObject; }; // ### merge with QtQml::JSCodeGen and operate directly on object->functionsAndExpressions once old compiler is gone. -- cgit v1.2.3 From 8c10d12be1a219a6bd5bd54499b281551c79c823 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 16:49:25 +0200 Subject: Remove remaining dependency of QQmlPropertyValidator on QQmlTypeCompiler Instead we can get most of the data from QV4::CompilationUnit - which is always ready at that point in time. And errors are now returned explicitly in the API. Change-Id: I6acbeb550661f88f7fad09e97f78a1a4cbd77568 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 247 +++++++++++++++------------------- src/qml/compiler/qqmltypecompiler_p.h | 16 ++- src/qml/qml/qqmlcustomparser.cpp | 6 +- src/qml/qml/qqmlcustomparser_p.h | 5 +- 4 files changed, 126 insertions(+), 148 deletions(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index d36c7f21ac..809f0033e9 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -251,10 +251,16 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() } } + { // Sanity check property bindings - QQmlPropertyValidator validator(this, engine, *imports(), compilationUnit); - if (!validator.validate()) - return nullptr; + QQmlPropertyValidator validator(engine, *imports(), compilationUnit); + QVector errors = validator.validate(); + if (!errors.isEmpty()) { + for (const QQmlCompileError &error: qAsConst(errors)) + recordError(error); + return nullptr; + } + } // Collect some data for instantiation later. int bindingCount = 0; @@ -1333,9 +1339,8 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) } -QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit) - : QQmlCompilePass(typeCompiler) - , enginePrivate(enginePrivate) +QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit) + : enginePrivate(enginePrivate) , imports(imports) , qmlUnit(compilationUnit->data) , resolvedTypes(compilationUnit->resolvedTypes) @@ -1345,7 +1350,7 @@ QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, QQm bindingPropertyDataPerObject->resize(qmlUnit->nObjects); } -bool QQmlPropertyValidator::validate() +QVector QQmlPropertyValidator::validate() { return validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0); } @@ -1368,7 +1373,7 @@ struct BindingFinder } }; -bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const +QVector QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); @@ -1381,7 +1386,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); if (!propertyCache) - return true; + return QVector(); QStringList deferredPropertyNames; { @@ -1413,8 +1418,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD continue; if (populatingValueTypeGroupProperty) { - recordError(binding->location, tr("Property assignment expected")); - return false; + return recordError(binding->location, tr("Property assignment expected")); } GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); @@ -1469,14 +1473,14 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD QString typeName = stringAt(obj->inheritedTypeNameIndex); auto *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex); if (objectType && objectType->type) { - COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); + return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); } else { - COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); + return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); } } } else { if (isGroupProperty) - COMPILE_EXCEPTION(binding, tr("Cannot assign a value directly to a grouped property")); + return recordError(binding->location, tr("Cannot assign a value directly to a grouped property")); pd = defaultProperty; name = defaultPropertyName; @@ -1491,16 +1495,14 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD QQmlImportNamespace *typeNamespace = 0; imports.resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace); if (typeNamespace) - recordError(binding->location, tr("Invalid use of namespace")); - else - recordError(binding->location, tr("Invalid attached object assignment")); - return false; + return recordError(binding->location, tr("Invalid use of namespace")); + return recordError(binding->location, tr("Invalid attached object assignment")); } if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { - const bool subObjectValid = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)); - if (!subObjectValid) - return false; + const QVector subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)); + if (!subObjectValidatorErrors.isEmpty()) + return subObjectValidatorErrors; } // Signal handlers were resolved and checked earlier in the signal handler conversion pass. @@ -1510,8 +1512,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) { - recordError(binding->location, tr("Attached properties cannot be used here")); - return false; + return recordError(binding->location, tr("Attached properties cannot be used here")); } continue; } @@ -1527,10 +1528,8 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD ) { if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object) - recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); - else - recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); - return false; + return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); + return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); } if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { @@ -1539,8 +1538,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD error = tr( "Cannot assign multiple values to a script property"); else error = tr( "Cannot assign multiple values to a singular property"); - recordError(binding->valueLocation, error); - return false; + return recordError(binding->valueLocation, error); } if (!bindingToDefaultProperty @@ -1552,33 +1550,30 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD loc = (*assignedGroupProperty)->valueLocation; if (pd && QQmlValueTypeFactory::isValueType(pd->propType)) - recordError(loc, tr("Property has already been assigned a value")); - else - recordError(loc, tr("Cannot assign a value directly to a grouped property")); - return false; + return recordError(loc, tr("Property has already been assigned a value")); + return recordError(loc, tr("Cannot assign a value directly to a grouped property")); } if (binding->type < QV4::CompiledData::Binding::Type_Script) { - if (!validateLiteralBinding(propertyCache, pd, binding)) - return false; + QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding); + if (bindingError.isSet()) + return recordError(bindingError); } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { - if (!validateObjectBinding(pd, name, binding)) - return false; + QQmlCompileError bindingError = validateObjectBinding(pd, name, binding); + if (bindingError.isSet()) + return recordError(bindingError); } else if (binding->isGroupProperty()) { if (QQmlValueTypeFactory::isValueType(pd->propType)) { if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)) { if (!pd->isWritable()) { - recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); - return false; + return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); } } else { - recordError(binding->location, tr("Invalid grouped property access")); - return false; + return recordError(binding->location, tr("Invalid grouped property access")); } } else { if (!enginePrivate->propertyCacheForType(pd->propType)) { - recordError(binding->location, tr("Invalid grouped property access")); - return false; + return recordError(binding->location, tr("Invalid grouped property access")); } } } @@ -1588,9 +1583,9 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD continue; } if (bindingToDefaultProperty) { - COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent default property")); + return recordError(binding->location, tr("Cannot assign to non-existent default property")); } else { - COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent property \"%1\"").arg(name)); + return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name)); } } } @@ -1609,29 +1604,28 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD customParser->validator = 0; customParser->engine = 0; customParser->imports = (QQmlImports*)0; - const QList parserErrors = customParser->errors(); - if (!parserErrors.isEmpty()) { - foreach (const QQmlError &error, parserErrors) - compiler->recordError(error); - return false; - } + QVector parserErrors = customParser->errors(); + if (!parserErrors.isEmpty()) + return parserErrors; } (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; - return true; + QVector noError; + return noError; } -bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const +QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const { if (property->isQList()) { - recordError(binding->valueLocation, tr("Cannot assign primitives to lists")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists")); } + QQmlCompileError noError; + if (property->isEnum()) { if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) - return true; + return noError; QString value = binding->valueAsString(qmlUnit); QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex); @@ -1642,10 +1636,9 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa p.enumerator().keyToValue(value.toUtf8().constData(), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); } - return true; + return noError; } switch (property->propType) { @@ -1653,29 +1646,25 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa break; case QVariant::String: { if (!binding->evaluatesToString()) { - recordError(binding->valueLocation, tr("Invalid property assignment: string expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string expected")); } } break; case QVariant::StringList: { if (!binding->evaluatesToString()) { - recordError(binding->valueLocation, tr("Invalid property assignment: string or string list expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or string list expected")); } } break; case QVariant::ByteArray: { if (binding->type != QV4::CompiledData::Binding::Type_String) { - recordError(binding->valueLocation, tr("Invalid property assignment: byte array expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: byte array expected")); } } break; case QVariant::Url: { if (binding->type != QV4::CompiledData::Binding::Type_String) { - recordError(binding->valueLocation, tr("Invalid property assignment: url expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url expected")); } } break; @@ -1683,33 +1672,29 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa if (binding->type == QV4::CompiledData::Binding::Type_Number) { double d = binding->valueAsNumber(); if (double(uint(d)) == d) - return true; + return noError; } - recordError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected")); } break; case QVariant::Int: { if (binding->type == QV4::CompiledData::Binding::Type_Number) { double d = binding->valueAsNumber(); if (double(int(d)) == d) - return true; + return noError; } - recordError(binding->valueLocation, tr("Invalid property assignment: int expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int expected")); } break; case QMetaType::Float: { if (binding->type != QV4::CompiledData::Binding::Type_Number) { - recordError(binding->valueLocation, tr("Invalid property assignment: number expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected")); } } break; case QVariant::Double: { if (binding->type != QV4::CompiledData::Binding::Type_Number) { - recordError(binding->valueLocation, tr("Invalid property assignment: number expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected")); } } break; @@ -1717,8 +1702,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa bool ok = false; QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: color expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: color expected")); } } break; @@ -1727,8 +1711,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa bool ok = false; QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: date expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: date expected")); } } break; @@ -1736,8 +1719,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa bool ok = false; QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: time expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: time expected")); } } break; @@ -1745,8 +1727,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa bool ok = false; QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: datetime expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: datetime expected")); } } break; @@ -1755,8 +1736,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa bool ok = false; QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: point expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); } } break; @@ -1764,8 +1744,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa bool ok = false; QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: point expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); } } break; @@ -1773,8 +1752,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa bool ok = false; QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: size expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected")); } } break; @@ -1782,8 +1760,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa bool ok = false; QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: size expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected")); } } break; @@ -1791,8 +1768,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa bool ok = false; QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: rect expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: rect expected")); } } break; @@ -1800,15 +1776,13 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa bool ok = false; QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: point expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); } } break; case QVariant::Bool: { if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - recordError(binding->valueLocation, tr("Invalid property assignment: boolean expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: boolean expected")); } } break; @@ -1819,8 +1793,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa float zy; } vec; if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { - recordError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected")); } } break; @@ -1832,20 +1805,17 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa float wp; } vec; if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { - recordError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected")); } } break; case QVariant::RegExp: - recordError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); default: { // generate single literal value assignment to a list property if required if (property->propType == qMetaTypeId >()) { if (binding->type != QV4::CompiledData::Binding::Type_Number) { - recordError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected")); } break; } else if (property->propType == qMetaTypeId >()) { @@ -1856,24 +1826,21 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa ok = false; } if (!ok) - recordError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected")); + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected")); break; } else if (property->propType == qMetaTypeId >()) { if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - recordError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected")); } break; } else if (property->propType == qMetaTypeId >()) { if (binding->type != QV4::CompiledData::Binding::Type_String) { - recordError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected")); } break; } else if (property->propType == qMetaTypeId >()) { if (!binding->evaluatesToString()) { - recordError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected")); } break; } else if (property->propType == qMetaTypeId()) { @@ -1885,13 +1852,12 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa // otherwise, try a custom type assignment QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); if (!converter) { - recordError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); } } break; } - return true; + return noError; } /*! @@ -1910,8 +1876,24 @@ bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const return false; } -bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const +QVector QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const { + QVector errors; + errors.append(QQmlCompileError(location, description)); + return errors; +} + +QVector QQmlPropertyValidator::recordError(const QQmlCompileError &error) const +{ + QVector errors; + errors.append(error); + return errors; +} + +QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const +{ + QQmlCompileError noError; + if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); @@ -1936,40 +1918,36 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co } if (!isValueSource && !isPropertyInterceptor) { - recordError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); - return false; + return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); } - return true; + return noError; } if (QQmlMetaType::isInterface(property->propType)) { // Can only check at instantiation time if the created sub-object successfully casts to the // target interface. - return true; + return noError; } else if (property->propType == QMetaType::QVariant) { // We can convert everything to QVariant :) - return true; + return noError; } else if (property->isQList()) { const int listType = enginePrivate->listType(property->propType); if (!QQmlMetaType::isInterface(listType)) { QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); if (!canCoerce(listType, source)) { - recordError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); - return false; + return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); } } - return true; + return noError; } else if (qmlUnit->objectAt(binding->value.objectIndex)->flags & QV4::CompiledData::Object::IsComponent) { - return true; + return noError; } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { - return true; + return noError; } else if (QQmlValueTypeFactory::isValueType(property->propType)) { - recordError(binding->location, tr("Unexpected object assignment")); - return false; + return QQmlCompileError(binding->location, tr("Unexpected object assignment")); } else if (property->propType == qMetaTypeId()) { - recordError(binding->valueLocation, tr("Invalid property assignment: script expected")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); } else { // We want to raw metaObject here as the raw metaobject is the // actual property type before we applied any extensions that might @@ -1988,11 +1966,10 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co } if (!isAssignable) { - recordError(binding->valueLocation, tr("Cannot assign object to property")); - return false; + return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property")); } } - return true; + return noError; } QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen) diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 17125a69f0..6fddd37111 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -299,21 +299,25 @@ private: bool _seenObjectWithId; }; -class QQmlPropertyValidator : public QQmlCompilePass +class QQmlPropertyValidator { Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) public: - QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit); + QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit); - bool validate(); + QVector validate(); private: - bool validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; - bool validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; - bool validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const; + QVector validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; + QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; + QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const; bool canCoerce(int to, QQmlPropertyCache *fromMo) const; + QVector recordError(const QV4::CompiledData::Location &location, const QString &description) const Q_REQUIRED_RESULT; + QVector recordError(const QQmlCompileError &error) const Q_REQUIRED_RESULT; + QString stringAt(int index) const { return qmlUnit->stringAt(index); } + QQmlEnginePrivate *enginePrivate; const QQmlImports &imports; const QV4::CompiledData::Unit *qmlUnit; diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index 30eed4d551..85c91a592a 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -100,11 +100,7 @@ void QQmlCustomParser::clearErrors() */ void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description) { - QQmlError error; - error.setLine(location.line); - error.setColumn(location.column); - error.setDescription(description); - exceptions << error; + exceptions << QQmlCompileError(location, description); } struct StaticQtMetaObject : public QObject diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index d1ed61defe..5eb409990d 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -54,6 +54,7 @@ #include "qqmlmetatype_p.h" #include "qqmlerror.h" #include "qqmlbinding_p.h" +#include #include #include @@ -83,7 +84,7 @@ public: virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList &) = 0; virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList &) = 0; - QList errors() const { return exceptions; } + QVector errors() const { return exceptions; } protected: void error(const QV4::CompiledData::Binding *binding, const QString& description) @@ -97,7 +98,7 @@ protected: const QMetaObject *resolveType(const QString&) const; private: - QList exceptions; + QVector exceptions; QQmlEnginePrivate *engine; const QQmlPropertyValidator *validator; Flags m_flags; -- cgit v1.2.3 From 6b60de934bef14b17c05e726cea05b0bef8b8004 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Jun 2016 16:56:56 +0200 Subject: Separate QQmlPropertyValidator into a standalone file The type compiler file is getting crowded and this class is destined to be used outside of the scope of the type compiler. Change-Id: I21aff8ee64dbfeb039523518e912ef206637fb41 Reviewed-by: Lars Knoll --- src/qml/compiler/compiler.pri | 6 +- src/qml/compiler/qqmlpropertyvalidator.cpp | 680 +++++++++++++++++++++++++++++ src/qml/compiler/qqmlpropertyvalidator_p.h | 87 ++++ src/qml/compiler/qqmltypecompiler.cpp | 636 +-------------------------- src/qml/compiler/qqmltypecompiler_p.h | 28 -- 5 files changed, 772 insertions(+), 665 deletions(-) create mode 100644 src/qml/compiler/qqmlpropertyvalidator.cpp create mode 100644 src/qml/compiler/qqmlpropertyvalidator_p.h diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 3bcac65f81..ad73c26b15 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -27,13 +27,15 @@ HEADERS += \ $$PWD/qqmltypecompiler_p.h \ $$PWD/qv4isel_moth_p.h \ $$PWD/qv4instr_moth_p.h \ - $$PWD/qqmlpropertycachecreator_p.h + $$PWD/qqmlpropertycachecreator_p.h \ + $$PWD/qqmlpropertyvalidator_p.h SOURCES += \ $$PWD/qqmltypecompiler.cpp \ $$PWD/qv4instr_moth.cpp \ $$PWD/qv4isel_moth.cpp \ - $$PWD/qqmlpropertycachecreator.cpp + $$PWD/qqmlpropertycachecreator.cpp \ + $$PWD/qqmlpropertyvalidator.cpp } diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp new file mode 100644 index 0000000000..dd44a49896 --- /dev/null +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -0,0 +1,680 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qqmlpropertyvalidator_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit) + : enginePrivate(enginePrivate) + , imports(imports) + , qmlUnit(compilationUnit->data) + , resolvedTypes(compilationUnit->resolvedTypes) + , propertyCaches(compilationUnit->propertyCaches) + , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject) +{ + bindingPropertyDataPerObject->resize(qmlUnit->nObjects); +} + +QVector QQmlPropertyValidator::validate() +{ + return validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0); +} + +typedef QVarLengthArray GroupPropertyVector; + +struct BindingFinder +{ + bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const + { + return name < binding->propertyNameIndex; + } + bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const + { + return binding->propertyNameIndex < name; + } + bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const + { + return lhs->propertyNameIndex < rhs->propertyNameIndex; + } +}; + +QVector QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const +{ + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); + + if (obj->flags & QV4::CompiledData::Object::IsComponent) { + Q_ASSERT(obj->nBindings == 1); + const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); + Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + return validateObject(componentBinding->value.objectIndex, componentBinding); + } + + QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); + if (!propertyCache) + return QVector(); + + QStringList deferredPropertyNames; + { + const QMetaObject *mo = propertyCache->firstCppMetaObject(); + const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); + if (namesIndex != -1) { + QMetaClassInfo classInfo = mo->classInfo(namesIndex); + deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + } + } + + QQmlCustomParser *customParser = 0; + if (auto typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { + if (typeRef->type) + customParser = typeRef->type->customParser(); + } + + QList customBindings; + + // Collect group properties first for sanity checking + // vector values are sorted by property name string index. + GroupPropertyVector groupProperties; + const QV4::CompiledData::Binding *binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + if (!binding->isGroupProperty()) + continue; + + if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + continue; + + if (populatingValueTypeGroupProperty) { + return recordError(binding->location, tr("Property assignment expected")); + } + + GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); + groupProperties.insert(pos, binding); + } + + QmlIR::PropertyResolver propertyResolver(propertyCache); + + QString defaultPropertyName; + QQmlPropertyData *defaultProperty = 0; + if (obj->indexOfDefaultPropertyOrAlias != -1) { + QQmlPropertyCache *cache = propertyCache->parent(); + defaultPropertyName = cache->defaultPropertyName(); + defaultProperty = cache->defaultProperty(); + } else { + defaultPropertyName = propertyCache->defaultPropertyName(); + defaultProperty = propertyCache->defaultProperty(); + } + + QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings); + + binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + QString name = stringAt(binding->propertyNameIndex); + + if (customParser) { + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { + customBindings << binding; + continue; + } + } else if (QmlIR::IRBuilder::isSignalPropertyName(name) + && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { + customBindings << binding; + continue; + } + } + + bool bindingToDefaultProperty = false; + bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty; + + bool notInRevision = false; + QQmlPropertyData *pd = 0; + if (!name.isEmpty()) { + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + pd = propertyResolver.signal(name, ¬InRevision); + else + pd = propertyResolver.property(name, ¬InRevision, isGroupProperty ? QmlIR::PropertyResolver::IgnoreRevision : QmlIR::PropertyResolver::CheckRevision); + + if (notInRevision) { + QString typeName = stringAt(obj->inheritedTypeNameIndex); + auto *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex); + if (objectType && objectType->type) { + return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); + } else { + return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); + } + } + } else { + if (isGroupProperty) + return recordError(binding->location, tr("Cannot assign a value directly to a grouped property")); + + pd = defaultProperty; + name = defaultPropertyName; + bindingToDefaultProperty = true; + } + + if (pd) + collectedBindingPropertyData[i] = pd; + + if (name.constData()->isUpper() && !binding->isAttachedProperty()) { + QQmlType *type = 0; + QQmlImportNamespace *typeNamespace = 0; + imports.resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace); + if (typeNamespace) + return recordError(binding->location, tr("Invalid use of namespace")); + return recordError(binding->location, tr("Invalid attached object assignment")); + } + + if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { + const QVector subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)); + if (!subObjectValidatorErrors.isEmpty()) + return subObjectValidatorErrors; + } + + // Signal handlers were resolved and checked earlier in the signal handler conversion pass. + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + continue; + + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) { + return recordError(binding->location, tr("Attached properties cannot be used here")); + } + continue; + } + + if (pd) { + GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); + const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex); + + if (!pd->isWritable() + && !pd->isQList() + && !binding->isGroupProperty() + && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) + ) { + + if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object) + return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); + return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); + } + + if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { + QString error; + if (pd->propType == qMetaTypeId()) + error = tr( "Cannot assign multiple values to a script property"); + else + error = tr( "Cannot assign multiple values to a singular property"); + return recordError(binding->valueLocation, error); + } + + if (!bindingToDefaultProperty + && !binding->isGroupProperty() + && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + && assigningToGroupProperty) { + QV4::CompiledData::Location loc = binding->valueLocation; + if (loc < (*assignedGroupProperty)->valueLocation) + loc = (*assignedGroupProperty)->valueLocation; + + if (pd && QQmlValueTypeFactory::isValueType(pd->propType)) + return recordError(loc, tr("Property has already been assigned a value")); + return recordError(loc, tr("Cannot assign a value directly to a grouped property")); + } + + if (binding->type < QV4::CompiledData::Binding::Type_Script) { + QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding); + if (bindingError.isSet()) + return recordError(bindingError); + } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { + QQmlCompileError bindingError = validateObjectBinding(pd, name, binding); + if (bindingError.isSet()) + return recordError(bindingError); + } else if (binding->isGroupProperty()) { + if (QQmlValueTypeFactory::isValueType(pd->propType)) { + if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)) { + if (!pd->isWritable()) { + return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); + } + } else { + return recordError(binding->location, tr("Invalid grouped property access")); + } + } else { + if (!enginePrivate->propertyCacheForType(pd->propType)) { + return recordError(binding->location, tr("Invalid grouped property access")); + } + } + } + } else { + if (customParser) { + customBindings << binding; + continue; + } + if (bindingToDefaultProperty) { + return recordError(binding->location, tr("Cannot assign to non-existent default property")); + } else { + return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name)); + } + } + } + + if (obj->idNameIndex) { + bool notInRevision = false; + collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), ¬InRevision); + } + + if (customParser && !customBindings.isEmpty()) { + customParser->clearErrors(); + customParser->validator = this; + customParser->engine = enginePrivate; + customParser->imports = &imports; + customParser->verifyBindings(qmlUnit, customBindings); + customParser->validator = 0; + customParser->engine = 0; + customParser->imports = (QQmlImports*)0; + QVector parserErrors = customParser->errors(); + if (!parserErrors.isEmpty()) + return parserErrors; + } + + (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; + + QVector noError; + return noError; +} + +QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const +{ + if (property->isQList()) { + return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists")); + } + + QQmlCompileError noError; + + if (property->isEnum()) { + if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) + return noError; + + QString value = binding->valueAsString(qmlUnit); + QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex); + bool ok; + if (p.isFlagType()) { + p.enumerator().keysToValue(value.toUtf8().constData(), &ok); + } else + p.enumerator().keyToValue(value.toUtf8().constData(), &ok); + + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); + } + return noError; + } + + switch (property->propType) { + case QMetaType::QVariant: + break; + case QVariant::String: { + if (!binding->evaluatesToString()) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string expected")); + } + } + break; + case QVariant::StringList: { + if (!binding->evaluatesToString()) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or string list expected")); + } + } + break; + case QVariant::ByteArray: { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: byte array expected")); + } + } + break; + case QVariant::Url: { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url expected")); + } + } + break; + case QVariant::UInt: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double d = binding->valueAsNumber(); + if (double(uint(d)) == d) + return noError; + } + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected")); + } + break; + case QVariant::Int: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double d = binding->valueAsNumber(); + if (double(int(d)) == d) + return noError; + } + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int expected")); + } + break; + case QMetaType::Float: { + if (binding->type != QV4::CompiledData::Binding::Type_Number) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected")); + } + } + break; + case QVariant::Double: { + if (binding->type != QV4::CompiledData::Binding::Type_Number) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected")); + } + } + break; + case QVariant::Color: { + bool ok = false; + QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: color expected")); + } + } + break; +#ifndef QT_NO_DATESTRING + case QVariant::Date: { + bool ok = false; + QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: date expected")); + } + } + break; + case QVariant::Time: { + bool ok = false; + QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: time expected")); + } + } + break; + case QVariant::DateTime: { + bool ok = false; + QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: datetime expected")); + } + } + break; +#endif // QT_NO_DATESTRING + case QVariant::Point: { + bool ok = false; + QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::PointF: { + bool ok = false; + QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::Size: { + bool ok = false; + QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected")); + } + } + break; + case QVariant::SizeF: { + bool ok = false; + QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected")); + } + } + break; + case QVariant::Rect: { + bool ok = false; + QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: rect expected")); + } + } + break; + case QVariant::RectF: { + bool ok = false; + QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::Bool: { + if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: boolean expected")); + } + } + break; + case QVariant::Vector3D: { + struct { + float xp; + float yp; + float zy; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected")); + } + } + break; + case QVariant::Vector4D: { + struct { + float xp; + float yp; + float zy; + float wp; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected")); + } + } + break; + case QVariant::RegExp: + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); + default: { + // generate single literal value assignment to a list property if required + if (property->propType == qMetaTypeId >()) { + if (binding->type != QV4::CompiledData::Binding::Type_Number) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected")); + } + break; + } else if (property->propType == qMetaTypeId >()) { + bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); + if (ok) { + double n = binding->valueAsNumber(); + if (double(int(n)) != n) + ok = false; + } + if (!ok) + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected")); + break; + } else if (property->propType == qMetaTypeId >()) { + if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected")); + } + break; + } else if (property->propType == qMetaTypeId >()) { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected")); + } + break; + } else if (property->propType == qMetaTypeId >()) { + if (!binding->evaluatesToString()) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected")); + } + break; + } else if (property->propType == qMetaTypeId()) { + break; + } else if (property->propType == qMetaTypeId()) { + break; + } + + // otherwise, try a custom type assignment + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); + if (!converter) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); + } + } + break; + } + return noError; +} + +/*! + Returns true if from can be assigned to a (QObject) property of type + to. +*/ +bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const +{ + QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); + + while (fromMo) { + if (fromMo == toMo) + return true; + fromMo = fromMo->parent(); + } + return false; +} + +QVector QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const +{ + QVector errors; + errors.append(QQmlCompileError(location, description)); + return errors; +} + +QVector QQmlPropertyValidator::recordError(const QQmlCompileError &error) const +{ + QVector errors; + errors.append(error); + return errors; +} + +QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const +{ + QQmlCompileError noError; + + if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); + + bool isValueSource = false; + bool isPropertyInterceptor = false; + + QQmlType *qmlType = 0; + const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex); + if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) { + QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + const QMetaObject *mo = cache->firstCppMetaObject(); + while (mo && !qmlType) { + qmlType = QQmlMetaType::qmlType(mo); + mo = mo->superClass(); + } + Q_ASSERT(qmlType); + } + + if (qmlType) { + isValueSource = qmlType->propertyValueSourceCast() != -1; + isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1; + } + + if (!isValueSource && !isPropertyInterceptor) { + return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); + } + + return noError; + } + + if (QQmlMetaType::isInterface(property->propType)) { + // Can only check at instantiation time if the created sub-object successfully casts to the + // target interface. + return noError; + } else if (property->propType == QMetaType::QVariant) { + // We can convert everything to QVariant :) + return noError; + } else if (property->isQList()) { + const int listType = enginePrivate->listType(property->propType); + if (!QQmlMetaType::isInterface(listType)) { + QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); + if (!canCoerce(listType, source)) { + return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); + } + } + return noError; + } else if (qmlUnit->objectAt(binding->value.objectIndex)->flags & QV4::CompiledData::Object::IsComponent) { + return noError; + } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { + return noError; + } else if (QQmlValueTypeFactory::isValueType(property->propType)) { + return QQmlCompileError(binding->location, tr("Unexpected object assignment")); + } else if (property->propType == qMetaTypeId()) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); + } else { + // We want to raw metaObject here as the raw metaobject is the + // actual property type before we applied any extensions that might + // effect the properties on the type, but don't effect assignability + QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType); + + // Will be true if the assgned type inherits propertyMetaObject + bool isAssignable = false; + // Determine isAssignable value + if (propertyMetaObject) { + QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); + while (c && !isAssignable) { + isAssignable |= c == propertyMetaObject; + c = c->parent(); + } + } + + if (!isAssignable) { + return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property")); + } + } + return noError; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h new file mode 100644 index 0000000000..1cbb370068 --- /dev/null +++ b/src/qml/compiler/qqmlpropertyvalidator_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ +#ifndef QQMLPROPERTYVALIDATOR_P_H +#define QQMLPROPERTYVALIDATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QQmlPropertyValidator +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) +public: + QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit); + + QVector validate(); + +private: + QVector validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; + QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; + QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const; + + bool canCoerce(int to, QQmlPropertyCache *fromMo) const; + + QVector recordError(const QV4::CompiledData::Location &location, const QString &description) const Q_REQUIRED_RESULT; + QVector recordError(const QQmlCompileError &error) const Q_REQUIRED_RESULT; + QString stringAt(int index) const { return qmlUnit->stringAt(index); } + + QQmlEnginePrivate *enginePrivate; + const QQmlImports &imports; + const QV4::CompiledData::Unit *qmlUnit; + const QHash &resolvedTypes; + const QQmlPropertyCacheVector &propertyCaches; + + QVector * const bindingPropertyDataPerObject; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYVALIDATOR_P_H diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 809f0033e9..357307fcea 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -44,10 +44,10 @@ #include #include #include -#include #include #include "qqmlpropertycachecreator_p.h" +#include "qqmlpropertyvalidator_p.h" #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -1338,640 +1338,6 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) return true; } - -QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit) - : enginePrivate(enginePrivate) - , imports(imports) - , qmlUnit(compilationUnit->data) - , resolvedTypes(compilationUnit->resolvedTypes) - , propertyCaches(compilationUnit->propertyCaches) - , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject) -{ - bindingPropertyDataPerObject->resize(qmlUnit->nObjects); -} - -QVector QQmlPropertyValidator::validate() -{ - return validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0); -} - -typedef QVarLengthArray GroupPropertyVector; - -struct BindingFinder -{ - bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const - { - return name < binding->propertyNameIndex; - } - bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const - { - return binding->propertyNameIndex < name; - } - bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const - { - return lhs->propertyNameIndex < rhs->propertyNameIndex; - } -}; - -QVector QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const -{ - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); - - if (obj->flags & QV4::CompiledData::Object::IsComponent) { - Q_ASSERT(obj->nBindings == 1); - const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); - return validateObject(componentBinding->value.objectIndex, componentBinding); - } - - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); - if (!propertyCache) - return QVector(); - - QStringList deferredPropertyNames; - { - const QMetaObject *mo = propertyCache->firstCppMetaObject(); - const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); - if (namesIndex != -1) { - QMetaClassInfo classInfo = mo->classInfo(namesIndex); - deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); - } - } - - QQmlCustomParser *customParser = 0; - if (auto typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - if (typeRef->type) - customParser = typeRef->type->customParser(); - } - - QList customBindings; - - // Collect group properties first for sanity checking - // vector values are sorted by property name string index. - GroupPropertyVector groupProperties; - const QV4::CompiledData::Binding *binding = obj->bindingTable(); - for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { - if (!binding->isGroupProperty()) - continue; - - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) - continue; - - if (populatingValueTypeGroupProperty) { - return recordError(binding->location, tr("Property assignment expected")); - } - - GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); - groupProperties.insert(pos, binding); - } - - QmlIR::PropertyResolver propertyResolver(propertyCache); - - QString defaultPropertyName; - QQmlPropertyData *defaultProperty = 0; - if (obj->indexOfDefaultPropertyOrAlias != -1) { - QQmlPropertyCache *cache = propertyCache->parent(); - defaultPropertyName = cache->defaultPropertyName(); - defaultProperty = cache->defaultProperty(); - } else { - defaultPropertyName = propertyCache->defaultPropertyName(); - defaultProperty = propertyCache->defaultProperty(); - } - - QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings); - - binding = obj->bindingTable(); - for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { - QString name = stringAt(binding->propertyNameIndex); - - if (customParser) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { - customBindings << binding; - continue; - } - } else if (QmlIR::IRBuilder::isSignalPropertyName(name) - && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { - customBindings << binding; - continue; - } - } - - bool bindingToDefaultProperty = false; - bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty; - - bool notInRevision = false; - QQmlPropertyData *pd = 0; - if (!name.isEmpty()) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - pd = propertyResolver.signal(name, ¬InRevision); - else - pd = propertyResolver.property(name, ¬InRevision, isGroupProperty ? QmlIR::PropertyResolver::IgnoreRevision : QmlIR::PropertyResolver::CheckRevision); - - if (notInRevision) { - QString typeName = stringAt(obj->inheritedTypeNameIndex); - auto *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex); - if (objectType && objectType->type) { - return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); - } else { - return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); - } - } - } else { - if (isGroupProperty) - return recordError(binding->location, tr("Cannot assign a value directly to a grouped property")); - - pd = defaultProperty; - name = defaultPropertyName; - bindingToDefaultProperty = true; - } - - if (pd) - collectedBindingPropertyData[i] = pd; - - if (name.constData()->isUpper() && !binding->isAttachedProperty()) { - QQmlType *type = 0; - QQmlImportNamespace *typeNamespace = 0; - imports.resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace); - if (typeNamespace) - return recordError(binding->location, tr("Invalid use of namespace")); - return recordError(binding->location, tr("Invalid attached object assignment")); - } - - if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { - const QVector subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)); - if (!subObjectValidatorErrors.isEmpty()) - return subObjectValidatorErrors; - } - - // Signal handlers were resolved and checked earlier in the signal handler conversion pass. - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - continue; - - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) { - return recordError(binding->location, tr("Attached properties cannot be used here")); - } - continue; - } - - if (pd) { - GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); - const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex); - - if (!pd->isWritable() - && !pd->isQList() - && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) - ) { - - if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object) - return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); - return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); - } - - if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { - QString error; - if (pd->propType == qMetaTypeId()) - error = tr( "Cannot assign multiple values to a script property"); - else - error = tr( "Cannot assign multiple values to a singular property"); - return recordError(binding->valueLocation, error); - } - - if (!bindingToDefaultProperty - && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) - && assigningToGroupProperty) { - QV4::CompiledData::Location loc = binding->valueLocation; - if (loc < (*assignedGroupProperty)->valueLocation) - loc = (*assignedGroupProperty)->valueLocation; - - if (pd && QQmlValueTypeFactory::isValueType(pd->propType)) - return recordError(loc, tr("Property has already been assigned a value")); - return recordError(loc, tr("Cannot assign a value directly to a grouped property")); - } - - if (binding->type < QV4::CompiledData::Binding::Type_Script) { - QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding); - if (bindingError.isSet()) - return recordError(bindingError); - } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { - QQmlCompileError bindingError = validateObjectBinding(pd, name, binding); - if (bindingError.isSet()) - return recordError(bindingError); - } else if (binding->isGroupProperty()) { - if (QQmlValueTypeFactory::isValueType(pd->propType)) { - if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)) { - if (!pd->isWritable()) { - return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); - } - } else { - return recordError(binding->location, tr("Invalid grouped property access")); - } - } else { - if (!enginePrivate->propertyCacheForType(pd->propType)) { - return recordError(binding->location, tr("Invalid grouped property access")); - } - } - } - } else { - if (customParser) { - customBindings << binding; - continue; - } - if (bindingToDefaultProperty) { - return recordError(binding->location, tr("Cannot assign to non-existent default property")); - } else { - return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name)); - } - } - } - - if (obj->idNameIndex) { - bool notInRevision = false; - collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), ¬InRevision); - } - - if (customParser && !customBindings.isEmpty()) { - customParser->clearErrors(); - customParser->validator = this; - customParser->engine = enginePrivate; - customParser->imports = &imports; - customParser->verifyBindings(qmlUnit, customBindings); - customParser->validator = 0; - customParser->engine = 0; - customParser->imports = (QQmlImports*)0; - QVector parserErrors = customParser->errors(); - if (!parserErrors.isEmpty()) - return parserErrors; - } - - (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; - - QVector noError; - return noError; -} - -QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const -{ - if (property->isQList()) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists")); - } - - QQmlCompileError noError; - - if (property->isEnum()) { - if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) - return noError; - - QString value = binding->valueAsString(qmlUnit); - QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex); - bool ok; - if (p.isFlagType()) { - p.enumerator().keysToValue(value.toUtf8().constData(), &ok); - } else - p.enumerator().keyToValue(value.toUtf8().constData(), &ok); - - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); - } - return noError; - } - - switch (property->propType) { - case QMetaType::QVariant: - break; - case QVariant::String: { - if (!binding->evaluatesToString()) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string expected")); - } - } - break; - case QVariant::StringList: { - if (!binding->evaluatesToString()) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or string list expected")); - } - } - break; - case QVariant::ByteArray: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: byte array expected")); - } - } - break; - case QVariant::Url: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url expected")); - } - } - break; - case QVariant::UInt: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = binding->valueAsNumber(); - if (double(uint(d)) == d) - return noError; - } - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected")); - } - break; - case QVariant::Int: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = binding->valueAsNumber(); - if (double(int(d)) == d) - return noError; - } - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int expected")); - } - break; - case QMetaType::Float: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected")); - } - } - break; - case QVariant::Double: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected")); - } - } - break; - case QVariant::Color: { - bool ok = false; - QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: color expected")); - } - } - break; -#ifndef QT_NO_DATESTRING - case QVariant::Date: { - bool ok = false; - QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: date expected")); - } - } - break; - case QVariant::Time: { - bool ok = false; - QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: time expected")); - } - } - break; - case QVariant::DateTime: { - bool ok = false; - QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: datetime expected")); - } - } - break; -#endif // QT_NO_DATESTRING - case QVariant::Point: { - bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); - } - } - break; - case QVariant::PointF: { - bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); - } - } - break; - case QVariant::Size: { - bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected")); - } - } - break; - case QVariant::SizeF: { - bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected")); - } - } - break; - case QVariant::Rect: { - bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: rect expected")); - } - } - break; - case QVariant::RectF: { - bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); - } - } - break; - case QVariant::Bool: { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: boolean expected")); - } - } - break; - case QVariant::Vector3D: { - struct { - float xp; - float yp; - float zy; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected")); - } - } - break; - case QVariant::Vector4D: { - struct { - float xp; - float yp; - float zy; - float wp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected")); - } - } - break; - case QVariant::RegExp: - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); - default: { - // generate single literal value assignment to a list property if required - if (property->propType == qMetaTypeId >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected")); - } - break; - } else if (property->propType == qMetaTypeId >()) { - bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); - if (ok) { - double n = binding->valueAsNumber(); - if (double(int(n)) != n) - ok = false; - } - if (!ok) - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected")); - break; - } else if (property->propType == qMetaTypeId >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected")); - } - break; - } else if (property->propType == qMetaTypeId >()) { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected")); - } - break; - } else if (property->propType == qMetaTypeId >()) { - if (!binding->evaluatesToString()) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected")); - } - break; - } else if (property->propType == qMetaTypeId()) { - break; - } else if (property->propType == qMetaTypeId()) { - break; - } - - // otherwise, try a custom type assignment - QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); - if (!converter) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); - } - } - break; - } - return noError; -} - -/*! - Returns true if from can be assigned to a (QObject) property of type - to. -*/ -bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const -{ - QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); - - while (fromMo) { - if (fromMo == toMo) - return true; - fromMo = fromMo->parent(); - } - return false; -} - -QVector QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const -{ - QVector errors; - errors.append(QQmlCompileError(location, description)); - return errors; -} - -QVector QQmlPropertyValidator::recordError(const QQmlCompileError &error) const -{ - QVector errors; - errors.append(error); - return errors; -} - -QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const -{ - QQmlCompileError noError; - - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); - - bool isValueSource = false; - bool isPropertyInterceptor = false; - - QQmlType *qmlType = 0; - const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex); - if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) { - QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - const QMetaObject *mo = cache->firstCppMetaObject(); - while (mo && !qmlType) { - qmlType = QQmlMetaType::qmlType(mo); - mo = mo->superClass(); - } - Q_ASSERT(qmlType); - } - - if (qmlType) { - isValueSource = qmlType->propertyValueSourceCast() != -1; - isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1; - } - - if (!isValueSource && !isPropertyInterceptor) { - return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); - } - - return noError; - } - - if (QQmlMetaType::isInterface(property->propType)) { - // Can only check at instantiation time if the created sub-object successfully casts to the - // target interface. - return noError; - } else if (property->propType == QMetaType::QVariant) { - // We can convert everything to QVariant :) - return noError; - } else if (property->isQList()) { - const int listType = enginePrivate->listType(property->propType); - if (!QQmlMetaType::isInterface(listType)) { - QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); - if (!canCoerce(listType, source)) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); - } - } - return noError; - } else if (qmlUnit->objectAt(binding->value.objectIndex)->flags & QV4::CompiledData::Object::IsComponent) { - return noError; - } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { - return noError; - } else if (QQmlValueTypeFactory::isValueType(property->propType)) { - return QQmlCompileError(binding->location, tr("Unexpected object assignment")); - } else if (property->propType == qMetaTypeId()) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); - } else { - // We want to raw metaObject here as the raw metaobject is the - // actual property type before we applied any extensions that might - // effect the properties on the type, but don't effect assignability - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType); - - // Will be true if the assgned type inherits propertyMetaObject - bool isAssignable = false; - // Determine isAssignable value - if (propertyMetaObject) { - QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); - while (c && !isAssignable) { - isAssignable |= c == propertyMetaObject; - c = c->parent(); - } - } - - if (!isAssignable) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property")); - } - } - return noError; -} - QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen) : QQmlCompilePass(typeCompiler) , resolvedTypes(typeCompiler->resolvedTypes) diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 6fddd37111..b2494212fa 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -299,34 +299,6 @@ private: bool _seenObjectWithId; }; -class QQmlPropertyValidator -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) -public: - QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit); - - QVector validate(); - -private: - QVector validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; - QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; - QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const; - - bool canCoerce(int to, QQmlPropertyCache *fromMo) const; - - QVector recordError(const QV4::CompiledData::Location &location, const QString &description) const Q_REQUIRED_RESULT; - QVector recordError(const QQmlCompileError &error) const Q_REQUIRED_RESULT; - QString stringAt(int index) const { return qmlUnit->stringAt(index); } - - QQmlEnginePrivate *enginePrivate; - const QQmlImports &imports; - const QV4::CompiledData::Unit *qmlUnit; - const QHash &resolvedTypes; - const QQmlPropertyCacheVector &propertyCaches; - - QVector * const bindingPropertyDataPerObject; -}; - // ### merge with QtQml::JSCodeGen and operate directly on object->functionsAndExpressions once old compiler is gone. class QQmlJSCodeGenerator : public QQmlCompilePass { -- cgit v1.2.3 From d18026b00713de93bc3bd8f7e85f1ff0cf128185 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 16 Jun 2016 10:08:13 +0200 Subject: Moved import cache and type resolution cache code The code reads mostly from QQmlTypeData internal data structures, so it's cleaner to move it there - we can remove the getters for these internal fields. And it also allows for re-use later when not using the type compiler. Change-Id: I36cfaf3f1cecd6c8b223ee08516884a07bc60d6c Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 52 ++--------------------- src/qml/compiler/qqmltypecompiler_p.h | 4 +- src/qml/compiler/qv4compileddata_p.h | 3 +- src/qml/qml/qqmltypeloader.cpp | 78 ++++++++++++++++++++++++++++++----- src/qml/qml/qqmltypeloader_p.h | 8 ++-- 5 files changed, 79 insertions(+), 66 deletions(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 357307fcea..add330d5ce 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -57,61 +57,17 @@ QT_BEGIN_NAMESPACE -QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *parsedQML) - : engine(engine) +QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *parsedQML, const QQmlRefPointer &importCache, const QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap &resolvedTypeCache) + : resolvedTypes(resolvedTypeCache) + , engine(engine) , typeData(typeData) + , importCache(importCache) , document(parsedQML) { } QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() { - importCache.adopt(new QQmlTypeNameCache); - - foreach (const QString &ns, typeData->namespaces()) - importCache->add(ns); - - // Add any Composite Singletons that were used to the import cache - foreach (const QQmlTypeData::TypeReference &singleton, typeData->compositeSingletons()) - importCache->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); - - typeData->imports().populateCache(importCache.data()); - - const QHash &resolvedTypeRefs = typeData->resolvedTypeRefs(); - for (QHash::ConstIterator resolvedType = resolvedTypeRefs.constBegin(), end = resolvedTypeRefs.constEnd(); - resolvedType != end; ++resolvedType) { - QScopedPointer ref(new QV4::CompiledData::CompilationUnit::ResolvedTypeReference); - QQmlType *qmlType = resolvedType->type; - if (resolvedType->typeData) { - if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) { - recordError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName())); - return nullptr; - } - ref->compilationUnit = resolvedType->typeData->compilationUnit(); - } else if (qmlType) { - ref->type = qmlType; - Q_ASSERT(ref->type); - - if (resolvedType->needsCreation && !ref->type->isCreatable()) { - QQmlError error; - QString reason = ref->type->noCreationReason(); - if (reason.isEmpty()) - reason = tr("Element is not creatable."); - recordError(resolvedType->location, reason); - return nullptr; - } - - if (ref->type->containsRevisionedAttributes()) { - ref->typePropertyCache = engine->cache(ref->type, - resolvedType->minorVersion); - } - } - ref->majorVersion = resolvedType->majorVersion; - ref->minorVersion = resolvedType->minorVersion; - ref->doDynamicTypeCheck(); - resolvedTypes.insert(resolvedType.key(), ref.take()); - } - // Build property caches and VME meta object data for (auto it = resolvedTypes.constBegin(), end = resolvedTypes.constEnd(); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index b2494212fa..1692e05450 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -89,7 +89,7 @@ struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document); + QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer &importCache, const QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap &resolvedTypeCache); // --- interface used by QQmlPropertyCacheCreator typedef QmlIR::Object CompiledObject; @@ -98,7 +98,7 @@ public: QString stringAt(int idx) const; QmlIR::PoolList::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } QmlIR::PoolList::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } - QHash resolvedTypes; + QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap resolvedTypes; // --- QV4::CompiledData::CompilationUnit *compile(); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index fb82b2bcbb..36103a7b37 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -791,7 +791,8 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount void doDynamicTypeCheck(); }; // map from name index - QHash resolvedTypes; + typedef QHash ResolvedTypeReferenceMap; + ResolvedTypeReferenceMap resolvedTypes; int metaTypeId; int listMetaTypeId; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 847e138c5a..01201e66e7 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -434,6 +434,16 @@ void QQmlDataBlob::setError(const QList &errors) tryDone(); } +void QQmlDataBlob::setError(const QQmlCompileError &error) +{ + QQmlError e; + e.setColumn(error.location.column); + e.setLine(error.location.line); + e.setDescription(error.description); + e.setUrl(url()); + setError(e); +} + /*! Wait for \a blob to become complete or to error. If \a blob is already complete or in error, or this blob is already complete, this has no effect. @@ -2033,16 +2043,6 @@ const QList &QQmlTypeData::resolvedScripts() cons return m_scripts; } -const QSet &QQmlTypeData::namespaces() const -{ - return m_namespaces; -} - -const QList &QQmlTypeData::compositeSingletons() const -{ - return m_compositeSingletons; -} - QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const { return m_compiledData.data(); @@ -2301,7 +2301,15 @@ void QQmlTypeData::compile() { Q_ASSERT(m_compiledData.isNull()); - QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), this, m_document.data()); + QQmlRefPointer importCache; + QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap resolvedTypeCache; + QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache); + if (error.isSet()) { + setError(error); + return; + } + + QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), this, m_document.data(), importCache, resolvedTypeCache); m_compiledData = compiler.compile(); if (!m_compiledData) { setError(compiler.compilationErrors()); @@ -2428,6 +2436,54 @@ void QQmlTypeData::resolveTypes() } } +QQmlCompileError QQmlTypeData::buildTypeResolutionCaches(QQmlRefPointer *importCache, QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap *resolvedTypeCache) const +{ + importCache->adopt(new QQmlTypeNameCache); + + for (const QString &ns: m_namespaces) + (*importCache)->add(ns); + + // Add any Composite Singletons that were used to the import cache + for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) + (*importCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); + + m_importCache.populateCache(*importCache); + + QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + + for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) { + QScopedPointer ref(new QV4::CompiledData::CompilationUnit::ResolvedTypeReference); + QQmlType *qmlType = resolvedType->type; + if (resolvedType->typeData) { + if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) { + return QQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName())); + } + ref->compilationUnit = resolvedType->typeData->compilationUnit(); + } else if (qmlType) { + ref->type = qmlType; + Q_ASSERT(ref->type); + + if (resolvedType->needsCreation && !ref->type->isCreatable()) { + QString reason = ref->type->noCreationReason(); + if (reason.isEmpty()) + reason = tr("Element is not creatable."); + return QQmlCompileError(resolvedType->location, reason); + } + + if (ref->type->containsRevisionedAttributes()) { + ref->typePropertyCache = engine->cache(ref->type, + resolvedType->minorVersion); + } + } + ref->majorVersion = resolvedType->majorVersion; + ref->minorVersion = resolvedType->minorVersion; + ref->doDynamicTypeCheck(); + resolvedTypeCache->insert(resolvedType.key(), ref.take()); + } + QQmlCompileError noError; + return noError; +} + bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref) { QQmlImportNamespace *typeNamespace = 0; diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 68b4d18a14..20f701514a 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -81,6 +81,7 @@ class QQmlComponentPrivate; class QQmlTypeData; class QQmlTypeLoader; class QQmlExtensionInterface; +struct QQmlCompileError; namespace QmlIR { struct Document; @@ -151,6 +152,7 @@ protected: // Can be called from within callbacks void setError(const QQmlError &); void setError(const QList &errors); + void setError(const QQmlCompileError &error); void addDependency(QQmlDataBlob *); // Callbacks made in load thread @@ -390,6 +392,7 @@ private: class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob { + Q_DECLARE_TR_FUNCTIONS(QQmlTypeData) public: struct TypeReference { @@ -421,11 +424,7 @@ private: public: ~QQmlTypeData(); - const QHash &resolvedTypeRefs() const { return m_resolvedTypes; } - const QList &resolvedScripts() const; - const QSet &namespaces() const; - const QList &compositeSingletons() const; QV4::CompiledData::CompilationUnit *compilationUnit() const; @@ -451,6 +450,7 @@ protected: private: void continueLoadFromIR(); void resolveTypes(); + QQmlCompileError buildTypeResolutionCaches(QQmlRefPointer *importCache, QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap *resolvedTypeCache) const; void compile(); bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref); -- cgit v1.2.3 From 0dfd0a1c09e36bb80c575c097ea6a5809df5e640 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 16 Jun 2016 13:29:36 +0200 Subject: Move object/binding counter code to CompilationUnit The code to update the counters operates entirely on data structures from CompilationUnit, so it makes sense to move it into that class. Plus - you guessed it - this will also be called when not using the type compiler in the future. Change-Id: I644b0914980b799be1020ed74d2791b127bbcf9f Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 25 +------------------------ src/qml/compiler/qv4compileddata.cpp | 28 ++++++++++++++++++++++++++++ src/qml/compiler/qv4compileddata_p.h | 2 ++ 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index add330d5ce..766e1fd40f 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -218,30 +218,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() } } - // Collect some data for instantiation later. - int bindingCount = 0; - int parserStatusCount = 0; - int objectCount = 0; - for (quint32 i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); - bindingCount += obj->nBindings; - if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - if (QQmlType *qmlType = typeRef->type) { - if (qmlType->parserStatusCast() != -1) - ++parserStatusCount; - } - ++objectCount; - if (typeRef->compilationUnit) { - bindingCount += typeRef->compilationUnit->totalBindingsCount; - parserStatusCount += typeRef->compilationUnit->totalParserStatusCount; - objectCount += typeRef->compilationUnit->totalObjectCount; - } - } - } - - compilationUnit->totalBindingsCount = bindingCount; - compilationUnit->totalParserStatusCount = parserStatusCount; - compilationUnit->totalObjectCount = objectCount; + compilationUnit->updateBindingAndObjectCounters(); if (errors.isEmpty()) return compilationUnit; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index db25e265d0..a97902e2a0 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -238,6 +238,34 @@ IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjec return *it; } +void CompilationUnit::updateBindingAndObjectCounters() +{ + + // Collect some data for instantiation later. + int bindingCount = 0; + int parserStatusCount = 0; + int objectCount = 0; + for (quint32 i = 0; i < data->nObjects; ++i) { + const QV4::CompiledData::Object *obj = data->objectAt(i); + bindingCount += obj->nBindings; + if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { + if (QQmlType *qmlType = typeRef->type) { + if (qmlType->parserStatusCast() != -1) + ++parserStatusCount; + } + ++objectCount; + if (typeRef->compilationUnit) { + bindingCount += typeRef->compilationUnit->totalBindingsCount; + parserStatusCount += typeRef->compilationUnit->totalParserStatusCount; + objectCount += typeRef->compilationUnit->totalObjectCount; + } + } + } + + totalBindingsCount = bindingCount; + totalParserStatusCount = parserStatusCount; + totalObjectCount = objectCount; +} #endif // V4_BOOTSTRAP Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 36103a7b37..10bc01e883 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -760,6 +760,8 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QHash> namedObjectsPerComponentCache; IdentifierHash namedObjectsPerComponent(int componentObjectIndex); + void updateBindingAndObjectCounters(); + int totalBindingsCount; // Number of bindings used in this type int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses int totalObjectCount; // Number of objects explicitly instantiated -- cgit v1.2.3 From 8a33d37006e8ad9010fe076105ada9f1ca5d9871 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 6 Jun 2016 11:46:01 +0200 Subject: Added basic support for saving compilation units to disk Hidden behind a QML_DISK_CACHE=1 environment variable we will now attempt to save a binary representation of the type compilation for Foo.qml next to it called Foo.qmlc. Change-Id: I27e800b50cdb186669256fd277578ea1f1e70513 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 58 ++++++++ src/qml/compiler/qv4compileddata_p.h | 11 ++ src/qml/compiler/qv4compiler.cpp | 3 + src/qml/jit/qv4assembler.cpp | 47 ++++++ src/qml/jit/qv4assembler_p.h | 4 +- src/qml/qml/qqmltypeloader.cpp | 7 + tests/auto/qml/qml.pro | 3 +- tests/auto/qml/qmldiskcache/qmldiskcache.pro | 7 + tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 182 +++++++++++++++++++++++ 9 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 tests/auto/qml/qmldiskcache/qmldiskcache.pro create mode 100644 tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index a97902e2a0..d2587a547c 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #endif #include #include @@ -266,6 +267,63 @@ void CompilationUnit::updateBindingAndObjectCounters() totalParserStatusCount = parserStatusCount; totalObjectCount = objectCount; } + +bool CompilationUnit::saveToDisk(QString *errorString) +{ + errorString->clear(); + + const QUrl unitUrl = url(); + if (!unitUrl.isLocalFile()) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + // Foo.qml -> Foo.qmlc + QSaveFile cacheFile(unitUrl.toLocalFile() + QLatin1Char('c')); + if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + *errorString = cacheFile.errorString(); + return false; + } + + QByteArray modifiedUnit; + modifiedUnit.resize(data->unitSize); + memcpy(modifiedUnit.data(), data, data->unitSize); + const char *dataPtr = modifiedUnit.data(); + Unit *unitPtr; + memcpy(&unitPtr, &dataPtr, sizeof(unitPtr)); + unitPtr->flags |= Unit::StaticData; + + prepareCodeOffsetsForDiskStorage(unitPtr); + + qint64 headerWritten = cacheFile.write(modifiedUnit); + if (headerWritten != modifiedUnit.size()) { + *errorString = cacheFile.errorString(); + return false; + } + + if (!saveCodeToDisk(&cacheFile, unitPtr, errorString)) + return false; + + if (!cacheFile.commit()) { + *errorString = cacheFile.errorString(); + return false; + } + + return true; +} + +void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) +{ + Q_UNUSED(unit); +} + +bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString) +{ + Q_UNUSED(device); + Q_UNUSED(unit); + *errorString = QStringLiteral("Saving code to disk is not supported in this configuration"); + return false; +} #endif // V4_BOOTSTRAP Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 10bc01e883..b960901402 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -69,6 +69,7 @@ QT_BEGIN_NAMESPACE +class QIODevice; class QQmlPropertyCache; class QQmlPropertyData; class QQmlTypeNameCache; @@ -207,6 +208,11 @@ struct Function quint32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index) // Qml Extensions End + // Absolute offset into file where the code for this function is located. Only used when the function + // is serialized. + quint64 codeOffset; + quint64 codeSize; + // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] // quint32 offsetForInnerFunctions[nInnerFunctions] @@ -830,8 +836,13 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount void markObjects(QV4::ExecutionEngine *e); void destroy() Q_DECL_OVERRIDE; + + bool saveToDisk(QString *errorString); + protected: virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; + virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit); + virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); #endif // V4_BOOTSTRAP }; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 81f407485f..b801009f4e 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -362,6 +362,9 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *ir function->location.line = irFunction->line; function->location.column = irFunction->column; + function->codeOffset = 0; + function->codeSize = 0; + // write formals quint32 *formals = (quint32 *)(f + function->formalsOffset); for (int i = 0; i < irFunction->formals.size(); ++i) diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 7d0d93b63a..d700449e9e 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -79,6 +79,53 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) } } +void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) +{ + const int codeAlignment = 16; + quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize); + Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + for (int i = 0; i < codeRefs.size(); ++i) { + CompiledData::Function *compiledFunction = const_cast(unit->functionAt(i)); + compiledFunction->codeOffset = offset; + compiledFunction->codeSize = codeRefs.at(i).size(); + offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize); + } +} + +bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) +{ + Q_ASSERT(device->pos() == unit->unitSize); + Q_ASSERT(device->atEnd()); + Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + + QByteArray padding; + + for (int i = 0; i < codeRefs.size(); ++i) { + const CompiledData::Function *compiledFunction = unit->functionAt(i); + + if (device->pos() > qint64(compiledFunction->codeOffset)) { + *errorString = QStringLiteral("Invalid state of cache file to write."); + return false; + } + + const quint64 paddingSize = compiledFunction->codeOffset - device->pos(); + padding.fill(0, paddingSize); + qint64 written = device->write(padding); + if (written != padding.size()) { + *errorString = device->errorString(); + return false; + } + + const void *undecoratedCodePtr = codeRefs.at(i).code().dataLocation(); + written = device->write(reinterpret_cast(undecoratedCodePtr), compiledFunction->codeSize); + if (written != qint64(compiledFunction->codeSize)) { + *errorString = device->errorString(); + return false; + } + } + return true; +} + const Assembler::VoidType Assembler::Void; Assembler::Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 9a681bc9ba..f0063aae06 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -81,7 +81,9 @@ struct CompilationUnit : public QV4::CompiledData::CompilationUnit { virtual ~CompilationUnit(); - virtual void linkBackendToEngine(QV4::ExecutionEngine *engine); + void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; + void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; + bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); // Coderef + execution engine diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 01201e66e7..fef317cbbd 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -97,6 +97,7 @@ #endif DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); +DEFINE_BOOL_CONFIG_OPTION(diskCache, QML_DISK_CACHE); QT_BEGIN_NAMESPACE @@ -2315,6 +2316,12 @@ void QQmlTypeData::compile() setError(compiler.compilationErrors()); return; } + if (diskCache()) { + QString errorString; + if (!m_compiledData->saveToDisk(&errorString)) { + qDebug() << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString; + } + } } void QQmlTypeData::resolveTypes() diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 28f04be5d7..33b8d5d983 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -61,7 +61,8 @@ PRIVATETESTS += \ v4misc \ qqmltranslation \ qqmlimport \ - qqmlobjectmodel + qqmlobjectmodel \ + qmldiskcache qtHaveModule(widgets) { PUBLICTESTS += \ diff --git a/tests/auto/qml/qmldiskcache/qmldiskcache.pro b/tests/auto/qml/qmldiskcache/qmldiskcache.pro new file mode 100644 index 0000000000..f2d1a04780 --- /dev/null +++ b/tests/auto/qml/qmldiskcache/qmldiskcache.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qmldiskcache +osx:CONFIG -= app_bundle + +SOURCES += tst_qmldiskcache.cpp + +QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp new file mode 100644 index 0000000000..edab49e4be --- /dev/null +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** 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 + +class tst_qmldiskcache: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void regenerateAfterChange(); +}; + +struct TestCompiler +{ + TestCompiler(QQmlEngine *engine) + : engine(engine) + , tempDir() + , testFilePath(tempDir.path() + QStringLiteral("/test.qml")) + , cacheFilePath(tempDir.path() + QStringLiteral("/test.qmlc")) + , mappedFile(cacheFilePath) + , currentMapping(nullptr) + { + } + + bool compile(const QByteArray &contents) + { + if (currentMapping) { + mappedFile.unmap(currentMapping); + currentMapping = nullptr; + } + mappedFile.close(); + + { + QFile f(testFilePath); + if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + lastErrorString = f.errorString(); + return false; + } + if (f.write(contents) != contents.size()) { + lastErrorString = f.errorString(); + return false; + } + } + + QQmlComponent component(engine, testFilePath); + if (!component.isReady()) { + lastErrorString = component.errorString(); + return false; + } + + return true; + } + + const QV4::CompiledData::Unit *mapUnit() + { + if (!mappedFile.open(QIODevice::ReadOnly)) { + lastErrorString = mappedFile.errorString(); + return nullptr; + } + + currentMapping = mappedFile.map(/*offset*/0, mappedFile.size()); + if (!currentMapping) { + lastErrorString = mappedFile.errorString(); + return nullptr; + } + QV4::CompiledData::Unit *unitPtr; + memcpy(&unitPtr, ¤tMapping, sizeof(unitPtr)); + return unitPtr; + } + + QQmlEngine *engine; + const QTemporaryDir tempDir; + const QString testFilePath; + const QString cacheFilePath; + QString lastErrorString; + QFile mappedFile; + uchar *currentMapping; +}; + +void tst_qmldiskcache::initTestCase() +{ + qputenv("QML_DISK_CACHE", "1"); +} + +void tst_qmldiskcache::regenerateAfterChange() +{ + QQmlEngine engine; + TestCompiler testCompiler(&engine); + + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n" + "QtObject {\n" + " property string blah: Qt.platform;\n" + "}"); + + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + +#ifdef V4_ENABLE_JIT + { + const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); + QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); + + QCOMPARE(testUnit->nObjects, quint32(1)); + + const QV4::CompiledData::Object *obj = testUnit->objectAt(0); + QCOMPARE(obj->nBindings, quint32(1)); + QCOMPARE(obj->bindingTable()->type, quint32(QV4::CompiledData::Binding::Type_Script)); + QCOMPARE(obj->bindingTable()->value.compiledScriptIndex, quint32(1)); + + QCOMPARE(testUnit->functionTableSize, quint32(2)); + + const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1); + QVERIFY(bindingFunction->codeOffset > testUnit->unitSize); + } +#else + QVERIFY(!testCompiler.mapUnit()); + return; +#endif + + engine.clearComponentCache(); + + { + const QByteArray newContents = QByteArrayLiteral("import QtQml 2.0\n" + "QtObject {\n" + " property string blah: Qt.platform;\n" + " property int secondProperty: 42;\n" + "}"); + + QVERIFY2(testCompiler.compile(newContents), qPrintable(testCompiler.lastErrorString)); + const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); + QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); + + QCOMPARE(testUnit->nObjects, quint32(1)); + + const QV4::CompiledData::Object *obj = testUnit->objectAt(0); + QCOMPARE(obj->nBindings, quint32(2)); + QCOMPARE(obj->bindingTable()->type, quint32(QV4::CompiledData::Binding::Type_Number)); + QCOMPARE(obj->bindingTable()->value.d, double(42)); + + QCOMPARE(testUnit->functionTableSize, quint32(2)); + + const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1); + QVERIFY(bindingFunction->codeOffset > testUnit->unitSize); + } +} + +QTEST_MAIN(tst_qmldiskcache) + +#include "tst_qmldiskcache.moc" -- cgit v1.2.3 From c8241e8551b9491081486cc98936ad5ad1824a1e Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 17 Jun 2016 14:42:37 +0200 Subject: qmltest::linecount - adjust text width With text line width 50 'Hello world!' is split into 2 lines on OS X 10.11 and the test expectes 3 lines, let's try something less than 50 (44). Task-number: QTBUG-53778 Change-Id: Id3254e9d89e7b41498ff8735eff97f0317ae9677 Reviewed-by: Shawn Rutledge --- tests/auto/qmltest/text/tst_text.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/auto/qmltest/text/tst_text.qml b/tests/auto/qmltest/text/tst_text.qml index 691a4838cd..d2899cfb74 100644 --- a/tests/auto/qmltest/text/tst_text.qml +++ b/tests/auto/qmltest/text/tst_text.qml @@ -118,9 +118,10 @@ Item { compare(txtlinecount.lineCount, 2) txtlinecount.text = txtlinecount.third; compare(txtlinecount.lineCount, 3) + console.log(txtlinecount.width) txtlinecount.text = txtlinecount.first; compare(txtlinecount.lineCount, 1) - txtlinecount.width = 50; + txtlinecount.width = 44; compare(txtlinecount.lineCount, 3) } function test_linecounts() { -- cgit v1.2.3 From 0960561981b59cbd293aeb32e87dc8a7732fe521 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Sun, 29 May 2016 00:07:26 +0200 Subject: Doc: Refactor cpp integration Message sample Since MyItem.qml is a GUI element, the example as is triggers a segfault when starting. This patch refactors the cpp code to make it a GUI application. Change-Id: I983c8c1945bd4be364c988ab9fdfa69e35e7b9de Reviewed-by: J-P Nurmi Reviewed-by: Sze Howe Koh --- src/qml/doc/src/cppintegration/exposecppattributes.qdoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc index f4f688520a..c7e4930bfb 100644 --- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc +++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc @@ -119,13 +119,13 @@ from C++: \code int main(int argc, char *argv[]) { - QCoreApplication app(argc, argv); + QGuiApplication app(argc, argv); - QQmlEngine engine; + QQuickView view; Message msg; - engine.rootContext()->setContextProperty("msg", &msg); - QQmlComponent component(&engine, QUrl::fromLocalFile("MyItem.qml")); - component.create(); + view.engine()->rootContext()->setContextProperty("msg", &msg); + view.setSource(QUrl::fromLocalFile("MyItem.qml")); + view.show(); return app.exec(); } -- cgit v1.2.3 From 156d10e16557b0d9f6c66963019f267314e73f13 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 17 Jun 2016 13:21:07 +0200 Subject: Clean up file/error handling in the type loader Fold the functionality of reading QFile contents via QQmlFile into QQmlDataBlob::Data. This reduces the dependency on QQmlFile - which is scheduled for removal - and it makes it possible in the future to avoid reading the file altogether if we have a cached compilation unit on disk. Change-Id: Ieeaf52b6fb1d25665cd3c3b196819e25aba3dd15 Reviewed-by: Lars Knoll --- src/qml/qml/qqmltypeloader.cpp | 71 ++++++++++++++++------ src/qml/qml/qqmltypeloader_p.h | 47 ++------------ .../data/badCompositeRegistration.1.errors.txt | 2 +- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 2 +- tests/auto/quick/qquickloader/tst_qquickloader.cpp | 6 +- 5 files changed, 62 insertions(+), 66 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index fef317cbbd..e11507a56a 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -445,6 +445,14 @@ void QQmlDataBlob::setError(const QQmlCompileError &error) setError(e); } +void QQmlDataBlob::setError(const QString &description) +{ + QQmlError e; + e.setDescription(description); + e.setUrl(finalUrl()); + setError(e); +} + /*! Wait for \a blob to become complete or to error. If \a blob is already complete or in error, or this blob is already complete, this has no effect. @@ -1091,13 +1099,9 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) QML_MEMORY_SCOPE_URL(blob->m_url); if (QQmlFile::isSynchronous(blob->m_url)) { - QQmlFile file(m_engine, blob->m_url); - - if (file.isError()) { - QQmlError error; - error.setUrl(blob->m_url); - error.setDescription(file.error()); - blob->setError(error); + const QString fileName = QQmlFile::urlToLocalFileOrQrc(blob->m_url); + if (!QQml_isFileCaseCorrect(fileName)) { + blob->setError(QLatin1String("File name case mismatch")); return; } @@ -1105,7 +1109,7 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) if (blob->m_data.isAsync()) m_thread->callDownloadProgressChanged(blob, 1.); - setData(blob, &file); + setData(blob, fileName); } else { #ifndef QT_NO_NETWORK @@ -1225,11 +1229,11 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) setData(blob, d); } -void QQmlTypeLoader::setData(QQmlDataBlob *blob, QQmlFile *file) +void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) { QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::Data d; - d.d = file; + d.d = &fileName; setData(blob, d); } @@ -2122,11 +2126,7 @@ void QQmlTypeData::done() QQmlType *type = QQmlMetaType::qmlType(url(), true); if (!isError() && type && type->isCompositeSingleton() && !m_isSingleton) { QString typeName = type->qmlTypeName(); - - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); - error.setUrl(finalUrl()); - setError(error); + setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); } // Compile component @@ -2170,7 +2170,12 @@ bool QQmlTypeData::loadImplicitImport() void QQmlTypeData::dataReceived(const Data &data) { - QString code = QString::fromUtf8(data.data(), data.size()); + QString error; + QString code = QString::fromUtf8(data.readAll(&error)); + if (!error.isEmpty()) { + setError(error); + return; + } QQmlEngine *qmlEngine = typeLoader()->engine(); m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0)); QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); @@ -2699,7 +2704,12 @@ struct EmptyCompilationUnit : public QV4::CompiledData::CompilationUnit void QQmlScriptBlob::dataReceived(const Data &data) { - QString source = QString::fromUtf8(data.data(), data.size()); + QString error; + QString source = QString::fromUtf8(data.readAll(&error)); + if (!error.isEmpty()) { + setError(error); + return; + } QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); QmlIR::Document irUnit(v4->debugger != 0); @@ -2854,7 +2864,12 @@ void QQmlQmldirData::setPriority(int priority) void QQmlQmldirData::dataReceived(const Data &data) { - m_content = QString::fromUtf8(data.data(), data.size()); + QString error; + m_content = QString::fromUtf8(data.readAll(&error)); + if (!error.isEmpty()) { + setError(error); + return; + } } void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *) @@ -2862,6 +2877,26 @@ void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit * Q_UNIMPLEMENTED(); } +QByteArray QQmlDataBlob::Data::readAll(QString *error) const +{ + Q_ASSERT(!d.isNull()); + error->clear(); + if (d.isT1()) { + return *d.asT1(); + } + QFile f(*d.asT2()); + if (!f.open(QIODevice::ReadOnly)) { + *error = f.errorString(); + return QByteArray(); + } + QByteArray data(f.size(), Qt::Uninitialized); + if (f.read(data.data(), data.length()) != data.length()) { + *error = f.errorString(); + return QByteArray(); + } + return data; +} + QT_END_NAMESPACE #include "qqmltypeloader.moc" diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 20f701514a..d476056ef1 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -131,21 +131,14 @@ public: class Data { public: - inline const char *data() const; - inline int size() const; - - inline QByteArray asByteArray() const; - - inline bool isFile() const; - inline QQmlFile *asFile() const; - + QByteArray readAll(QString *error) const; private: friend class QQmlDataBlob; friend class QQmlTypeLoader; inline Data(); Data(const Data &); Data &operator=(const Data &); - QBiPointer d; + QBiPointer d; }; protected: @@ -153,6 +146,7 @@ protected: void setError(const QQmlError &); void setError(const QList &errors); void setError(const QQmlCompileError &error); + void setError(const QString &description); void addDependency(QQmlDataBlob *); // Callbacks made in load thread @@ -342,7 +336,7 @@ private: #endif void setData(QQmlDataBlob *, const QByteArray &); - void setData(QQmlDataBlob *, QQmlFile *); + void setData(QQmlDataBlob *, const QString &fileName); void setData(QQmlDataBlob *, const QQmlDataBlob::Data &); void setCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit); @@ -582,40 +576,7 @@ QQmlDataBlob::Data::Data() { } -const char *QQmlDataBlob::Data::data() const -{ - Q_ASSERT(!d.isNull()); - - if (d.isT1()) return d.asT1()->constData(); - else return d.asT2()->data(); -} - -int QQmlDataBlob::Data::size() const -{ - Q_ASSERT(!d.isNull()); - - if (d.isT1()) return d.asT1()->size(); - else return d.asT2()->size(); -} -bool QQmlDataBlob::Data::isFile() const -{ - return d.isT2(); -} - -QByteArray QQmlDataBlob::Data::asByteArray() const -{ - Q_ASSERT(!d.isNull()); - - if (d.isT1()) return *d.asT1(); - else return d.asT2()->dataByteArray(); -} - -QQmlFile *QQmlDataBlob::Data::asFile() const -{ - if (d.isT2()) return d.asT2(); - else return 0; -} QT_END_NAMESPACE diff --git a/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt index 7a75447a62..acf0d1da84 100644 --- a/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt @@ -1,2 +1,2 @@ 3:1:Type RegisteredCompositeType2 unavailable --1:-1:File not found +-1:-1:No such file or directory diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index e800d64471..70db9aecd4 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2898,7 +2898,7 @@ void tst_qqmllanguage::importIncorrectCase() QCOMPARE(errors.count(), 1); const QString expectedError = isCaseSensitiveFileSystem(dataDirectory()) ? - QStringLiteral("File not found") : + QStringLiteral("No such file or directory") : QStringLiteral("File name case mismatch"); QCOMPARE(errors.at(0).description(), expectedError); diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index fe22a238b2..77af4796b6 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -213,7 +213,7 @@ void tst_QQuickLoader::sourceOrComponent_data() QTest::newRow("source with encoded subdir binding") << "source" << "source: encodeURIComponent('subdir/Test.qml')\n" << testFileUrl("subdir/Test.qml") << ""; QTest::newRow("sourceComponent") << "component" << "Component { id: comp; Rectangle { width: 100; height: 50 } }\n sourceComponent: comp\n" << QUrl() << ""; QTest::newRow("invalid source") << "source" << "source: 'IDontExist.qml'\n" << testFileUrl("IDontExist.qml") - << QString(testFileUrl("IDontExist.qml").toString() + ": File not found"); + << QString(testFileUrl("IDontExist.qml").toString() + ": No such file or directory"); } void tst_QQuickLoader::clear() @@ -748,7 +748,7 @@ void tst_QQuickLoader::initialPropertyValuesError_data() << (QStringList() << QString(testFileUrl("initialPropertyValues.error.1.qml").toString() + ":6:5: QML Loader: setSource: value is not an object")); QTest::newRow("nonexistent source url") << testFileUrl("initialPropertyValues.error.2.qml") - << (QStringList() << QString(testFileUrl("NonexistentSourceComponent.qml").toString() + ": File not found")); + << (QStringList() << QString(testFileUrl("NonexistentSourceComponent.qml").toString() + ": No such file or directory")); QTest::newRow("invalid source url") << testFileUrl("initialPropertyValues.error.3.qml") << (QStringList() << QString(testFileUrl("InvalidSourceComponent.qml").toString() + ":5:1: Syntax error")); @@ -901,7 +901,7 @@ void tst_QQuickLoader::asynchronous_data() << QStringList(); QTest::newRow("Non-existent component") << testFileUrl("IDoNotExist.qml") - << (QStringList() << QString(testFileUrl("IDoNotExist.qml").toString() + ": File not found")); + << (QStringList() << QString(testFileUrl("IDoNotExist.qml").toString() + ": No such file or directory")); QTest::newRow("Invalid component") << testFileUrl("InvalidSourceComponent.qml") << (QStringList() << QString(testFileUrl("InvalidSourceComponent.qml").toString() + ":5:1: Syntax error")); -- cgit v1.2.3 From f7e462ba153ff33a02cad903efc8662f1eacd225 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 10 Jun 2016 15:47:44 +0200 Subject: fix incorrect propagation of hover events in some cases Both the item itself and its descendants need to be considered together when deciding whether or not it is interested in hover events. A correction to f728a514abdf59e2ac556eef027a69d9f21db4b2. Rename it while we're at it, to make the new meaning clearer. Task-number: QTBUG-54019 Change-Id: I1fe9f4a7f71d037a542ffefd0f3aff6f52a80a40 Reviewed-by: Robin Burchell --- src/quick/items/qquickitem.cpp | 19 ++++++++++--------- src/quick/items/qquickitem_p.h | 2 +- src/quick/items/qquickwindow.cpp | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 17081eca17..7417dd64cc 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2918,7 +2918,7 @@ void QQuickItemPrivate::addChild(QQuickItem *child) if (childPrivate->hasCursorInChild && !hasCursorInChild) setHasCursorInChild(true); #endif - if (childPrivate->hasHoverInChild && !hasHoverInChild) + if (childPrivate->subtreeHoverEnabled && !subtreeHoverEnabled) setHasHoverInChild(true); markSortedChildrenDirty(child); @@ -2945,7 +2945,7 @@ void QQuickItemPrivate::removeChild(QQuickItem *child) if (childPrivate->hasCursorInChild && hasCursorInChild) setHasCursorInChild(false); #endif - if (childPrivate->hasHoverInChild && hasHoverInChild) + if (childPrivate->subtreeHoverEnabled && subtreeHoverEnabled) setHasHoverInChild(false); markSortedChildrenDirty(child); @@ -3169,7 +3169,7 @@ QQuickItemPrivate::QQuickItemPrivate() , culled(false) , hasCursor(false) , hasCursorInChild(false) - , hasHoverInChild(false) + , subtreeHoverEnabled(false) , activeFocusOnTab(false) , implicitAntialiasing(false) , antialiasingValid(false) @@ -7070,18 +7070,19 @@ void QQuickItemPrivate::setHasHoverInChild(bool hasHover) Q_Q(QQuickItem); // if we're asked to turn it off (because of a setAcceptHoverEvents call, or a node - // removal) then we should check our children and make sure it's really ok - // to turn it off. - if (!hasHover && hasHoverInChild) { + // removal) then we should make sure it's really ok to turn it off. + if (!hasHover && subtreeHoverEnabled) { + if (hoverEnabled) + return; // nope! sorry, I need hover myself foreach (QQuickItem *otherChild, childItems) { QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild); - if (otherChildPrivate->hasHoverInChild) + if (otherChildPrivate->subtreeHoverEnabled || otherChildPrivate->hoverEnabled) return; // nope! sorry, something else wants it kept on. } } - qCDebug(DBG_HOVER_TRACE) << q << hasHoverInChild << "->" << hasHover; - hasHoverInChild = hasHover; + qCDebug(DBG_HOVER_TRACE) << q << subtreeHoverEnabled << "->" << hasHover; + subtreeHoverEnabled = hasHover; QQuickItem *parent = q->parentItem(); if (parent) { QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parent); diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 477071f7b3..0c5a8abb4a 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -429,7 +429,7 @@ public: bool hasCursor:1; bool hasCursorInChild:1; // Bit 32 - bool hasHoverInChild:1; + bool subtreeHoverEnabled:1; bool activeFocusOnTab:1; bool implicitAntialiasing:1; bool antialiasingValid:1; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index eb035c571d..98e450abdb 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1789,8 +1789,8 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce return false; } - qCDebug(DBG_HOVER_TRACE) << q << item << scenePos << lastScenePos << "hasHoverInChild" << itemPrivate->hasHoverInChild; - if (itemPrivate->hasHoverInChild) { + qCDebug(DBG_HOVER_TRACE) << q << item << scenePos << lastScenePos << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled; + if (itemPrivate->subtreeHoverEnabled) { QList children = itemPrivate->paintOrderChildItems(); for (int ii = children.count() - 1; ii >= 0; --ii) { QQuickItem *child = children.at(ii); -- cgit v1.2.3 From e88500ff2518de358c0b5e9a7e8e0d7c33bfeadd Mon Sep 17 00:00:00 2001 From: Frank Meerkoetter Date: Sat, 11 Jun 2016 22:23:41 +0200 Subject: Increase test coverage for the V4 memory manager This commit adds a small test that exercises a number of code paths inside qv4mm.cpp which are normally gated via environment variables. Change-Id: Ibe959387a9b86ce68df258513446d2165fe06ee2 Reviewed-by: Simon Hausmann --- src/qml/memory/qv4mm.cpp | 10 +++---- src/qml/memory/qv4mm_p.h | 4 +++ tests/auto/qml/qml.pro | 3 +- tests/auto/qml/qv4mm/qv4mm.pro | 8 ++++++ tests/auto/qml/qv4mm/tst_qv4mm.cpp | 58 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 tests/auto/qml/qv4mm/qv4mm.pro create mode 100644 tests/auto/qml/qv4mm/tst_qv4mm.cpp diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 226358074b..8adc783ab2 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -79,9 +79,9 @@ static uint maxShiftValue() static uint result = 0; if (!result) { result = 6; - if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAXBLOCK_SHIFT"))) { + if (Q_UNLIKELY(qEnvironmentVariableIsSet(QV4_MM_MAXBLOCK_SHIFT))) { bool ok; - const uint overrideValue = qgetenv("QV4_MM_MAXBLOCK_SHIFT").toUInt(&ok); + const uint overrideValue = qgetenv(QV4_MM_MAXBLOCK_SHIFT).toUInt(&ok); if (ok && overrideValue <= 11 && overrideValue > 0) result = overrideValue; } @@ -94,9 +94,9 @@ static std::size_t maxChunkSizeValue() static std::size_t result = 0; if (!result) { result = 32 * 1024; - if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAX_CHUNK_SIZE"))) { + if (Q_UNLIKELY(qEnvironmentVariableIsSet(QV4_MM_MAX_CHUNK_SIZE))) { bool ok; - const std::size_t overrideValue = qgetenv("QV4_MM_MAX_CHUNK_SIZE").toUInt(&ok); + const std::size_t overrideValue = qgetenv(QV4_MM_MAX_CHUNK_SIZE).toUInt(&ok); if (ok) result = overrideValue; } @@ -170,7 +170,7 @@ struct MemoryManager::Data , maxShift(maxShiftValue()) , gcBlocked(false) , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) - , gcStats(!qEnvironmentVariableIsEmpty("QV4_MM_STATS")) + , gcStats(!qEnvironmentVariableIsEmpty(QV4_MM_STATS)) { memset(nonFullChunks, 0, sizeof(nonFullChunks)); memset(nChunks, 0, sizeof(nChunks)); diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index e169675f7d..026cbd8c6b 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -59,6 +59,10 @@ //#define DETAILED_MM_STATS +#define QV4_MM_MAXBLOCK_SHIFT "QV4_MM_MAXBLOCK_SHIFT" +#define QV4_MM_MAX_CHUNK_SIZE "QV4_MM_MAX_CHUNK_SIZE" +#define QV4_MM_STATS "QV4_MM_STATS" + QT_BEGIN_NAMESPACE namespace QV4 { diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 33b8d5d983..a1daa7a0c4 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -62,7 +62,8 @@ PRIVATETESTS += \ qqmltranslation \ qqmlimport \ qqmlobjectmodel \ - qmldiskcache + qmldiskcache \ + qv4mm qtHaveModule(widgets) { PUBLICTESTS += \ diff --git a/tests/auto/qml/qv4mm/qv4mm.pro b/tests/auto/qml/qv4mm/qv4mm.pro new file mode 100644 index 0000000000..d9b749af4a --- /dev/null +++ b/tests/auto/qml/qv4mm/qv4mm.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +TARGET = tst_qv4mm +osx:CONFIG -= app_bundle + +SOURCES += tst_qv4mm.cpp + +QT += qml qml-private testlib + diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp new file mode 100644 index 0000000000..d4ba363d00 --- /dev/null +++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 basysKom GmbH. +** 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 + +class tst_qv4mm : public QObject +{ + Q_OBJECT + +private slots: + void gcStats(); + void tweaks(); +}; + +void tst_qv4mm::gcStats() +{ + qputenv(QV4_MM_STATS, "1"); + QQmlEngine engine; + engine.collectGarbage(); +} + +void tst_qv4mm::tweaks() +{ + qputenv(QV4_MM_MAXBLOCK_SHIFT, "5"); + qputenv(QV4_MM_MAX_CHUNK_SIZE, "65536"); + QQmlEngine engine; +} + +QTEST_MAIN(tst_qv4mm) + +#include "tst_qv4mm.moc" -- cgit v1.2.3 From 7c8441feced8ed41621cd2aad09c1f6d1da7e2ff Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 17 Jun 2016 14:11:22 +0200 Subject: Clean up pragma singleton handling in the type loader Get rid of the m_isSingleton boolean by checking the CompiledData::Unit::flags after the type compilation. This is more compact and makes the singleton type checks independent from the IR. Change-Id: I04189d284e6ea275ac8540918836b641914e7588 Reviewed-by: Lars Knoll --- src/qml/qml/qqmltypeloader.cpp | 89 +++++++--------------- src/qml/qml/qqmltypeloader_p.h | 2 - .../qqmllanguage/data/singletonTest12.error.txt | 2 +- .../qml/qqmllanguage/data/singletonTest4.error.txt | 2 +- 4 files changed, 29 insertions(+), 66 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index e11507a56a..2efc1ba139 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1284,7 +1284,7 @@ void QQmlTypeLoader::shutdownThread() } QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader) - : QQmlDataBlob(url, type, loader), m_importCache(loader), m_isSingleton(false) + : QQmlDataBlob(url, type, loader), m_importCache(loader) { } @@ -1448,51 +1448,6 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL return true; } -bool QQmlTypeLoader::Blob::addPragma(const QmlIR::Pragma &pragma, QList *errors) -{ - Q_ASSERT(errors); - - if (pragma.type == QmlIR::Pragma::PragmaSingleton) { - QUrl myUrl = finalUrl(); - - QQmlType *ret = QQmlMetaType::qmlType(myUrl, true); - if (!ret) { - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent.")); - error.setUrl(myUrl); - error.setLine(pragma.location.line); - error.setColumn(pragma.location.column); - errors->prepend(error); - return false; - } - - if (!ret->isCompositeSingleton()) { - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(ret->qmlTypeName())); - error.setUrl(myUrl); - error.setLine(pragma.location.line); - error.setColumn(pragma.location.column); - errors->prepend(error); - return false; - } - // This flag is used for error checking when a qmldir file marks a type as - // composite singleton, but there is no pragma Singleton defined in QML. - m_isSingleton = true; - } else { - QQmlError error; - error.setDescription(QLatin1String("Invalid pragma")); - error.setUrl(finalUrl()); - error.setLine(pragma.location.line); - error.setColumn(pragma.location.column); - errors->prepend(error); - return false; - } - - return true; -} - - - void QQmlTypeLoader::Blob::dependencyError(QQmlDataBlob *blob) { if (blob->type() == QQmlDataBlob::QmldirFile) { @@ -2121,18 +2076,36 @@ void QQmlTypeData::done() } } - // If the type is CompositeSingleton but there was no pragma Singleton in the - // QML file, lets report an error. - QQmlType *type = QQmlMetaType::qmlType(url(), true); - if (!isError() && type && type->isCompositeSingleton() && !m_isSingleton) { - QString typeName = type->qmlTypeName(); - setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); - } - // Compile component if (!isError()) compile(); + if (!isError()) { + QQmlType *type = QQmlMetaType::qmlType(finalUrl(), true); + if (m_compiledData && m_compiledData->data->flags & QV4::CompiledData::Unit::IsSingleton) { + if (!type) { + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent.")); + setError(error); + } else if (!type->isCompositeSingleton()) { + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type->qmlTypeName())); + setError(error); + } + } else { + // If the type is CompositeSingleton but there was no pragma Singleton in the + // QML file, lets report an error. + if (type && type->isCompositeSingleton()) { + QString typeName = type->qmlTypeName(); + setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); + } + } + } + + if (isError()) { + m_compiledData = nullptr; + } + m_document.reset(); m_typeReferences.clear(); m_implicitImport = 0; @@ -2248,14 +2221,6 @@ void QQmlTypeData::continueLoadFromIR() return; } } - - foreach (QmlIR::Pragma *pragma, m_document->pragmas) { - if (!addPragma(*pragma, &errors)) { - Q_ASSERT(errors.size()); - setError(errors); - return; - } - } } void QQmlTypeData::allDependenciesDone() diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index d476056ef1..fa9389e9ca 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -232,7 +232,6 @@ public: protected: bool addImport(const QV4::CompiledData::Import *import, QList *errors); - bool addPragma(const QmlIR::Pragma &pragma, QList *errors); bool fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList *errors); bool updateQmldir(QQmlQmldirData *data, const QV4::CompiledData::Import *import, QList *errors); @@ -249,7 +248,6 @@ public: virtual QString stringAt(int) const { return QString(); } QQmlImports m_importCache; - bool m_isSingleton; QHash m_unresolvedImports; QList m_qmldirs; }; diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt b/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt index 716cf5709a..b3082d80e6 100644 --- a/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt +++ b/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt @@ -1,2 +1,2 @@ 5:5:Type RegisteredCompositeType unavailable -2:1:pragma Singleton used with a non composite singleton type CompositeSingletonTest/RegisteredCompositeType +-1:-1:pragma Singleton used with a non composite singleton type CompositeSingletonTest/RegisteredCompositeType diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt b/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt index 77c26df310..ebeab6987b 100644 --- a/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt +++ b/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt @@ -1 +1 @@ -2:1:No matching type found, pragma Singleton files cannot be used by QQmlComponent. +-1:-1:No matching type found, pragma Singleton files cannot be used by QQmlComponent. -- cgit v1.2.3 From b9645cc5774a2497b2927b5fdad14c8c09a91fe2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 17 Jun 2016 14:19:34 +0200 Subject: Minor cleanup for implicit import handling We don't need the m_implicitImport member in the QQmlTypeData when it is used in only one function and generally a pointer into the memory pool. Change-Id: Ib3de07631a6626ba1ebdf9e6fef379af1231fd24 Reviewed-by: Lars Knoll --- src/qml/qml/qqmltypeloader.cpp | 15 +++++++-------- src/qml/qml/qqmltypeloader_p.h | 1 - 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 2efc1ba139..56dc4694e0 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1978,7 +1978,7 @@ QQmlTypeData::TypeDataCallback::~TypeDataCallback() QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) : QQmlTypeLoader::Blob(url, QmlFile, manager), - m_typesResolved(false), m_implicitImport(0), m_implicitImportLoaded(false) + m_typesResolved(false), m_implicitImportLoaded(false) { } @@ -2108,7 +2108,6 @@ void QQmlTypeData::done() m_document.reset(); m_typeReferences.clear(); - m_implicitImport = 0; } void QQmlTypeData::completed() @@ -2193,14 +2192,14 @@ void QQmlTypeData::continueLoadFromIR() return; // This qmldir is for the implicit import QQmlJS::MemoryPool *pool = m_document->jsParserEngine.pool(); - m_implicitImport = pool->New(); - m_implicitImport->uriIndex = m_document->registerString(QLatin1String(".")); - m_implicitImport->qualifierIndex = 0; // empty string - m_implicitImport->majorVersion = -1; - m_implicitImport->minorVersion = -1; + auto implicitImport = pool->New(); + implicitImport->uriIndex = m_document->registerString(QLatin1String(".")); + implicitImport->qualifierIndex = 0; // empty string + implicitImport->majorVersion = -1; + implicitImport->minorVersion = -1; QList errors; - if (!fetchQmldir(qmldirUrl, m_implicitImport, 1, &errors)) { + if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) { setError(errors); return; } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index fa9389e9ca..9fd7fb9f51 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -464,7 +464,6 @@ private: QList m_callbacks; - QV4::CompiledData::Import *m_implicitImport; bool m_implicitImportLoaded; bool loadImplicitImport(); }; -- cgit v1.2.3 From 1d515e2e0f58fd8ee60bc7f2b9a2b31a38c8ce43 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 17 Jun 2016 14:33:47 +0200 Subject: Script dependency handling cleanup The code for setting up CompiledData::m_dependentScripts is independent from the QML type compiler and can be moved into the type loader. This will also allow for re-use when using disk backed compilation units. Change-Id: I56ce5674c50dcca8fda294556a3fd31e073cf24d Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 23 ----------------------- src/qml/qml/qqmltypeloader.cpp | 22 +++++++++++++++++++++- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 766e1fd40f..e40666864c 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -113,28 +113,6 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() annotator.annotateBindingsToAliases(); } - // Collect imported scripts - const QList &scripts = typeData->resolvedScripts(); - QVector dependentScripts; - dependentScripts.reserve(scripts.count()); - for (int scriptIndex = 0; scriptIndex < scripts.count(); ++scriptIndex) { - const QQmlTypeData::ScriptReference &script = scripts.at(scriptIndex); - - QStringRef qualifier(&script.qualifier); - QString enclosingNamespace; - - const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); - if (lastDotIndex != -1) { - enclosingNamespace = qualifier.left(lastDotIndex).toString(); - qualifier = qualifier.mid(lastDotIndex+1); - } - - importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); - QQmlScriptData *scriptData = script.script->scriptData(); - scriptData->addref(); - dependentScripts << scriptData; - } - // Resolve component boundaries and aliases { @@ -186,7 +164,6 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit; compilationUnit = document->javaScriptCompilationUnit; compilationUnit->importCache = importCache; - compilationUnit->dependentScripts = dependentScripts; compilationUnit->resolvedTypes = resolvedTypes; compilationUnit->propertyCaches = std::move(m_propertyCaches); Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast(compilationUnit->data->nObjects)); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 56dc4694e0..0ba0384c60 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2102,7 +2102,27 @@ void QQmlTypeData::done() } } - if (isError()) { + if (!isError()) { + // Collect imported scripts + m_compiledData->dependentScripts.reserve(m_scripts.count()); + for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { + const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex); + + QStringRef qualifier(&script.qualifier); + QString enclosingNamespace; + + const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); + if (lastDotIndex != -1) { + enclosingNamespace = qualifier.left(lastDotIndex).toString(); + qualifier = qualifier.mid(lastDotIndex+1); + } + + m_compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); + QQmlScriptData *scriptData = script.script->scriptData(); + scriptData->addref(); + m_compiledData->dependentScripts << scriptData; + } + } else { m_compiledData = nullptr; } -- cgit v1.2.3 From 7a1b5b1cfece18d5f4b2d8beb340d3cbe8f54451 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Mon, 20 Jun 2016 12:30:28 +0200 Subject: Software Image Node: Do not make equality check in setTexture When setTexture was called on the Software image node, we were doing a check to see if the QSGTexture pointer was the same as the current texture assuming it meant that if they were the same pointer then it was in fact the same texture, and thus not dirty. This was a wrong assumption, because the texture data can be different but still use the same pointer. So the node should always respond by updating the texture when setTexture is called and mark the nodes material as dirty for the renderer. This allows canvas items to be updated at runtime with the software renderer. Change-Id: Idf504035a6aa858e865050a58460189a12e77ad4 Reviewed-by: Laszlo Agocs --- .../scenegraph/adaptations/software/qsgsoftwareimagenode.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp index 7dadc1d3d4..c92a032623 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp @@ -364,11 +364,9 @@ void QSGSoftwareImageNode::setSubSourceRect(const QRectF &rect) void QSGSoftwareImageNode::setTexture(QSGTexture *texture) { - if (m_texture != texture) { - m_texture = texture; - m_cachedMirroredPixmapIsDirty = true; - markDirty(DirtyMaterial); - } + m_texture = texture; + m_cachedMirroredPixmapIsDirty = true; + markDirty(DirtyMaterial); } void QSGSoftwareImageNode::setMirror(bool mirror) -- cgit v1.2.3 From c884f85863d5cbb90d8a1f3972ddb37b3d3f7e14 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 20 Jun 2016 08:32:21 +0200 Subject: Move type compilation independent bits out of QQmlTypeCompiler The type validation is something that works on the final compilation unit, so it can be done separately. The same applies to the composite type registration and object/binding calculation. Both steps will be shared with compilation units loaded from disk. Change-Id: I43636d7ac76077c76289d7c1c9eba5e9c6b8239a Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 29 ----------------------------- src/qml/compiler/qv4compileddata.cpp | 17 ++++++++++++++++- src/qml/compiler/qv4compileddata_p.h | 2 +- src/qml/qml/qqmltypeloader.cpp | 30 ++++++++++++++++++++++++++++++ src/qml/qml/qqmltypeloader_p.h | 1 + 5 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index e40666864c..d73ce6f09d 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -47,7 +47,6 @@ #include #include "qqmlpropertycachecreator_p.h" -#include "qqmlpropertyvalidator_p.h" #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -168,34 +167,6 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() compilationUnit->propertyCaches = std::move(m_propertyCaches); Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast(compilationUnit->data->nObjects)); - // Add to type registry of composites - if (compilationUnit->propertyCaches.needsVMEMetaObject(qmlUnit->indexOfRootObject)) - engine->registerInternalCompositeType(compilationUnit); - else { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject); - auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - if (typeRef->compilationUnit) { - compilationUnit->metaTypeId = typeRef->compilationUnit->metaTypeId; - compilationUnit->listMetaTypeId = typeRef->compilationUnit->listMetaTypeId; - } else { - compilationUnit->metaTypeId = typeRef->type->typeId(); - compilationUnit->listMetaTypeId = typeRef->type->qListTypeId(); - } - } - - { - // Sanity check property bindings - QQmlPropertyValidator validator(engine, *imports(), compilationUnit); - QVector errors = validator.validate(); - if (!errors.isEmpty()) { - for (const QQmlCompileError &error: qAsConst(errors)) - recordError(error); - return nullptr; - } - } - - compilationUnit->updateBindingAndObjectCounters(); if (errors.isEmpty()) return compilationUnit; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index d2587a547c..b05abdc0c8 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -239,8 +239,23 @@ IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjec return *it; } -void CompilationUnit::updateBindingAndObjectCounters() +void CompilationUnit::finalize(QQmlEnginePrivate *engine) { + // Add to type registry of composites + if (propertyCaches.needsVMEMetaObject(data->indexOfRootObject)) + engine->registerInternalCompositeType(this); + else { + const QV4::CompiledData::Object *obj = objectAt(data->indexOfRootObject); + auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + if (typeRef->compilationUnit) { + metaTypeId = typeRef->compilationUnit->metaTypeId; + listMetaTypeId = typeRef->compilationUnit->listMetaTypeId; + } else { + metaTypeId = typeRef->type->typeId(); + listMetaTypeId = typeRef->type->qListTypeId(); + } + } // Collect some data for instantiation later. int bindingCount = 0; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index b960901402..a9a0ebbf51 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -766,7 +766,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QHash> namedObjectsPerComponentCache; IdentifierHash namedObjectsPerComponent(int componentObjectIndex); - void updateBindingAndObjectCounters(); + void finalize(QQmlEnginePrivate *engine); int totalBindingsCount; // Number of bindings used in this type int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 0ba0384c60..1158cde5ca 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -445,6 +446,21 @@ void QQmlDataBlob::setError(const QQmlCompileError &error) setError(e); } +void QQmlDataBlob::setError(const QVector &errors) +{ + QList finalErrors; + finalErrors.reserve(errors.count()); + for (const QQmlCompileError &error: errors) { + QQmlError e; + e.setColumn(error.location.column); + e.setLine(error.location.line); + e.setDescription(error.description); + e.setUrl(url()); + finalErrors << e; + } + setError(finalErrors); +} + void QQmlDataBlob::setError(const QString &description) { QQmlError e; @@ -2080,6 +2096,20 @@ void QQmlTypeData::done() if (!isError()) compile(); + if (!isError()) { + QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + { + // Sanity check property bindings + QQmlPropertyValidator validator(engine, m_importCache, m_compiledData); + QVector errors = validator.validate(); + if (!errors.isEmpty()) { + setError(errors); + } + } + + m_compiledData->finalize(engine); + } + if (!isError()) { QQmlType *type = QQmlMetaType::qmlType(finalUrl(), true); if (m_compiledData && m_compiledData->data->flags & QV4::CompiledData::Unit::IsSingleton) { diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 9fd7fb9f51..2030dbf427 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -146,6 +146,7 @@ protected: void setError(const QQmlError &); void setError(const QList &errors); void setError(const QQmlCompileError &error); + void setError(const QVector &errors); void setError(const QString &description); void addDependency(QQmlDataBlob *); -- cgit v1.2.3 From cc4954e9646cd93d9710e4e28942d2a82762771e Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Mon, 20 Jun 2016 14:45:29 +0200 Subject: Software Renderer: Call QBackingStore::beginPaint with correct QRegion When rendering into QBackingStores, it is important to let the backingstore know what region you intended to paint into before you start rendering. Previously we were calling QBackingStore::beginPaint on the whole window area, but then only rendering and flushing for a subset of that area. This causes issues on platforms that depend on QBackingStore::beginPaint to be all area that will be repainted before the next flush, so that they can for example clear those areas. This change required a bit of extra plumbing in the Software Renderer, but now it is possible to calculate the area that will be repainted before painting it, and pass that region to QBackingStore::beginPaint(). Change-Id: I90be1916ebbe8fc182cd13246bb6690cc7e16d27 Reviewed-by: Laszlo Agocs --- .../software/qsgabstractsoftwarerenderer.cpp | 6 ++++- .../software/qsgabstractsoftwarerenderer_p.h | 2 +- .../adaptations/software/qsgsoftwarerenderer.cpp | 31 ++++++++++++++++++---- .../adaptations/software/qsgsoftwarerenderer_p.h | 3 +++ .../adaptations/software/qsgsoftwarerenderloop.cpp | 4 +-- 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp index eb0e26462a..26efba5b13 100644 --- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp @@ -149,7 +149,7 @@ void QSGAbstractSoftwareRenderer::buildRenderList() QSGSoftwareRenderListBuilder(this).visitChildren(rootNode()); } -void QSGAbstractSoftwareRenderer::optimizeRenderList() +QRegion QSGAbstractSoftwareRenderer::optimizeRenderList() { // Iterate through the renderlist from front to back // Objective is to update the dirty status and rects. @@ -212,9 +212,13 @@ void QSGAbstractSoftwareRenderer::optimizeRenderList() m_dirtyRegion += node->dirtyRegion(); } + QRegion updateRegion = m_dirtyRegion; + // Empty dirtyRegion m_dirtyRegion = QRegion(); m_obscuredRegion = QRegion(); + + return updateRegion; } void QSGAbstractSoftwareRenderer::setBackgroundColor(const QColor &color) diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h index a2e953f40d..73410b09f5 100644 --- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h @@ -78,7 +78,7 @@ public: protected: QRegion renderNodes(QPainter *painter); void buildRenderList(); - void optimizeRenderList(); + QRegion optimizeRenderList(); void setBackgroundColor(const QColor &color); void setBackgroundSize(const QSize &size); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp index ea00de7a66..7bf06f8081 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp @@ -45,6 +45,7 @@ #include "qsgsoftwarerenderablenode_p.h" #include +#include #include Q_LOGGING_CATEGORY(lcRenderer, "qt.scenegraph.softwarecontext.renderer") @@ -53,6 +54,8 @@ QT_BEGIN_NAMESPACE QSGSoftwareRenderer::QSGSoftwareRenderer(QSGRenderContext *context) : QSGAbstractSoftwareRenderer(context) + , m_paintDevice(nullptr) + , m_backingStore(nullptr) { } @@ -63,6 +66,13 @@ QSGSoftwareRenderer::~QSGSoftwareRenderer() void QSGSoftwareRenderer::setCurrentPaintDevice(QPaintDevice *device) { m_paintDevice = device; + m_backingStore = nullptr; +} + +void QSGSoftwareRenderer::setBackingStore(QBackingStore *backingStore) +{ + m_backingStore = backingStore; + m_paintDevice = nullptr; } QRegion QSGSoftwareRenderer::flushRegion() const @@ -82,18 +92,19 @@ void QSGSoftwareRenderer::renderScene(uint) void QSGSoftwareRenderer::render() { - if (!m_paintDevice) + if (!m_paintDevice && !m_backingStore) return; + // If there is a backingstore, set the current paint device + if (m_backingStore) + m_paintDevice = m_backingStore->paintDevice(); + QElapsedTimer renderTimer; setBackgroundColor(clearColor()); setBackgroundSize(QSize(m_paintDevice->width() / m_paintDevice->devicePixelRatio(), m_paintDevice->height() / m_paintDevice->devicePixelRatio())); - QPainter painter(m_paintDevice); - painter.setRenderHint(QPainter::Antialiasing); - // Build Renderlist // The renderlist is created by visiting each node in the tree and when a // renderable node is reach, we find the coorosponding RenderableNode object @@ -113,13 +124,23 @@ void QSGSoftwareRenderer::render() // side effect of this is that additional nodes may need to be marked dirty to // force a repaint. It is also important that any item that needs to be // repainted only paints what is needed, via the use of clip regions. - optimizeRenderList(); + const QRegion updateRegion = optimizeRenderList(); qint64 optimizeRenderListTime = renderTimer.restart(); + // If Rendering to a backingstore, prepare it to be updated + if (m_backingStore != nullptr) + m_backingStore->beginPaint(updateRegion); + + QPainter painter(m_paintDevice); + painter.setRenderHint(QPainter::Antialiasing); + // Render the contents Renderlist m_flushRegion = renderNodes(&painter); qint64 renderTime = renderTimer.elapsed(); + if (m_backingStore != nullptr) + m_backingStore->endPaint(); + qCDebug(lcRenderer) << "render" << m_flushRegion << buildRenderListTime << optimizeRenderListTime << renderTime; } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h index e2b8bcddca..a201e4887e 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h @@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE class QPaintDevice; +class QBackingStore; class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderer : public QSGAbstractSoftwareRenderer { @@ -64,6 +65,7 @@ public: virtual ~QSGSoftwareRenderer(); void setCurrentPaintDevice(QPaintDevice *device); + void setBackingStore(QBackingStore *backingStore); QRegion flushRegion() const; protected: @@ -72,6 +74,7 @@ protected: private: QPaintDevice* m_paintDevice; + QBackingStore* m_backingStore; QRegion m_flushRegion; }; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index 5292e1371f..0b111c7b19 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -156,11 +156,9 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window) //Tell the renderer about the windows backing store auto softwareRenderer = static_cast(cd->renderer); if (softwareRenderer) - softwareRenderer->setCurrentPaintDevice(m_backingStores[window]->paintDevice()); + softwareRenderer->setBackingStore(m_backingStores[window]); - m_backingStores[window]->beginPaint(QRect(0, 0, window->width(), window->height())); cd->renderSceneGraph(window->size()); - m_backingStores[window]->endPaint(); if (profileFrames) renderTime = renderTimer.nsecsElapsed(); -- cgit v1.2.3 From b30d970691e9a6abf56bacb53a2d3f734d6f031c Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 20 Jun 2016 11:00:39 +0200 Subject: Add a tab widget to QQuickWidget example ...and include a tab with custom OpenGL rendering. Tab widgets are interesting because they are commonly used in applications in combination with widgets like QOpenGLWidget and QQuickWidget, and because they typically trigger rapid show-hide sequences which QQuickWidget does not always handle as well as it could. Thus this serves both as a helpful example and a useful testing tool. Plus it exposes at least two bugs already, namely that the continuous animation in the QQuickFBO does not start when switching to the tab containing the QQuickWidget in question, and that it is impossible to integrate third-party rendering code in a robust manner due to QQuickWidget destroying everything whenever hiding the widget. This is not what happens with a QQuickView and the normal render loops. Task-number: QTBUG-54133 Change-Id: Ie7d92cd0f685e4a26f4bb351cc023eb697a36bf4 Reviewed-by: Andy Nichols --- .../quick/quickwidgets/quickwidget/customgl.qml | 59 ++++++++++++++ examples/quick/quickwidgets/quickwidget/fbitem.cpp | 90 ++++++++++++++++++++++ examples/quick/quickwidgets/quickwidget/fbitem.h | 53 +++++++++++++ examples/quick/quickwidgets/quickwidget/main.cpp | 38 ++++++++- .../quick/quickwidgets/quickwidget/quickwidget.pro | 3 +- .../quick/quickwidgets/quickwidget/quickwidget.qrc | 2 + .../quickwidgets/quickwidget/rotatingsquaretab.qml | 66 ++++++++++++++++ 7 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 examples/quick/quickwidgets/quickwidget/customgl.qml create mode 100644 examples/quick/quickwidgets/quickwidget/fbitem.cpp create mode 100644 examples/quick/quickwidgets/quickwidget/fbitem.h create mode 100644 examples/quick/quickwidgets/quickwidget/rotatingsquaretab.qml diff --git a/examples/quick/quickwidgets/quickwidget/customgl.qml b/examples/quick/quickwidgets/quickwidget/customgl.qml new file mode 100644 index 0000000000..81e33e1ac9 --- /dev/null +++ b/examples/quick/quickwidgets/quickwidget/customgl.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QuickWidgetExample 1.0 + +Rectangle { + color: "lightGray" + + FbItem { + anchors.fill: parent + anchors.margins: 10 + } + + Text { + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.margins: 15 + text: "QQuickFramebufferObject with animated clear color" + color: "white" + } +} diff --git a/examples/quick/quickwidgets/quickwidget/fbitem.cpp b/examples/quick/quickwidgets/quickwidget/fbitem.cpp new file mode 100644 index 0000000000..fc2a4ea7ad --- /dev/null +++ b/examples/quick/quickwidgets/quickwidget/fbitem.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "fbitem.h" +#include +#include +#include +#include + +#ifndef QT_NO_OPENGL +class FbRenderer : public QQuickFramebufferObject::Renderer +{ +public: + FbRenderer() : c(0), dir(1) { } + + // The lifetime of the FBO and this class depends on how QQuickWidget + // manages the scenegraph and context when it comes to showing and hiding + // the widget. The actual behavior is proven by the debug prints. + ~FbRenderer() { + qDebug("FbRenderer destroyed"); + } + + void render() { + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + f->glClearColor(c, 0, 0, 1); + f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + c += 0.01f * dir; + if (c >= 1.0f || c <= 0.0f) + dir *= -1; + update(); + } + + QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) { + qDebug() << "Creating FBO" << size; + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + return new QOpenGLFramebufferObject(size, format); + } + +private: + float c; + int dir; +}; +#endif + +QQuickFramebufferObject::Renderer *FbItem::createRenderer() const +{ +#ifndef QT_NO_OPENGL + return new FbRenderer; +#else + return nullptr; +#endif +} diff --git a/examples/quick/quickwidgets/quickwidget/fbitem.h b/examples/quick/quickwidgets/quickwidget/fbitem.h new file mode 100644 index 0000000000..59280eb3b8 --- /dev/null +++ b/examples/quick/quickwidgets/quickwidget/fbitem.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FBITEM_H +#define FBITEM_H + +#include + +class FbItem : public QQuickFramebufferObject +{ + Q_OBJECT +public: + Renderer *createRenderer() const; +}; + +#endif diff --git a/examples/quick/quickwidgets/quickwidget/main.cpp b/examples/quick/quickwidgets/quickwidget/main.cpp index 65258d958e..2f73447a5d 100644 --- a/examples/quick/quickwidgets/quickwidget/main.cpp +++ b/examples/quick/quickwidgets/quickwidget/main.cpp @@ -41,6 +41,7 @@ #include #include #include +#include "fbitem.h" class MainWindow : public QMainWindow { Q_OBJECT @@ -52,6 +53,7 @@ private slots: void sceneGraphError(QQuickWindow::SceneGraphError error, const QString &message); void grabToFile(); void renderToFile(); + void createQuickWidgetsInTabs(QMdiArea *mdiArea); private: QQuickWidget *m_quickWidget; @@ -74,7 +76,7 @@ MainWindow::MainWindow() QLCDNumber *lcd = new QLCDNumber; lcd->display(1337); lcd->setMinimumSize(250,100); - centralWidget ->addSubWindow(lcd); + centralWidget->addSubWindow(lcd); QUrl source("qrc:quickwidget/rotatingsquare.qml"); @@ -86,14 +88,42 @@ MainWindow::MainWindow() m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView ); m_quickWidget->setSource(source); - centralWidget ->addSubWindow(m_quickWidget); + centralWidget->addSubWindow(m_quickWidget); setCentralWidget(centralWidget); QMenu *fileMenu = menuBar()->addMenu(tr("&File")); - fileMenu->addAction(tr("Grab to imFage"), this, &MainWindow::grabToFile); + fileMenu->addAction(tr("Grab to image"), this, &MainWindow::grabToFile); fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToFile); fileMenu->addAction(tr("Quit"), qApp, &QCoreApplication::quit); + + QMenu *windowMenu = menuBar()->addMenu(tr("&Window")); + windowMenu->addAction(tr("Add tab widget"), this, + [this, centralWidget] { createQuickWidgetsInTabs(centralWidget); }); +} + +void MainWindow::createQuickWidgetsInTabs(QMdiArea *mdiArea) +{ + QTabWidget *tabWidget = new QTabWidget; + + const QSize size(400, 400); + + QQuickWidget *w = new QQuickWidget; + w->resize(size); + w->setResizeMode(QQuickWidget::SizeRootObjectToView); + w->setSource(QUrl("qrc:quickwidget/rotatingsquaretab.qml")); + + tabWidget->addTab(w, tr("Plain Quick content")); + + w = new QQuickWidget; + w->resize(size); + w->setResizeMode(QQuickWidget::SizeRootObjectToView); + w->setSource(QUrl("qrc:quickwidget/customgl.qml")); + + tabWidget->addTab(w, tr("Custom OpenGL drawing")); + + mdiArea->addSubWindow(tabWidget); + tabWidget->show(); } void MainWindow::quickWidgetStatusChanged(QQuickWidget::Status status) @@ -139,6 +169,8 @@ int main(int argc, char **argv) { QApplication app(argc, argv); + qmlRegisterType("QuickWidgetExample", 1, 0, "FbItem"); + MainWindow mainWindow; mainWindow.show(); diff --git a/examples/quick/quickwidgets/quickwidget/quickwidget.pro b/examples/quick/quickwidgets/quickwidget/quickwidget.pro index 04fb5541a7..5be006f7fa 100644 --- a/examples/quick/quickwidgets/quickwidget/quickwidget.pro +++ b/examples/quick/quickwidgets/quickwidget/quickwidget.pro @@ -3,7 +3,8 @@ QT += core gui quick widgets quickwidgets TARGET = quickwidget TEMPLATE = app -SOURCES += main.cpp +SOURCES += main.cpp fbitem.cpp +HEADERS += fbitem.h RESOURCES += quickwidget.qrc diff --git a/examples/quick/quickwidgets/quickwidget/quickwidget.qrc b/examples/quick/quickwidgets/quickwidget/quickwidget.qrc index c073b7b80d..85a49b75ca 100644 --- a/examples/quick/quickwidgets/quickwidget/quickwidget.qrc +++ b/examples/quick/quickwidgets/quickwidget/quickwidget.qrc @@ -1,5 +1,7 @@ rotatingsquare.qml + rotatingsquaretab.qml + customgl.qml diff --git a/examples/quick/quickwidgets/quickwidget/rotatingsquaretab.qml b/examples/quick/quickwidgets/quickwidget/rotatingsquaretab.qml new file mode 100644 index 0000000000..51c17b9ffb --- /dev/null +++ b/examples/quick/quickwidgets/quickwidget/rotatingsquaretab.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + gradient: Gradient { + GradientStop { position: 0; color: "steelblue" } + GradientStop { position: 1; color: "black" } + } + + Rectangle { + property int d: 100 + id: square + width: d + height: d + anchors.centerIn: parent + color: "green" + NumberAnimation on rotation { from: 360; to: 0; duration: 4000; loops: Animation.Infinite; } + } + + Text { + anchors.centerIn: parent + text: "Qt Quick running in a tab widget" + color: "purple" + font.bold: true + font.pointSize: 14 + } +} -- cgit v1.2.3 From c15fea6691485501547449e5f78725d3b6f1c968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Mon, 20 Jun 2016 13:48:12 +0200 Subject: QQuickWidget: Propagate screen changes. This is required to make Qt Quick pick up changes to the devicePixelRatio. Failure to do so prevents a proper update of text nodes, as seen in the Qt Creator welcome screen. Propagate the new screen to the offscreenWindow, offscreenSurface and context. [ChangeLog][QtQuick] QQuickWidget now properly repaints text on high-DPI screen changes. Change-Id: I8f0b9f2f8768f99e293de018ae56d50ddf20b43a Reviewed-by: Laszlo Agocs --- src/quickwidgets/qquickwidget.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 1a840b44bd..6709d88fa3 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1217,6 +1217,17 @@ bool QQuickWidget::event(QEvent *e) break; case QEvent::ScreenChangeInternal: + if (QWindow *window = this->window()->windowHandle()) { + QScreen *newScreen = window->screen(); + + if (d->offscreenWindow) + d->offscreenWindow->setScreen(newScreen); + if (d->offscreenSurface) + d->offscreenSurface->setScreen(newScreen); + if (d->context) + d->context->setScreen(newScreen); + } + if (d->fbo) { // This will check the size taking the devicePixelRatio into account // and recreate if needed. -- cgit v1.2.3 From 99e181a5434389e13b099592f984a2ff1f3583e1 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Mon, 20 Jun 2016 16:36:31 +0200 Subject: qmltest:doublevalidator - fix locale The string "1.0" can fail validation due to locale (requiring ',' for example). Task-number: QTBUG-53779 Change-Id: I44b2b6886d96a7a32668bea56b5f34bb8d9db8d3 Reviewed-by: Shawn Rutledge --- tests/auto/qmltest/BLACKLIST | 2 -- tests/auto/qmltest/textinput/tst_textinput.qml | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/auto/qmltest/BLACKLIST b/tests/auto/qmltest/BLACKLIST index 628290f2b4..a53afb1358 100644 --- a/tests/auto/qmltest/BLACKLIST +++ b/tests/auto/qmltest/BLACKLIST @@ -10,5 +10,3 @@ linux [Text::test_linecount] osx windows -[TextInput::test_doublevalidators] -osx diff --git a/tests/auto/qmltest/textinput/tst_textinput.qml b/tests/auto/qmltest/textinput/tst_textinput.qml index 62659a2188..51868ec8aa 100644 --- a/tests/auto/qmltest/textinput/tst_textinput.qml +++ b/tests/auto/qmltest/textinput/tst_textinput.qml @@ -277,6 +277,7 @@ Item { } function test_doublevalidators(row) { + txtdoublevalidator.validator.locale = "C" compare(txtdoublevalidator.validator.top, 2.0) compare(txtdoublevalidator.validator.bottom, 1.0) txtdoublevalidator.text = row.testnumber; -- cgit v1.2.3 From ec77984db9df6bada82a84d08a13ac1b0d6c8a9d Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Tue, 21 Jun 2016 12:24:38 +0200 Subject: qmltest::item-grabber - fix image paths When running qmltest::itemgrabber we save grabbed image into the qmltest dir but using this name later as 'source' (relative) url we fail to load image. Task-number: QTBUG-53782 Change-Id: Ibd1f32d8bc13ff155b23491401075638cef16987 Reviewed-by: Shawn Rutledge --- .../itemgrabber/tst_itemgrabber.qml | 152 --------------------- tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml | 152 +++++++++++++++++++++ 2 files changed, 152 insertions(+), 152 deletions(-) delete mode 100644 tests/auto/qmltest-blacklist/itemgrabber/tst_itemgrabber.qml create mode 100644 tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml diff --git a/tests/auto/qmltest-blacklist/itemgrabber/tst_itemgrabber.qml b/tests/auto/qmltest-blacklist/itemgrabber/tst_itemgrabber.qml deleted file mode 100644 index 5d65a1666c..0000000000 --- a/tests/auto/qmltest-blacklist/itemgrabber/tst_itemgrabber.qml +++ /dev/null @@ -1,152 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jolla Ltd, author: -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -import QtQuick 2.4 -import QtTest 1.1 - -Item { - id: root; - width: 400 - height: 400 - - TestCase { - id: testCase - name: "item-grabber" - when: imageOnDisk.ready && imageOnDiskSmall.ready && imageInCache.ready && imageInCacheSmall.ready - function test_endresult() { - var image = grabImage(root); - - // imageOnDisk at (0, 0) - (100x100) - compare(imageOnDisk.width, 100); - compare(imageOnDisk.height, 100); - verify(image.pixel(0, 0) === Qt.rgba(1, 0, 0, 1)); // Use verify because compare doesn't support colors (QTBUG-34878) - verify(image.pixel(99, 99) === Qt.rgba(0, 0, 1, 1)); - - // imageOnDiskSmall at (100, 0) - 50x50 - compare(imageOnDiskSmall.width, 50); - compare(imageOnDiskSmall.height, 50); - verify(image.pixel(100, 0) === Qt.rgba(1, 0, 0, 1)); - verify(image.pixel(149, 49) === Qt.rgba(0, 0, 1, 1)); - - // imageInCache at (0, 100) - 100x100 - compare(imageInCache.width, 100); - compare(imageInCache.height, 100); - verify(image.pixel(0, 100) === Qt.rgba(1, 0, 0, 1)); - verify(image.pixel(99, 199) === Qt.rgba(0, 0, 1, 1)); - - // imageInCacheSmall at (100, 100) - 50x50 - compare(imageInCacheSmall.width, 50); - compare(imageInCacheSmall.height, 50); - verify(image.pixel(100, 100) === Qt.rgba(1, 0, 0, 1)); - verify(image.pixel(149, 149) === Qt.rgba(0, 0, 1, 1)); - - // After all that has been going on, it should only have been called that one time.. - compare(imageOnDisk.callCount, 1); - } - - onWindowShownChanged: { - box.grabToImage(imageOnDisk.handleGrab); - box.grabToImage(imageOnDiskSmall.handleGrab, Qt.size(50, 50)); - box.grabToImage(imageInCache.handleGrab); - box.grabToImage(imageInCacheSmall.handleGrab, Qt.size(50, 50)); - } - - } - - Rectangle { - id: box - width: 100 - height: 100 - color: "red"; - - visible: false - - Rectangle { - anchors.bottom: parent.bottom; - anchors.right: parent.right; - width: 10 - height: 10 - color: "blue"; - } - } - - Image { - id: imageOnDisk - x: 0 - y: 0 - property int callCount: 0; - property bool ready: false; - function handleGrab(result) { - if (!result.saveToFile("image.png")) - print("Error: Failed to save image to disk..."); - source = "image.png"; - ready = true; - ++callCount; - } - } - - Image { - id: imageOnDiskSmall - x: 100 - y: 0 - property bool ready: false; - function handleGrab(result) { - if (!result.saveToFile("image_small.png")) - print("Error: Failed to save image to disk..."); - source = "image_small.png"; - ready = true; - } - } - - Image { - id: imageInCache - x: 0 - y: 100 - property bool ready: false; - function handleGrab(result) { - source = result.url; - ready = true; - } - } - - Image { - id: imageInCacheSmall - x: 100 - y: 100 - property bool ready: false; - function handleGrab(result) { - source = result.url; - ready = true; - } - } -} diff --git a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml new file mode 100644 index 0000000000..d366b2183a --- /dev/null +++ b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtTest 1.1 + +Item { + id: root; + width: 400 + height: 400 + + TestCase { + id: testCase + name: "item-grabber" + when: imageOnDisk.ready && imageOnDiskSmall.ready && imageInCache.ready && imageInCacheSmall.ready + function test_endresult() { + var image = grabImage(root); + + // imageOnDisk at (0, 0) - (100x100) + compare(imageOnDisk.width, 100); + compare(imageOnDisk.height, 100); + verify(image.pixel(0, 0) === Qt.rgba(1, 0, 0, 1)); // Use verify because compare doesn't support colors (QTBUG-34878) + verify(image.pixel(99, 99) === Qt.rgba(0, 0, 1, 1)); + + // imageOnDiskSmall at (100, 0) - 50x50 + compare(imageOnDiskSmall.width, 50); + compare(imageOnDiskSmall.height, 50); + verify(image.pixel(100, 0) === Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(149, 49) === Qt.rgba(0, 0, 1, 1)); + + // imageInCache at (0, 100) - 100x100 + compare(imageInCache.width, 100); + compare(imageInCache.height, 100); + verify(image.pixel(0, 100) === Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(99, 199) === Qt.rgba(0, 0, 1, 1)); + + // imageInCacheSmall at (100, 100) - 50x50 + compare(imageInCacheSmall.width, 50); + compare(imageInCacheSmall.height, 50); + verify(image.pixel(100, 100) === Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(149, 149) === Qt.rgba(0, 0, 1, 1)); + + // After all that has been going on, it should only have been called that one time.. + compare(imageOnDisk.callCount, 1); + } + + onWindowShownChanged: { + box.grabToImage(imageOnDisk.handleGrab); + box.grabToImage(imageOnDiskSmall.handleGrab, Qt.size(50, 50)); + box.grabToImage(imageInCache.handleGrab); + box.grabToImage(imageInCacheSmall.handleGrab, Qt.size(50, 50)); + } + + } + + Rectangle { + id: box + width: 100 + height: 100 + color: "red"; + + visible: false + + Rectangle { + anchors.bottom: parent.bottom; + anchors.right: parent.right; + width: 10 + height: 10 + color: "blue"; + } + } + + Image { + id: imageOnDisk + x: 0 + y: 0 + property int callCount: 0; + property bool ready: false; + function handleGrab(result) { + if (!result.saveToFile("itemgrabber/image.png")) + print("Error: Failed to save image to disk..."); + source = "image.png"; + ready = true; + ++callCount; + } + } + + Image { + id: imageOnDiskSmall + x: 100 + y: 0 + property bool ready: false; + function handleGrab(result) { + if (!result.saveToFile("itemgrabber/image_small.png")) + print("Error: Failed to save image to disk..."); + source = "image_small.png"; + ready = true; + } + } + + Image { + id: imageInCache + x: 0 + y: 100 + property bool ready: false; + function handleGrab(result) { + source = result.url; + ready = true; + } + } + + Image { + id: imageInCacheSmall + x: 100 + y: 100 + property bool ready: false; + function handleGrab(result) { + source = result.url; + ready = true; + } + } +} -- cgit v1.2.3 From 4c1dd3efb0a28c022e0968ba80737de8694c6e4c Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Tue, 21 Jun 2016 09:50:10 +0200 Subject: qmltest - remove 'linecount' from BLACKLIST Must be fixed on OS X now. Task-number: QTBUG-53778 Change-Id: If94085210115534cf7a467100ec00fc419474c67 Reviewed-by: Shawn Rutledge --- tests/auto/qmltest/BLACKLIST | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/auto/qmltest/BLACKLIST b/tests/auto/qmltest/BLACKLIST index a53afb1358..2ac01edc31 100644 --- a/tests/auto/qmltest/BLACKLIST +++ b/tests/auto/qmltest/BLACKLIST @@ -7,6 +7,4 @@ linux [ListView::test_listInteractiveCurrentIndexEnforce] linux -[Text::test_linecount] -osx windows -- cgit v1.2.3 From 5d23470b8d0bacb0e0ba074672f94e526cc9e456 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 1 Jun 2016 11:40:45 +0200 Subject: Report changes correctly when inserting into a ListView Commit v5.6.0-beta1~7 (ListView: Sanitize visibleItems list after model insertions, 2015-12-07) introduced sanitizing of the container of visibleItems, but it did not affect the return value of the QQuickListViewPrivate::applyInsertionChange function. The return value is used in QQuickItemViewPrivate::layout() to determine whether the layouting should proceed, or an early return is possible instead. If the layouting does not proceed, then the newly inserted visible items do not get painted, resulting in the linked bug. The return value of the QQuickListViewPrivate::applyInsertionChange function was previously determined by whether the new count of visible items is greater than the previous count. After the sanitation in commit v5.6.0-beta1~7, this numeric comparison is no longer a good indicator of whether a repaint is needed. Change the return value to indicate whether new items were inserted which are visible in a more-direct way. Verify that visible items are initialized correctly in tests. They should not be 'culled'. It is necessary to invoke the layout method first to clear the forceLayout state. Two pre-existing tests fail before the fix in this patch. Change-Id: I625f1e02bf7001834adb147161a1e478a0ce2a0d Task-number: QTBUG-53263 Reviewed-by: Robin Burchell --- src/quick/items/qquicklistview.cpp | 8 ++++++-- tests/auto/quick/qquicklistview/tst_qquicklistview.cpp | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 78a64c5df1..b324e00cac 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -3117,7 +3117,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch } } - int prevVisibleCount = visibleItems.count(); + bool visibleAffected = false; if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) { // Insert items before the visible item. int insertionIdx = index; @@ -3140,6 +3140,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch if (!item) return false; + visibleAffected = true; visibleItems.insert(insertionIdx, item); if (insertionIdx == 0) insertResult->changedFirstItem = true; @@ -3171,6 +3172,9 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch } else { qreal to = buffer + displayMarginEnd + tempPos + size(); + + visibleAffected = count > 0 && pos < to; + for (int i = 0; i < count && pos <= to; ++i) { FxViewItem *item = 0; if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) @@ -3221,7 +3225,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch updateVisibleIndex(); - return visibleItems.count() > prevVisibleCount; + return visibleAffected; } void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult) diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 19f4010f8c..48230891ba 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -633,6 +633,8 @@ void tst_QQuickListView::inserted_more(QQuickItemView::VerticalLayoutDirection v } listview->setContentY(contentY); + QQuickItemViewPrivate::get(listview)->layout(); + QList > newData; for (int i=0; iy(), itemsOffsetAfterMove); #endif + QList visibleItems = QQuickItemViewPrivate::get(listview)->visibleItems; + for (QList::const_iterator itemIt = visibleItems.begin(); itemIt != visibleItems.end(); ++itemIt) + { + FxViewItem *item = *itemIt; + if (item->item->position().y() >= 0 && item->item->position().y() < listview->height()) + { + QVERIFY(!QQuickItemPrivate::get(item->item)->culled); + } + } + QList items = findItems(contentItem, "wrapper"); int firstVisibleIndex = -1; for (int i=0; i Date: Tue, 21 Jun 2016 13:16:41 +0200 Subject: Add missing signal handling for QJSValue Emitting signal with QJSValue argument ends in QVariant converstion in qml bound signal expression evaluation. Create missing handling for arguments of type QJSValue. Change-Id: I3d51a5455c09d0eef1123941066d20ad68f4074d Reviewed-by: Erik Verbruggen Reviewed-by: Lars Knoll --- src/qml/qml/qqmlboundsignal.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 6d8f883e4c..a6fdf60c86 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -48,6 +48,7 @@ #include #include "qqmlinfo.h" +#include #include #include @@ -217,7 +218,9 @@ void QQmlBoundSignalExpression::evaluate(void **a) //### ideally we would use metaTypeToJS, however it currently gives different results // for several cases (such as QVariant type and QObject-derived types) //args[ii] = engine->metaTypeToJS(type, a[ii + 1]); - if (type == QMetaType::QVariant) { + if (type == qMetaTypeId()) { + callData->args[ii] = *QJSValuePrivate::getValue(reinterpret_cast(a[ii + 1])); + } else if (type == QMetaType::QVariant) { callData->args[ii] = scope.engine->fromVariant(*((QVariant *)a[ii + 1])); } else if (type == QMetaType::Int) { //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise -- cgit v1.2.3 From 5511ed66e654dbd0ce3c03c7bf22b201494bb82f Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 21 Jun 2016 09:03:04 +0200 Subject: Add potentially missing include to qquickviewcomparison example Task-number: QTBUG-54244 Change-Id: I38c3fff07ed2a5ff3ffddef1d523f110f0863ac0 Reviewed-by: Andy Nichols --- examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp b/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp index 078d8e7e03..ea6c7f2f9a 100644 --- a/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp +++ b/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp @@ -40,6 +40,7 @@ #include "mainwindow.h" #include "fbitem.h" +#include #include #include #include -- cgit v1.2.3 From b61c774ce58d15bfc26a2a75b55e3f5eefbcdcc2 Mon Sep 17 00:00:00 2001 From: Daniel d'Andrada Date: Wed, 8 Jun 2016 14:42:03 -0300 Subject: QQuickSpriteEngine: avoid entering infinite loop in assembledImage() Do not allow a frame size larger than the image size, otherwise we would never leave "while (framesLeft > 0) {...}" as framesLeft is never decremented because "copied/frameWidth" in the expression "framesLeft -= copied/frameWidth;" always resolves to zero because copied < frameWidth. Task-number: QTBUG-53937 Change-Id: Ia777ec65d72562426b13533918efcaca5bcabdd7 Reviewed-by: Albert Astals Cid Reviewed-by: Shawn Rutledge Reviewed-by: Andy Nichols --- src/quick/items/qquickspriteengine.cpp | 9 ++++ .../quick/qquickanimatedsprite/data/img100x100.png | Bin 0 -> 238 bytes .../quick/qquickanimatedsprite/data/img50x50.png | Bin 0 -> 135 bytes .../qquickanimatedsprite/data/sourceSwitch.qml | 46 +++++++++++++++++++++ .../tst_qquickanimatedsprite.cpp | 39 +++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 tests/auto/quick/qquickanimatedsprite/data/img100x100.png create mode 100644 tests/auto/quick/qquickanimatedsprite/data/img50x50.png create mode 100644 tests/auto/quick/qquickanimatedsprite/data/sourceSwitch.qml diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index 243feef683..864f632e7c 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -399,6 +399,15 @@ QImage QQuickSpriteEngine::assembledImage() QImage img = state->m_pix.image(); + { + const QSize frameSize(state->m_frameWidth, state->m_frameHeight); + if (!(img.size() - frameSize).isValid()) { + qmlInfo(state).nospace() << "SpriteEngine: Invalid frame size " << frameSize << "." + " It's bigger than image size " << img.size() << "."; + return QImage(); + } + } + //Check that the frame sizes are the same within one sprite if (!state->m_frameWidth) state->m_frameWidth = img.width() / state->frames(); diff --git a/tests/auto/quick/qquickanimatedsprite/data/img100x100.png b/tests/auto/quick/qquickanimatedsprite/data/img100x100.png new file mode 100644 index 0000000000..9dd1990780 Binary files /dev/null and b/tests/auto/quick/qquickanimatedsprite/data/img100x100.png differ diff --git a/tests/auto/quick/qquickanimatedsprite/data/img50x50.png b/tests/auto/quick/qquickanimatedsprite/data/img50x50.png new file mode 100644 index 0000000000..5cbb47981a Binary files /dev/null and b/tests/auto/quick/qquickanimatedsprite/data/img50x50.png differ diff --git a/tests/auto/quick/qquickanimatedsprite/data/sourceSwitch.qml b/tests/auto/quick/qquickanimatedsprite/data/sourceSwitch.qml new file mode 100644 index 0000000000..18a8f52661 --- /dev/null +++ b/tests/auto/quick/qquickanimatedsprite/data/sourceSwitch.qml @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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: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$ +** +****************************************************************************/ + +import QtQuick 2.4 + +AnimatedSprite { + id: animatedSprite + source: big ? "img100x100.png" : "img50x50.png" + frameWidth: 100 + frameHeight: 100 + property bool big: true + MouseArea { + anchors.fill: parent + onClicked: animatedSprite.big = !animatedSprite.big + } +} diff --git a/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp b/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp index 3cb7ff511f..b34ac63b83 100644 --- a/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp +++ b/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp @@ -40,6 +40,7 @@ #include #include #include +#include class tst_qquickanimatedsprite : public QQmlDataTest { @@ -55,6 +56,7 @@ private slots: void test_largeAnimation_data(); void test_largeAnimation(); void test_reparenting(); + void test_changeSourceToSmallerImgKeepingBigFrameSize(); }; void tst_qquickanimatedsprite::initTestCase() @@ -291,6 +293,43 @@ void tst_qquickanimatedsprite::test_reparenting() QTRY_COMPARE(QQuickItemPrivate::get(sprite)->polishScheduled, false); } +class KillerThread : public QThread +{ + Q_OBJECT +protected: + void run() Q_DECL_OVERRIDE { + sleep(3); + qFatal("Either the GUI or the render thread is stuck in an infinite loop."); + } +}; + +// Regression test for QTBUG-53937 +void tst_qquickanimatedsprite::test_changeSourceToSmallerImgKeepingBigFrameSize() +{ + QQuickView window; + window.setSource(testFileUrl("sourceSwitch.qml")); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QVERIFY(window.rootObject()); + QQuickAnimatedSprite* sprite = qobject_cast(window.rootObject()); + QVERIFY(sprite); + + QQmlProperty big(sprite, "big"); + big.write(QVariant::fromValue(false)); + + KillerThread *killer = new KillerThread; + killer->start(); // will kill us in case the GUI or render thread enters an infinite loop + + QTest::qWait(50); // let it draw with the new source. + + // If we reach this point it's because we didn't hit QTBUG-53937 + + killer->terminate(); + killer->wait(); + delete killer; +} + QTEST_MAIN(tst_qquickanimatedsprite) #include "tst_qquickanimatedsprite.moc" -- cgit v1.2.3 From 84b51ca931e6a673738c59ac4686340c4199e203 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 21 Jun 2016 12:00:11 +0200 Subject: Update adaptations doc page With information on how to switch backends. Change-Id: I13d00cd4e5212e1412b19b37748b6773ebb25360 Reviewed-by: Andy Nichols --- .../doc/src/concepts/visualcanvas/adaptations.qdoc | 44 +++++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index d7d2fea281..a1b4650507 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -31,14 +31,46 @@ \section1 Scene Graph Adaptations in Qt Quick -Originally Qt Quick only had one available renderer for parsing the -scene graph and rendering the results to a render target. This renderer -is now the default OpenGL Renderer which supports rendering either using -the OpenGL ES 2.0 or OpenGL 2.0 APIs. The Qt Quick APIs are designed -with the assumption that these two APIs are always available. It is -however possible now to use other graphics API's to render Qt Quick +Originally Qt Quick only had one available renderer for parsing the scene graph +and rendering the results to a render target. This renderer is now the default +OpenGL Renderer which supports rendering either using the OpenGL ES 2.0 or +OpenGL 2.0 (with framebuffer object extensions) APIs. The Qt Quick APIs have +originally been designed with the assumption that OpenGL is always available. +However, it is now possible to use other graphics API's to render Qt Quick scenes using the scene graph APIs. +\section1 Switching between the adaptation used by the application + +The default of the OpenGL, or - in Qt builds with disabled OpenGL support - the +software adaptation, can be overridden either by using an environment variable +or a C++ API. The former consists of setting the \c{QT_QUICK_BACKEND} or the +legacy \c{QMLSCENE_DEVICE} environment variable before launching applications. +The latter is done by calling QQuickWindow::setSceneGraphBackend() early in the +application's main() function. + +The supported backends are the following + +\list + +\li OpenGL - Requested by the string \c{""} or the enum value QSGRendererInterface::OpenGL. + +\li Software - Requested by the string \c{"software"} or the enum value QSGRendererInterface::Software. + +\li Direct3D 12 - Requested by the string \c{"d3d12"} or the enum value QSGRendererInterface::Direct3D12. + +\endlist + +When in doubt which backend is in use, enable basic scenegraph information +logging via the \c{QSG_INFO} environment variable or the +\c{qt.scenegraph.general} logging category. This will result in printing some +information during application startup onto the debug output. + +\note Adaptations other than OpenGL will typically come with a set of +limitations since they are unlikely to provide a feature set 100% compatible +with OpenGL. However, they may provide their own specific advantages in certain +areas. Refer to the sections below for more information on the various +adaptations. + \section1 OpenGL ES 2.0 and OpenGL 2.0 Adaptation The default adaptation capable of providing the full Qt Quick 2 feature -- cgit v1.2.3 From d2b3a3a4e60aab1b2aeb322c994bc401d9dd920b Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 22 Jun 2016 08:33:53 +0200 Subject: qmltest - fix accidentally broken BLACKLIST Remove the line that previously was a part of 'linecount' test BLACKLISTed and now accidentally BLACKLISTed another test instead. Change-Id: I3618fc0478d7a245c1c5aaaf6f38eb9359f523b0 Reviewed-by: Milla Pohjanheimo Reviewed-by: Shawn Rutledge --- tests/auto/qmltest/BLACKLIST | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/auto/qmltest/BLACKLIST b/tests/auto/qmltest/BLACKLIST index 2ac01edc31..41af442971 100644 --- a/tests/auto/qmltest/BLACKLIST +++ b/tests/auto/qmltest/BLACKLIST @@ -7,4 +7,3 @@ linux [ListView::test_listInteractiveCurrentIndexEnforce] linux -windows -- cgit v1.2.3 From 7de18e6f52d6247bddd7bfabe0b2601d7db239b5 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 15 Jun 2016 15:17:32 +0200 Subject: D3D12: Support runtime threaded shader compilation Let's revise our policy of offline/bytecode only shaders. ShaderEffect benefits greatly from having runtime compilation support for HLSL source strings, especially when dynamically constructing shader strings. There is no reason not to support both approaches since we rely on d3dcompiler for reflection anyhow. What's more, we can call D3DCompile on a dedicated thread, keeping the gui responsive while compilation is on-going. Change-Id: Ie6c02c2aa0ebd0c8371bbf30b3ce6582128c457b Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 4 +- .../scenegraph/d3d12/qsgd3d12shadereffectnode.cpp | 89 ++++++++++++++++++--- .../scenegraph/d3d12/qsgd3d12shadereffectnode_p.h | 9 ++- src/quick/items/qquickgenericshadereffect.cpp | 90 +++++++++++++++++----- src/quick/items/qquickgenericshadereffect_p.h | 8 +- src/quick/items/qquickshadereffect.cpp | 30 +++++++- src/quick/scenegraph/qsgadaptationlayer_p.h | 6 +- tests/manual/nodetypes/Effects.qml | 39 +++++++++- 8 files changed, 232 insertions(+), 43 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 45b9de9ece..b54d10aa85 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -552,12 +552,12 @@ QSGRendererInterface::ShaderType QSGD3D12Engine::shaderType() const QSGRendererInterface::ShaderCompilationTypes QSGD3D12Engine::shaderCompilationType() const { - return OfflineCompilation; + return RuntimeCompilation | OfflineCompilation; } QSGRendererInterface::ShaderSourceTypes QSGD3D12Engine::shaderSourceType() const { - return ShaderByteCode; + return ShaderSourceString | ShaderByteCode; } static inline quint32 alignedSize(quint32 size, quint32 byteAlign) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp index f77719f876..8c46e781e9 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp @@ -41,6 +41,7 @@ #include "qsgd3d12rendercontext_p.h" #include "qsgd3d12texture_p.h" #include "qsgd3d12engine_p.h" +#include #include #include #include @@ -777,12 +778,12 @@ bool QSGD3D12GuiThreadShaderEffectManager::hasSeparateSamplerAndTextureObjects() QString QSGD3D12GuiThreadShaderEffectManager::log() const { - return QString(); + return m_log; } QSGGuiThreadShaderEffectManager::Status QSGD3D12GuiThreadShaderEffectManager::status() const { - return Compiled; + return m_status; } struct RefGuard { @@ -791,17 +792,85 @@ struct RefGuard { IUnknown *p; }; -bool QSGD3D12GuiThreadShaderEffectManager::reflect(const QByteArray &src, ShaderInfo *result) +class QSGD3D12ShaderCompileTask : public QRunnable { - const QString fn = QQmlFile::urlToLocalFileOrQrc(src); - QFile f(fn); - if (!f.open(QIODevice::ReadOnly)) { - qWarning("ShaderEffect: Failed to read %s", qPrintable(fn)); - return false; +public: + QSGD3D12ShaderCompileTask(QSGD3D12GuiThreadShaderEffectManager *mgr, + QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::Type typeHint, + const QByteArray &src, + QSGD3D12GuiThreadShaderEffectManager::ShaderInfo *result) + : mgr(mgr), typeHint(typeHint), src(src), result(result) { } + + void run() override; + +private: + QSGD3D12GuiThreadShaderEffectManager *mgr; + QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::Type typeHint; + QByteArray src; + QSGD3D12GuiThreadShaderEffectManager::ShaderInfo *result; +}; + +void QSGD3D12ShaderCompileTask::run() +{ + const char *target = typeHint == QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? "vs_5_0" : "ps_5_0"; + ID3DBlob *bytecode = nullptr; + ID3DBlob *errors = nullptr; + HRESULT hr = D3DCompile(src.constData(), src.size(), nullptr, nullptr, nullptr, + "main", target, 0, 0, &bytecode, &errors); + if (FAILED(hr) || !bytecode) { + qWarning("HLSL shader compilation failed: 0x%x", hr); + if (errors) { + mgr->m_log += QString::fromUtf8(static_cast(errors->GetBufferPointer()), errors->GetBufferSize()); + errors->Release(); + } + mgr->m_status = QSGGuiThreadShaderEffectManager::Error; + emit mgr->shaderCodePrepared(false, typeHint, src, result); + emit mgr->logAndStatusChanged(); + return; } - result->blob = f.readAll(); - f.close(); + result->blob.resize(bytecode->GetBufferSize()); + memcpy(result->blob.data(), bytecode->GetBufferPointer(), result->blob.size()); + bytecode->Release(); + + const bool ok = mgr->reflect(result); + mgr->m_status = ok ? QSGGuiThreadShaderEffectManager::Compiled : QSGGuiThreadShaderEffectManager::Error; + emit mgr->shaderCodePrepared(ok, typeHint, src, result); + emit mgr->logAndStatusChanged(); +} + +void QSGD3D12GuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) +{ + // The D3D12 backend's ShaderEffect implementation supports both HLSL + // source strings and bytecode in files as input. The latter is strongly + // recommended, but in order to make ShaderEffect users' (and + // qtgraphicaleffects') life easier, and since we link to d3dcompiler + // anyways, compiling from source is also supported. + + // For simplicity, assume that file = bytecode, string = HLSL. + QUrl srcUrl(src); + if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) { + const QString fn = QQmlFile::urlToLocalFileOrQrc(src); + QFile f(fn); + if (!f.open(QIODevice::ReadOnly)) { + qWarning("ShaderEffect: Failed to read %s", qPrintable(fn)); + emit shaderCodePrepared(false, typeHint, src, result); + return; + } + result->blob = f.readAll(); + f.close(); + const bool ok = reflect(result); + m_status = ok ? Compiled : Error; + emit shaderCodePrepared(ok, typeHint, src, result); + emit logAndStatusChanged(); + return; + } + + QThreadPool::globalInstance()->start(new QSGD3D12ShaderCompileTask(this, typeHint, src, result)); +} + +bool QSGD3D12GuiThreadShaderEffectManager::reflect(ShaderInfo *result) +{ ID3D12ShaderReflection *reflector; HRESULT hr = D3DReflect(result->blob.constData(), result->blob.size(), IID_PPV_ARGS(&reflector)); if (FAILED(hr)) { diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h index edeaba899b..c36ee1a6e6 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h @@ -160,7 +160,14 @@ public: QString log() const override; Status status() const override; - bool reflect(const QByteArray &src, ShaderInfo *result) override; + void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) override; + +private: + bool reflect(ShaderInfo *result); + QString m_log; + Status m_status = Uncompiled; + + friend class QSGD3D12ShaderCompileTask; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp index 47272a2eac..11259a588a 100644 --- a/src/quick/items/qquickgenericshadereffect.cpp +++ b/src/quick/items/qquickgenericshadereffect.cpp @@ -61,7 +61,10 @@ QQuickGenericShaderEffect::QQuickGenericShaderEffect(QQuickShaderEffect *item, Q , m_mgr(nullptr) , m_dirty(0) { + qRegisterMetaType("ShaderInfo::Type"); connect(m_item, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(itemWindowChanged(QQuickWindow*))); + for (int i = 0; i < NShader; ++i) + m_inProgress[i] = nullptr; } QQuickGenericShaderEffect::~QQuickGenericShaderEffect() @@ -232,6 +235,10 @@ QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQui return nullptr; } + // Do not change anything while a new shader is being reflected or compiled. + if (m_inProgress[Vertex] || m_inProgress[Fragment]) + return node; + // The manager should be already created on the gui thread. Just take that instance. QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager(); if (!mgr) { @@ -327,6 +334,7 @@ QSGGuiThreadShaderEffectManager *QQuickGenericShaderEffect::shaderEffectManager( connect(m_mgr, SIGNAL(logAndStatusChanged()), m_item, SIGNAL(logChanged())); connect(m_mgr, SIGNAL(logAndStatusChanged()), m_item, SIGNAL(statusChanged())); connect(m_mgr, SIGNAL(textureChanged()), this, SLOT(markGeometryDirtyAndUpdateIfSupportsAtlas())); + connect(m_mgr, &QSGGuiThreadShaderEffectManager::shaderCodePrepared, this, &QQuickGenericShaderEffect::shaderCodePrepared); } } else if (!w) { // Wait until itemWindowChanged() gets called. Return null for now. @@ -377,27 +385,27 @@ void QQuickGenericShaderEffect::disconnectSignals(Shader shaderType) } } -struct ReflectCache +struct ShaderInfoCache { bool contains(const QByteArray &key) const { - return m_reflectCache.contains(key); + return m_shaderInfoCache.contains(key); } QSGGuiThreadShaderEffectManager::ShaderInfo value(const QByteArray &key) const { - return m_reflectCache.value(key); + return m_shaderInfoCache.value(key); } void insert(const QByteArray &key, const QSGGuiThreadShaderEffectManager::ShaderInfo &value) { - m_reflectCache.insert(key, value); + m_shaderInfoCache.insert(key, value); } - QHash m_reflectCache; + QHash m_shaderInfoCache; }; -Q_GLOBAL_STATIC(ReflectCache, reflectCache) +Q_GLOBAL_STATIC(ShaderInfoCache, shaderInfoCache) void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray &src) { @@ -413,22 +421,26 @@ void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray m_shaders[shaderType].varData.clear(); if (!src.isEmpty()) { - // Figure out what input parameters and variables are used in the shader. - // For file-based shader source/bytecode this is where the data is pulled - // in from the file. - if (reflectCache()->contains(src)) { - m_shaders[shaderType].shaderInfo = reflectCache()->value(src); + if (shaderInfoCache()->contains(src)) { + m_shaders[shaderType].shaderInfo = shaderInfoCache()->value(src); + m_shaders[shaderType].hasShaderCode = true; } else { - QSGGuiThreadShaderEffectManager::ShaderInfo shaderInfo; - if (!mgr->reflect(src, &shaderInfo)) { - qWarning("ShaderEffect: shader reflection failed for %s", src.constData()); - m_shaders[shaderType].hasShaderCode = false; - return; - } - m_shaders[shaderType].shaderInfo = shaderInfo; - reflectCache()->insert(src, shaderInfo); + // Each prepareShaderCode call needs its own work area, hence the + // dynamic alloc. If there are calls in progress, let those run to + // finish, their results can then simply be ignored because + // m_inProgress indicates what we care about. + m_inProgress[shaderType] = new QSGGuiThreadShaderEffectManager::ShaderInfo; + const QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint = + shaderType == Vertex ? QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex + : QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment; + // Figure out what input parameters and variables are used in the + // shader. For file-based shader source/bytecode this is where the data + // is pulled in from the file. Some backends may choose to do + // source->bytecode compilation as well in this step. + mgr->prepareShaderCode(typeHint, src, m_inProgress[shaderType]); + // the rest is handled in shaderCodePrepared() + return; } - m_shaders[shaderType].hasShaderCode = true; } else { m_shaders[shaderType].hasShaderCode = false; if (shaderType == Fragment) { @@ -446,6 +458,44 @@ void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray } } + updateShaderVars(shaderType); +} + +void QQuickGenericShaderEffect::shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint, + const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result) +{ + const Shader shaderType = typeHint == QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? Vertex : Fragment; + + // If another call was made to updateShader() for the same shader type in + // the meantime then our results are useless, just drop them. + if (result != m_inProgress[shaderType]) { + delete result; + return; + } + + m_shaders[shaderType].shaderInfo = *result; + delete result; + m_inProgress[shaderType] = nullptr; + + if (!ok) { + qWarning("ShaderEffect: shader preparation failed for %s\n%s\n", src.constData(), qPrintable(log())); + m_shaders[shaderType].hasShaderCode = false; + return; + } + + m_shaders[shaderType].hasShaderCode = true; + shaderInfoCache()->insert(src, m_shaders[shaderType].shaderInfo); + updateShaderVars(shaderType); +} + +void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType) +{ + QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager(); + if (!mgr) + return; + + const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects(); + const int varCount = m_shaders[shaderType].shaderInfo.variables.count(); m_shaders[shaderType].varData.resize(varCount); diff --git a/src/quick/items/qquickgenericshadereffect_p.h b/src/quick/items/qquickgenericshadereffect_p.h index ab17a7fb87..5ec83eb60f 100644 --- a/src/quick/items/qquickgenericshadereffect_p.h +++ b/src/quick/items/qquickgenericshadereffect_p.h @@ -103,6 +103,8 @@ private slots: void markGeometryDirtyAndUpdateIfSupportsAtlas(); void itemWindowChanged(QQuickWindow *w); void backendChanged(); + void shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint, + const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result); private: QSGGuiThreadShaderEffectManager *shaderEffectManager() const; @@ -113,8 +115,9 @@ private: NShader }; - void updateShader(Shader which, const QByteArray &src); - void disconnectSignals(Shader which); + void updateShader(Shader shaderType, const QByteArray &src); + void updateShaderVars(Shader shaderType); + void disconnectSignals(Shader shaderType); bool sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const; QQuickShaderEffect *m_item; @@ -132,6 +135,7 @@ private: QSGShaderEffectNode::DirtyShaderFlags m_dirty; QSet m_dirtyConstants[NShader]; QSet m_dirtyTextures[NShader]; + QSGGuiThreadShaderEffectManager::ShaderInfo *m_inProgress[NShader]; struct SignalMapper { SignalMapper() : mapper(nullptr), active(false) { } diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index f7fc7880ed..4f1a9a28ec 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -209,11 +209,33 @@ QT_BEGIN_NAMESPACE it is the textures that map to properties referencing \l Image or \l ShaderEffectSource items. - Unlike with OpenGL, runtime compilation of shader source code may not be - supported. Backends for modern APIs are likely to prefer offline + Unlike OpenGL, backends for modern APIs will typically prefer offline compilation and shipping pre-compiled bytecode with applications instead of - inlined shader source strings. To check what is expected at runtime, use the - GraphicsInfo.shaderSourceType and GraphicsInfo.shaderCompilationType properties. + inlined shader source strings. In this case the string properties for + vertex and fragment shaders are treated as URLs referring to local files or + files shipped via the Qt resource system. + + To check at runtime what is supported, use the + GraphicsInfo.shaderSourceType and GraphicsInfo.shaderCompilationType + properties. Note that these are bitmasks, because some backends may support + multiple approaches. + + In case of Direct3D 12, both bytecode in files and HLSL source strings are + supported. If the vertexShader and fragmentShader properties form a valid + URL with the \c file or \c qrc schema, the bytecode is read from the + specified file. Otherwise, the string is treated as HLSL source code and is + compiled at runtime, assuming Shader Model 5.0 and an entry point of + \c{"main"}. This allows dynamically constructing shader strings. However, + whenever the shader source code is static, it is strongly recommended to + pre-compile to bytecode using the \c fxc tool and refer to these files from + QML. This will be a lot more efficient at runtime and allows catching + syntax errors in the shaders at compile time. + + Unlike OpenGL, the Direct3D backend is able to perform runtime shader + compilation on dedicated threads. This is managed transparently to the + applications, and means that ShaderEffect items that contain HLSL source + strings do not block the rendering or other parts of the application until + the bytecode is ready. \table 70% \row diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 8fdcf7af64..179ec3e3fa 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -273,9 +273,10 @@ public: uint constantDataSize; }; - virtual bool reflect(const QByteArray &src, ShaderInfo *result) = 0; + virtual void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) = 0; Q_SIGNALS: + void shaderCodePrepared(bool ok, ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result); void textureChanged(); void logAndStatusChanged(); }; @@ -536,7 +537,8 @@ inline bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph) return glyphData(glyph).texCoord.isValid(); } - QT_END_NAMESPACE +Q_DECLARE_METATYPE(QSGGuiThreadShaderEffectManager::ShaderInfo::Type) + #endif diff --git a/tests/manual/nodetypes/Effects.qml b/tests/manual/nodetypes/Effects.qml index 0d16cd1c84..85e7ab7a15 100644 --- a/tests/manual/nodetypes/Effects.qml +++ b/tests/manual/nodetypes/Effects.qml @@ -69,6 +69,7 @@ Item { NumberAnimation on time { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 600 } property bool customVertexShader: false // the effect is fine with the default vs, but toggle this to test + property bool useHLSLSourceString: false // toggle to provide HLSL shaders as strings instead of bytecode in files property string glslVertexShader: "uniform highp mat4 qt_Matrix;" + @@ -92,12 +93,43 @@ Item { " gl_FragColor = texture2D(source, qt_TexCoord0 + amplitude * vec2(p.y, -p.x)) * qt_Opacity;" + "}" + property string hlslVertexShader: "cbuffer ConstantBuffer : register(b0) {" + + " float4x4 qt_Matrix;" + + " float qt_Opacity; }" + + "struct PSInput {" + + " float4 position : SV_POSITION;" + + " float2 coord : TEXCOORD0; };" + + "PSInput main(float4 position : POSITION, float2 coord : TEXCOORD0) {" + + " PSInput result;" + + " result.position = mul(qt_Matrix, position);" + + " result.coord = coord;" + + " return result;" + + "}"; + + property string hlslPixelShader:"cbuffer ConstantBuffer : register(b0) {" + + " float4x4 qt_Matrix;" + + " float qt_Opacity;" + + " float amplitude;" + + " float frequency;" + + " float time; }" + + "Texture2D source : register(t0);" + + "SamplerState sourceSampler : register(s0);" + + "float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET" + + "{" + + " float2 p = sin(time + frequency * coord);" + + " return source.Sample(sourceSampler, coord + amplitude * float2(p.y, -p.x)) * qt_Opacity;" + + "}"; + property string hlslVertexShaderByteCode: "qrc:/vs_wobble.cso" property string hlslPixelShaderByteCode: "qrc:/ps_wobble.cso" - vertexShader: customVertexShader ? (GraphicsInfo.shaderType === GraphicsInfo.HLSL ? hlslVertexShaderByteCode : (GraphicsInfo.shaderType === GraphicsInfo.GLSL ? glslVertexShader : "")) : "" + vertexShader: customVertexShader ? (GraphicsInfo.shaderType === GraphicsInfo.HLSL + ? (useHLSLSourceString ? hlslVertexShader : hlslVertexShaderByteCode) + : (GraphicsInfo.shaderType === GraphicsInfo.GLSL ? glslVertexShader : "")) : "" - fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.HLSL ? hlslPixelShaderByteCode : (GraphicsInfo.shaderType === GraphicsInfo.GLSL ? glslFragmentShader : "") + fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.HLSL + ? (useHLSLSourceString ? hlslPixelShader : hlslPixelShaderByteCode) + : (GraphicsInfo.shaderType === GraphicsInfo.GLSL ? glslFragmentShader : "") } Image { @@ -181,6 +213,9 @@ Item { Text { text: GraphicsInfo.shaderType + " " + GraphicsInfo.shaderCompilationType + " " + GraphicsInfo.shaderSourceType } + Text { + text: eff.status + " " + eff.log + } } } } -- cgit v1.2.3 From 1569f46bdcffc9b4af3eed295aa29fa85e33f60c Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 21 Jun 2016 14:44:41 +0200 Subject: Make QSGEngine::initialize backend-independent Allow calling initialize(nullptr) regardless of which scenegraph backend is in use. Change-Id: Ie5965dcd12d423255d5eb85fed255107cac2acb9 Reviewed-by: Allan Sandfeld Jensen --- src/quick/scenegraph/qsgcontext_p.h | 2 ++ src/quick/scenegraph/qsgdefaultrendercontext.cpp | 2 +- src/quick/scenegraph/qsgdefaultrendercontext_p.h | 2 +- src/quick/scenegraph/util/qsgengine.cpp | 22 ++++++++++------------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 2fef0ff1e8..2527be0480 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -122,6 +122,8 @@ public: virtual QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const = 0; virtual QSGRenderer *createRenderer() = 0; + virtual void setAttachToGraphicsContext(bool attach) { Q_UNUSED(attach); } + void registerFontengineForCleanup(QFontEngine *engine); Q_SIGNALS: diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 92e7f983a0..870c0488c3 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -273,7 +273,7 @@ void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader) shader->initialize(); } -void QSGDefaultRenderContext::setAttachToGLContext(bool attach) +void QSGDefaultRenderContext::setAttachToGraphicsContext(bool attach) { Q_ASSERT(!isValid()); m_attachToGLContext = attach; diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h index bfb15b1eb9..c5e08c07f0 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h +++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h @@ -88,7 +88,7 @@ public: virtual void compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0); virtual void initializeShader(QSGMaterialShader *shader); - void setAttachToGLContext(bool attach); + void setAttachToGraphicsContext(bool attach) override; static QSGDefaultRenderContext *from(QOpenGLContext *context); diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp index 26a3d66077..f4d86bba04 100644 --- a/src/quick/scenegraph/util/qsgengine.cpp +++ b/src/quick/scenegraph/util/qsgengine.cpp @@ -115,23 +115,21 @@ QSGEngine::~QSGEngine() */ void QSGEngine::initialize(QOpenGLContext *context) { -#ifndef QT_NO_OPENGL Q_D(QSGEngine); - if (QOpenGLContext::currentContext() != context) { +#ifdef QT_NO_OPENGL + if (context && QOpenGLContext::currentContext() != context) { qWarning("WARNING: The context must be current before calling QSGEngine::initialize."); return; } - - auto openGLRenderContext = static_cast(d->sgRenderContext.data()); - - if (openGLRenderContext != nullptr && !openGLRenderContext->isValid()) { - openGLRenderContext->setAttachToGLContext(false); - openGLRenderContext->initialize(context); - connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate); - } -#else - Q_UNUSED(context) #endif + if (d->sgRenderContext && !d->sgRenderContext->isValid()) { + d->sgRenderContext->setAttachToGraphicsContext(false); + d->sgRenderContext->initialize(context); +#ifndef QT_NO_OPENGL + if (context) + connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate); +#endif + } } /*! -- cgit v1.2.3 From fd0e3c6d569a7410fff33974ce9f908dc2de0e22 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 22 Jun 2016 09:31:52 +0200 Subject: Fix build on Windows with MinGW and QNX Added missing QDate, QTime, QDateTime inclusion. Change-Id: I48da448cb7c352eb04c674aea8027fc5c7aae151 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlpropertyvalidator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index dd44a49896..9ea52e240d 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -41,6 +41,7 @@ #include #include +#include QT_BEGIN_NAMESPACE -- cgit v1.2.3 From 702c4247d74ffb7e4fb1aaca96d70f4591203ba2 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 22 Jun 2016 10:12:13 +0200 Subject: V4: Pass scope around as parameters inside the runtime. The implementation of many (or all) runtime functions consist of first creating a QV4::Scope, which saves and restores the JS stack pointer. It also prevents tail-calls because of that restoring behavior. In many cases it suffices to do that at the entry-point of the runtime. The return value of a JS function call is now also stored in the scope. Previously, all return values were stored in a ScopedValue, got loaded on return, and immediately stored in another ScopedValue in the caller. This resulted in a lot of stores, where now there is only one store needed, and no extra ScopedValue for every function. Change-Id: I13d80fc0ce72c5702ef1536d41d12f710c5914fa Reviewed-by: Simon Hausmann --- src/imports/localstorage/plugin.cpp | 6 +- .../qmldbg_debugger/qqmlnativedebugservice.cpp | 17 +- src/qml/jsapi/qjsvalue.cpp | 18 +- src/qml/jsruntime/qv4argumentsobject.cpp | 24 +-- src/qml/jsruntime/qv4argumentsobject_p.h | 4 +- src/qml/jsruntime/qv4arraybuffer.cpp | 30 ++-- src/qml/jsruntime/qv4arraybuffer_p.h | 4 +- src/qml/jsruntime/qv4arrayobject.cpp | 64 ++++--- src/qml/jsruntime/qv4arrayobject_p.h | 4 +- src/qml/jsruntime/qv4booleanobject.cpp | 9 +- src/qml/jsruntime/qv4booleanobject_p.h | 4 +- src/qml/jsruntime/qv4dataview.cpp | 22 +-- src/qml/jsruntime/qv4dataview_p.h | 4 +- src/qml/jsruntime/qv4dateobject.cpp | 12 +- src/qml/jsruntime/qv4dateobject_p.h | 4 +- src/qml/jsruntime/qv4engine.cpp | 5 + src/qml/jsruntime/qv4engine_p.h | 11 +- src/qml/jsruntime/qv4errorobject.cpp | 39 ++--- src/qml/jsruntime/qv4errorobject_p.h | 16 +- src/qml/jsruntime/qv4functionobject.cpp | 186 +++++++++++---------- src/qml/jsruntime/qv4functionobject_p.h | 28 ++-- src/qml/jsruntime/qv4globalobject.cpp | 36 ++-- src/qml/jsruntime/qv4globalobject_p.h | 4 +- src/qml/jsruntime/qv4include.cpp | 2 +- src/qml/jsruntime/qv4jsonobject.cpp | 42 ++--- src/qml/jsruntime/qv4lookup.cpp | 24 ++- src/qml/jsruntime/qv4numberobject.cpp | 9 +- src/qml/jsruntime/qv4numberobject_p.h | 4 +- src/qml/jsruntime/qv4object.cpp | 17 +- src/qml/jsruntime/qv4object_p.h | 16 +- src/qml/jsruntime/qv4objectproto.cpp | 21 ++- src/qml/jsruntime/qv4objectproto_p.h | 4 +- src/qml/jsruntime/qv4qobjectwrapper.cpp | 56 ++++--- src/qml/jsruntime/qv4qobjectwrapper_p.h | 6 +- src/qml/jsruntime/qv4regexpobject.cpp | 49 +++--- src/qml/jsruntime/qv4regexpobject_p.h | 4 +- src/qml/jsruntime/qv4runtime.cpp | 90 ++++++---- src/qml/jsruntime/qv4scopedvalue_p.h | 49 +++--- src/qml/jsruntime/qv4script.cpp | 26 +-- src/qml/jsruntime/qv4script_p.h | 4 +- src/qml/jsruntime/qv4sequenceobject.cpp | 4 +- src/qml/jsruntime/qv4stringobject.cpp | 49 +++--- src/qml/jsruntime/qv4stringobject_p.h | 4 +- src/qml/jsruntime/qv4typedarray.cpp | 81 +++++---- src/qml/jsruntime/qv4typedarray_p.h | 4 +- src/qml/qml/qqmlbinding.cpp | 10 +- src/qml/qml/qqmlboundsignal.cpp | 4 +- src/qml/qml/qqmlcomponent.cpp | 2 +- src/qml/qml/qqmldelayedcallqueue.cpp | 2 +- src/qml/qml/qqmlexpression.cpp | 9 +- src/qml/qml/qqmlexpression_p.h | 2 +- src/qml/qml/qqmljavascriptexpression.cpp | 24 +-- src/qml/qml/qqmljavascriptexpression_p.h | 5 +- src/qml/qml/qqmlvmemetaobject.cpp | 5 +- src/qml/qml/qqmlxmlhttprequest.cpp | 17 +- src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 5 +- src/qml/qml/v8/qqmlbuiltinfunctions_p.h | 2 +- src/qml/types/qqmldelegatemodel.cpp | 12 +- src/qml/types/qquickworkerscript.cpp | 12 +- src/quick/items/context2d/qquickcanvasitem.cpp | 2 +- .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 11 +- tools/qmljs/qmljs.cpp | 8 +- 62 files changed, 668 insertions(+), 580 deletions(-) diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index a619030100..7d96f11768 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -426,7 +426,7 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx) callData->args[0] = w; TransactionRollback rollbackOnException(&db, &w->d()->inTransaction); - callback->call(callData); + callback->call(scope, callData); rollbackOnException.clear(); if (!db.commit()) { db.rollback(); @@ -474,7 +474,7 @@ static ReturnedValue qmlsqldatabase_transaction_shared(CallContext *ctx, bool re callData->thisObject = scope.engine->globalObject; callData->args[0] = w; TransactionRollback rollbackOnException(&db, &w->d()->inTransaction); - callback->call(callData); + callback->call(scope, callData); rollbackOnException.clear(); if (!db.commit()) @@ -745,7 +745,7 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args) ScopedCallData callData(scope, 1); callData->thisObject = scope.engine->globalObject; callData->args[0] = db; - dbcreationCallback->call(callData); + dbcreationCallback->call(scope, callData); } args->setReturnValue(db.asReturnedValue()); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp index 43036e9c3c..d54bf98068 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp @@ -208,7 +208,7 @@ private: void handleDebuggerDeleted(QObject *debugger); - QV4::ReturnedValue evaluateExpression(QV4::Scope &scope, const QString &expression); + void evaluateExpression(QV4::Scope &scope, const QString &expression); bool checkCondition(const QString &expression); QStringList breakOnSignals; @@ -241,12 +241,11 @@ private: bool NativeDebugger::checkCondition(const QString &expression) { QV4::Scope scope(m_engine); - QV4::ReturnedValue result = evaluateExpression(scope, expression); - QV4::ScopedValue val(scope, result); - return val->booleanValue(); + evaluateExpression(scope, expression); + return scope.result.booleanValue(); } -QV4::ReturnedValue NativeDebugger::evaluateExpression(QV4::Scope &scope, const QString &expression) +void NativeDebugger::evaluateExpression(QV4::Scope &scope, const QString &expression) { m_runningJob = true; @@ -261,12 +260,10 @@ QV4::ReturnedValue NativeDebugger::evaluateExpression(QV4::Scope &scope, const Q // That is a side-effect of inheritContext. script.inheritContext = true; script.parse(); - QV4::ScopedValue result(scope); if (!m_engine->hasException) - result = script.run(); + scope.result = script.run(); m_runningJob = false; - return result->asReturnedValue(); } NativeDebugger::NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine) @@ -545,8 +542,8 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject TRACE_PROTOCOL("Evaluate expression: " << expression); m_runningJob = true; - QV4::ReturnedValue eval = evaluateExpression(scope, expression); - QV4::ScopedValue result(scope, eval); + evaluateExpression(scope, expression); + QV4::ScopedValue result(scope, scope.result); m_runningJob = false; if (result->isUndefined()) { diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 44746b8c2b..e5c1dcdb80 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -663,11 +663,11 @@ QJSValue QJSValue::call(const QJSValueList &args) callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); } - ScopedValue result(scope, f->call(callData)); + f->call(scope, callData); if (engine->hasException) - result = engine->catchException(); + scope.result = engine->catchException(); - return QJSValue(engine, result->asReturnedValue()); + return QJSValue(engine, scope.result.asReturnedValue()); } /*! @@ -719,11 +719,11 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); } - ScopedValue result(scope, f->call(callData)); + f->call(scope, callData); if (engine->hasException) - result = engine->catchException(); + scope.result = engine->catchException(); - return QJSValue(engine, result->asReturnedValue()); + return QJSValue(engine, scope.result.asReturnedValue()); } /*! @@ -767,11 +767,11 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); } - ScopedValue result(scope, f->construct(callData)); + f->construct(scope, callData); if (engine->hasException) - result = engine->catchException(); + scope.result = engine->catchException(); - return QJSValue(engine, result->asReturnedValue()); + return QJSValue(engine, scope.result.asReturnedValue()); } #ifdef QT_DEPRECATED diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 94f418cae1..b55494823c 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -134,7 +134,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con ScopedCallData callData(scope, 1); callData->thisObject = this->asReturnedValue(); callData->args[0] = desc->value; - setter->call(callData); + setter->call(scope, callData); if (attrs.isWritable()) { setArrayAttributes(index, mapAttrs); @@ -203,33 +203,35 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction); -ReturnedValue ArgumentsGetterFunction::call(const Managed *getter, CallData *callData) +void ArgumentsGetterFunction::call(const Managed *getter, Scope &scope, CallData *callData) { ExecutionEngine *v4 = static_cast(getter)->engine(); - Scope scope(v4); Scoped g(scope, static_cast(getter)); Scoped o(scope, callData->thisObject.as()); - if (!o) - return v4->throwTypeError(); + if (!o) { + scope.result = v4->throwTypeError(); + return; + } Q_ASSERT(g->index() < static_cast(o->context()->callData->argc)); - return o->context()->callData->args[g->index()].asReturnedValue(); + scope.result = o->context()->callData->args[g->index()]; } DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction); -ReturnedValue ArgumentsSetterFunction::call(const Managed *setter, CallData *callData) +void ArgumentsSetterFunction::call(const Managed *setter, Scope &scope, CallData *callData) { ExecutionEngine *v4 = static_cast(setter)->engine(); - Scope scope(v4); Scoped s(scope, static_cast(setter)); Scoped o(scope, callData->thisObject.as()); - if (!o) - return v4->throwTypeError(); + if (!o) { + scope.result = v4->throwTypeError(); + return; + } Q_ASSERT(s->index() < static_cast(o->context()->callData->argc)); o->context()->callData->args[s->index()] = callData->argc ? callData->args[0].asReturnedValue() : Encode::undefined(); - return Encode::undefined(); + scope.result = Encode::undefined(); } void ArgumentsObject::markObjects(Heap::Base *that, ExecutionEngine *e) diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index eeedcaf995..6ccfd89fd4 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -88,7 +88,7 @@ struct ArgumentsGetterFunction: FunctionObject V4_OBJECT2(ArgumentsGetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(const Managed *that, CallData *d); + static void call(const Managed *that, Scope &scope, CallData *d); }; inline @@ -103,7 +103,7 @@ struct ArgumentsSetterFunction: FunctionObject V4_OBJECT2(ArgumentsSetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(const Managed *that, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; inline diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index d170bde0e8..e006773c9e 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -51,29 +51,34 @@ Heap::ArrayBufferCtor::ArrayBufferCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ArrayBufferCtor::construct(const Managed *m, CallData *callData) +void ArrayBufferCtor::construct(const Managed *m, Scope &scope, CallData *callData) { ExecutionEngine *v4 = static_cast(m)->engine(); - Scope scope(v4); ScopedValue l(scope, callData->argument(0)); double dl = l->toInteger(); - if (v4->hasException) - return Encode::undefined(); + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } uint len = (uint)qBound(0., dl, (double)UINT_MAX); - if (len != dl) - return v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length")); + if (len != dl) { + scope.result = v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length")); + return; + } Scoped a(scope, v4->newArrayBuffer(len)); - if (scope.engine->hasException) - return Encode::undefined(); - return a.asReturnedValue(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + } else { + scope.result = a->asReturnedValue(); + } } -ReturnedValue ArrayBufferCtor::call(const Managed *that, CallData *callData) +void ArrayBufferCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return construct(that, callData); + construct(that, scope, callData); } ReturnedValue ArrayBufferCtor::method_isView(CallContext *ctx) @@ -184,7 +189,8 @@ ReturnedValue ArrayBufferPrototype::method_slice(CallContext *ctx) ScopedCallData callData(scope, 1); double newLen = qMax(final - first, 0.); callData->args[0] = QV4::Encode(newLen); - QV4::Scoped newBuffer(scope, constructor->construct(callData)); + constructor->construct(scope, callData); + QV4::Scoped newBuffer(scope, scope.result.asReturnedValue()); if (!newBuffer || newBuffer->d()->data->size < (int)newLen) return scope.engine->throwTypeError(); diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index 0413d2f28d..d079aeb9f7 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -78,8 +78,8 @@ struct ArrayBufferCtor: FunctionObject { V4_OBJECT2(ArrayBufferCtor, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); static ReturnedValue method_isView(CallContext *ctx); diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 3521673461..4d15c6c137 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -54,18 +54,19 @@ Heap::ArrayCtor::ArrayCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ArrayCtor::construct(const Managed *m, CallData *callData) +void ArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData) { ExecutionEngine *v4 = static_cast(m)->engine(); - Scope scope(v4); ScopedArrayObject a(scope, v4->newArrayObject()); uint len; if (callData->argc == 1 && callData->args[0].isNumber()) { bool ok; len = callData->args[0].asArrayLength(&ok); - if (!ok) - return v4->throwRangeError(callData->args[0]); + if (!ok) { + scope.result = v4->throwRangeError(callData->args[0]); + return; + } if (len < 0x1000) a->arrayReserve(len); @@ -76,12 +77,12 @@ ReturnedValue ArrayCtor::construct(const Managed *m, CallData *callData) } a->setArrayLengthUnchecked(len); - return a.asReturnedValue(); + scope.result = a.asReturnedValue(); } -ReturnedValue ArrayCtor::call(const Managed *that, CallData *callData) +void ArrayCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return construct(that, callData); + construct(that, scope, callData); } void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -132,7 +133,8 @@ ReturnedValue ArrayPrototype::method_toString(CallContext *ctx) if (!!f) { ScopedCallData d(scope, 0); d->thisObject = ctx->thisObject(); - return f->call(d); + f->call(scope, d); + return scope.result.asReturnedValue(); } return ObjectPrototype::method_toString(ctx); } @@ -704,7 +706,6 @@ ReturnedValue ArrayPrototype::method_every(CallContext *ctx) ScopedCallData callData(scope, 3); callData->args[2] = instance; callData->thisObject = ctx->argument(1); - ScopedValue r(scope); ScopedValue v(scope); bool ok = true; @@ -716,8 +717,8 @@ ReturnedValue ArrayPrototype::method_every(CallContext *ctx) callData->args[0] = v; callData->args[1] = Primitive::fromDouble(k); - r = callback->call(callData); - ok = r->toBoolean(); + callback->call(scope, callData); + ok = scope.result.toBoolean(); } return Encode(ok); } @@ -740,7 +741,6 @@ ReturnedValue ArrayPrototype::method_some(CallContext *ctx) callData->args[2] = instance; ScopedValue v(scope); - ScopedValue r(scope); for (uint k = 0; k < len; ++k) { bool exists; v = instance->getIndexed(k, &exists); @@ -749,8 +749,8 @@ ReturnedValue ArrayPrototype::method_some(CallContext *ctx) callData->args[0] = v; callData->args[1] = Primitive::fromDouble(k); - r = callback->call(callData); - if (r->toBoolean()) + callback->call(scope, callData); + if (scope.result.toBoolean()) return Encode(true); } return Encode(false); @@ -782,7 +782,7 @@ ReturnedValue ArrayPrototype::method_forEach(CallContext *ctx) callData->args[0] = v; callData->args[1] = Primitive::fromDouble(k); - callback->call(callData); + callback->call(scope, callData); } return Encode::undefined(); } @@ -804,7 +804,6 @@ ReturnedValue ArrayPrototype::method_map(CallContext *ctx) a->arrayReserve(len); a->setArrayLengthUnchecked(len); - ScopedValue mapped(scope); ScopedCallData callData(scope, 3); callData->thisObject = ctx->argument(1); callData->args[2] = instance; @@ -818,8 +817,8 @@ ReturnedValue ArrayPrototype::method_map(CallContext *ctx) callData->args[0] = v; callData->args[1] = Primitive::fromDouble(k); - mapped = callback->call(callData); - a->arraySet(k, mapped); + callback->call(scope, callData); + a->arraySet(k, scope.result); } return a.asReturnedValue(); } @@ -840,7 +839,6 @@ ReturnedValue ArrayPrototype::method_filter(CallContext *ctx) ScopedArrayObject a(scope, ctx->d()->engine->newArrayObject()); a->arrayReserve(len); - ScopedValue selected(scope); ScopedCallData callData(scope, 3); callData->thisObject = ctx->argument(1); callData->args[2] = instance; @@ -856,8 +854,8 @@ ReturnedValue ArrayPrototype::method_filter(CallContext *ctx) callData->args[0] = v; callData->args[1] = Primitive::fromDouble(k); - selected = callback->call(callData); - if (selected->toBoolean()) { + callback->call(scope, callData); + if (scope.result.toBoolean()) { a->arraySet(to, v); ++to; } @@ -879,17 +877,16 @@ ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx) return ctx->engine()->throwTypeError(); uint k = 0; - ScopedValue acc(scope); ScopedValue v(scope); if (ctx->argc() > 1) { - acc = ctx->argument(1); + scope.result = ctx->argument(1); } else { bool kPresent = false; while (k < len && !kPresent) { v = instance->getIndexed(k, &kPresent); if (kPresent) - acc = v; + scope.result = v; ++k; } if (!kPresent) @@ -898,21 +895,21 @@ ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx) ScopedCallData callData(scope, 4); callData->thisObject = Primitive::undefinedValue(); - callData->args[0] = acc; + callData->args[0] = scope.result; callData->args[3] = instance; while (k < len) { bool kPresent; v = instance->getIndexed(k, &kPresent); if (kPresent) { - callData->args[0] = acc; + callData->args[0] = scope.result; callData->args[1] = v; callData->args[2] = Primitive::fromDouble(k); - acc = callback->call(callData); + callback->call(scope, callData); } ++k; } - return acc->asReturnedValue(); + return scope.result.asReturnedValue(); } ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx) @@ -935,16 +932,15 @@ ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx) } uint k = len; - ScopedValue acc(scope); ScopedValue v(scope); if (ctx->argc() > 1) { - acc = ctx->argument(1); + scope.result = ctx->argument(1); } else { bool kPresent = false; while (k > 0 && !kPresent) { v = instance->getIndexed(k - 1, &kPresent); if (kPresent) - acc = v; + scope.result = v; --k; } if (!kPresent) @@ -959,13 +955,13 @@ ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx) bool kPresent; v = instance->getIndexed(k - 1, &kPresent); if (kPresent) { - callData->args[0] = acc; + callData->args[0] = scope.result; callData->args[1] = v; callData->args[2] = Primitive::fromDouble(k - 1); - acc = callback->call(callData); + callback->call(scope, callData); } --k; } - return acc->asReturnedValue(); + return scope.result.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index bae5f9e0da..f49ed76b02 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -70,8 +70,8 @@ struct ArrayCtor: FunctionObject { V4_OBJECT2(ArrayCtor, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct ArrayPrototype: ArrayObject diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index d9da7d7754..57c54e15c4 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -50,17 +50,16 @@ Heap::BooleanCtor::BooleanCtor(QV4::ExecutionContext *scope) { } -ReturnedValue BooleanCtor::construct(const Managed *m, CallData *callData) +void BooleanCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); bool n = callData->argc ? callData->args[0].toBoolean() : false; - return Encode(scope.engine->newBooleanObject(n)); + scope.result = Encode(scope.engine->newBooleanObject(n)); } -ReturnedValue BooleanCtor::call(const Managed *, CallData *callData) +void BooleanCtor::call(const Managed *, Scope &scope, CallData *callData) { bool value = callData->argc ? callData->args[0].toBoolean() : 0; - return Encode(value); + scope.result = Encode(value); } void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index eedafa6126..17543e33e0 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -70,8 +70,8 @@ struct BooleanCtor: FunctionObject { V4_OBJECT2(BooleanCtor, FunctionObject) - static ReturnedValue construct(const Managed *, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct BooleanPrototype: BooleanObject diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index f296ffd71e..fac3d5316c 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -54,32 +54,34 @@ Heap::DataViewCtor::DataViewCtor(QV4::ExecutionContext *scope) { } -ReturnedValue DataViewCtor::construct(const Managed *m, CallData *callData) +void DataViewCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); Scoped buffer(scope, callData->argument(0)); - if (!buffer) - return scope.engine->throwTypeError(); + if (!buffer) { + scope.result = scope.engine->throwTypeError(); + return; + } double bo = callData->argc > 1 ? callData->args[1].toNumber() : 0; uint byteOffset = (uint)bo; uint bufferLength = buffer->d()->data->size; double bl = callData->argc < 3 || callData->args[2].isUndefined() ? (bufferLength - bo) : callData->args[2].toNumber(); uint byteLength = (uint)bl; - if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) - return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); + if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) { + scope.result = scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); + return; + } Scoped a(scope, scope.engine->memoryManager->allocObject()); a->d()->buffer = buffer->d(); a->d()->byteLength = byteLength; a->d()->byteOffset = byteOffset; - return a.asReturnedValue(); - + scope.result = a.asReturnedValue(); } -ReturnedValue DataViewCtor::call(const Managed *that, CallData *callData) +void DataViewCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return construct(that, callData); + construct(that, scope, callData); } diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 2e8e94cecd..f996a4c2f1 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -76,8 +76,8 @@ struct DataViewCtor: FunctionObject { V4_OBJECT2(DataViewCtor, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct DataView : Object diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 0cd72d6811..6acc76cdd8 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -651,9 +651,8 @@ Heap::DateCtor::DateCtor(QV4::ExecutionContext *scope) { } -ReturnedValue DateCtor::construct(const Managed *m, CallData *callData) +void DateCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); double t = 0; if (callData->argc == 0) @@ -687,13 +686,13 @@ ReturnedValue DateCtor::construct(const Managed *m, CallData *callData) t = TimeClip(UTC(t)); } - return Encode(scope.engine->newDateObject(Primitive::fromDouble(t))); + scope.result = Encode(scope.engine->newDateObject(Primitive::fromDouble(t))); } -ReturnedValue DateCtor::call(const Managed *m, CallData *) +void DateCtor::call(const Managed *m, Scope &scope, CallData *) { double t = currentTime(); - return static_cast(m)->engine()->newString(ToString(t))->asReturnedValue(); + scope.result = static_cast(m)->engine()->newString(ToString(t)); } void DatePrototype::init(ExecutionEngine *engine, Object *ctor) @@ -1311,7 +1310,8 @@ ReturnedValue DatePrototype::method_toJSON(CallContext *ctx) ScopedCallData callData(scope); callData->thisObject = ctx->thisObject(); - return toIso->call(callData); + toIso->call(scope, callData); + return scope.result.asReturnedValue(); } void DatePrototype::timezoneUpdated() diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index e3615d76a7..13e9e04040 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -104,8 +104,8 @@ struct DateCtor: FunctionObject { V4_OBJECT2(DateCtor, FunctionObject) - static ReturnedValue construct(const Managed *, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *); + static void construct(const Managed *, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *); }; struct DatePrototype: DateObject diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index f560b3be4f..4a26b8b662 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1506,6 +1506,11 @@ void ExecutionEngine::assertObjectBelongsToEngine(const Heap::Base &baseObject) Q_UNUSED(baseObject); } +void ExecutionEngine::failStackLimitCheck(Scope &scope) +{ + scope.result = throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); +} + // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 933241ea27..ada6941381 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -478,7 +478,10 @@ public: void assertObjectBelongsToEngine(const Heap::Base &baseObject); - bool checkStackLimits(ReturnedValue &exception); + bool checkStackLimits(Scope &scope); + +private: + void failStackLimitCheck(Scope &scope); }; // This is a trick to tell the code generators that functions taking a NoThrowContext won't @@ -571,7 +574,7 @@ inline void Value::mark(ExecutionEngine *e) o->mark(e); } -#define CHECK_STACK_LIMITS(v4) { ReturnedValue e; if ((v4)->checkStackLimits(e)) return e; } \ +#define CHECK_STACK_LIMITS(v4, scope) if ((v4)->checkStackLimits(scope)) return; \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); struct ExecutionEngineCallDepthRecorder @@ -582,10 +585,10 @@ struct ExecutionEngineCallDepthRecorder ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; } }; -inline bool ExecutionEngine::checkStackLimits(ReturnedValue &exception) +inline bool ExecutionEngine::checkStackLimits(Scope &scope) { if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) { - exception = throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); + failStackLimitCheck(scope); return true; } diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 2db04bbfda..3763bf2613 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -239,16 +239,15 @@ Heap::ErrorCtor::ErrorCtor(QV4::ExecutionContext *scope, const QString &name) { } -ReturnedValue ErrorCtor::construct(const Managed *m, CallData *callData) +void ErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create(scope.engine, v); } -ReturnedValue ErrorCtor::call(const Managed *that, CallData *callData) +void ErrorCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return static_cast(that)->construct(callData); + static_cast(that)->construct(scope, callData); } Heap::EvalErrorCtor::EvalErrorCtor(QV4::ExecutionContext *scope) @@ -256,11 +255,10 @@ Heap::EvalErrorCtor::EvalErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue EvalErrorCtor::construct(const Managed *m, CallData *callData) +void EvalErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create(scope.engine, v); } Heap::RangeErrorCtor::RangeErrorCtor(QV4::ExecutionContext *scope) @@ -268,11 +266,10 @@ Heap::RangeErrorCtor::RangeErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue RangeErrorCtor::construct(const Managed *m, CallData *callData) +void RangeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create(scope.engine, v); } Heap::ReferenceErrorCtor::ReferenceErrorCtor(QV4::ExecutionContext *scope) @@ -280,11 +277,10 @@ Heap::ReferenceErrorCtor::ReferenceErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ReferenceErrorCtor::construct(const Managed *m, CallData *callData) +void ReferenceErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create(scope.engine, v); } Heap::SyntaxErrorCtor::SyntaxErrorCtor(QV4::ExecutionContext *scope) @@ -292,11 +288,10 @@ Heap::SyntaxErrorCtor::SyntaxErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue SyntaxErrorCtor::construct(const Managed *m, CallData *callData) +void SyntaxErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create(scope.engine, v); } Heap::TypeErrorCtor::TypeErrorCtor(QV4::ExecutionContext *scope) @@ -304,11 +299,10 @@ Heap::TypeErrorCtor::TypeErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue TypeErrorCtor::construct(const Managed *m, CallData *callData) +void TypeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create(scope.engine, v); } Heap::URIErrorCtor::URIErrorCtor(QV4::ExecutionContext *scope) @@ -316,11 +310,10 @@ Heap::URIErrorCtor::URIErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue URIErrorCtor::construct(const Managed *m, CallData *callData) +void URIErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create(scope.engine, v); } void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t) diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 1ca2fedd7b..42a3d05d9f 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -221,50 +221,50 @@ struct ErrorCtor: FunctionObject { V4_OBJECT2(ErrorCtor, FunctionObject) - static ReturnedValue construct(const Managed *, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct EvalErrorCtor: ErrorCtor { V4_OBJECT2(EvalErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); }; struct RangeErrorCtor: ErrorCtor { V4_OBJECT2(RangeErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); }; struct ReferenceErrorCtor: ErrorCtor { V4_OBJECT2(ReferenceErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); }; struct SyntaxErrorCtor: ErrorCtor { V4_OBJECT2(SyntaxErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); }; struct TypeErrorCtor: ErrorCtor { V4_OBJECT2(TypeErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); }; struct URIErrorCtor: ErrorCtor { V4_OBJECT2(URIErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 5d2c57a2ba..041044930b 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -166,14 +166,6 @@ ReturnedValue FunctionObject::name() const return get(scope()->engine->id_name()); } - -ReturnedValue FunctionObject::newInstance() -{ - Scope scope(internalClass()->engine); - ScopedCallData callData(scope); - return construct(callData); -} - ReturnedValue FunctionObject::construct(const Managed *that, CallData *) { return static_cast(that)->engine()->throwTypeError(); @@ -251,9 +243,8 @@ Heap::FunctionCtor::FunctionCtor(QV4::ExecutionContext *scope) } // 15.3.2 -ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData) +void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callData) { - Scope scope(static_cast(that)->engine()); Scoped f(scope, static_cast(that)); QString arguments; @@ -266,8 +257,10 @@ ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData) } body = callData->args[callData->argc - 1].toQString(); } - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}'); @@ -278,13 +271,17 @@ ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData) const bool parsed = parser.parseExpression(); - if (!parsed) - return scope.engine->throwSyntaxError(QLatin1String("Parse error")); + if (!parsed) { + scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error")); + return; + } using namespace QQmlJS::AST; FunctionExpression *fe = QQmlJS::AST::cast(parser.rootNode()); - if (!fe) - return scope.engine->throwSyntaxError(QLatin1String("Parse error")); + if (!fe) { + scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error")); + return; + } IR::Module module(scope.engine->debugger != 0); @@ -297,13 +294,13 @@ ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData) Function *vmf = compilationUnit->linkToEngine(scope.engine); ExecutionContext *global = scope.engine->rootContext(); - return FunctionObject::createScriptFunction(global, vmf)->asReturnedValue(); + scope.result = FunctionObject::createScriptFunction(global, vmf); } // 15.3.1: This is equivalent to new Function(...) -ReturnedValue FunctionCtor::call(const Managed *that, CallData *callData) +void FunctionCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return construct(that, callData); + construct(that, scope, callData); } DEFINE_OBJECT_VTABLE(FunctionPrototype); @@ -376,7 +373,8 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) } callData->thisObject = ctx->argument(0); - return o->call(callData); + o->call(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue FunctionPrototype::method_call(CallContext *ctx) @@ -393,7 +391,9 @@ ReturnedValue FunctionPrototype::method_call(CallContext *ctx) callData->args[i - 1] = ctx->args()[i]; } callData->thisObject = ctx->argument(0); - return o->call(callData); + + o->call(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue FunctionPrototype::method_bind(CallContext *ctx) @@ -422,14 +422,15 @@ Heap::ScriptFunction::ScriptFunction(QV4::ExecutionContext *scope, Function *fun { } -ReturnedValue ScriptFunction::construct(const Managed *that, CallData *callData) +void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = static_cast(that)->engine(); - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); Scoped f(scope, static_cast(that)); @@ -442,39 +443,37 @@ ReturnedValue ScriptFunction::construct(const Managed *that, CallData *callData) Scoped ctx(scope, v4->currentContext->newCallContext(f, callData)); v4->pushContext(ctx); - ScopedValue result(scope, Q_V4_PROFILE(v4, f->function())); + scope.result = Q_V4_PROFILE(v4, f->function()); if (f->function()->compiledFunction->hasQmlDependencies()) - QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction); - - if (v4->hasException) - return Encode::undefined(); + QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope); - if (result->isObject()) - return result->asReturnedValue(); - return obj.asReturnedValue(); + if (v4->hasException) { + scope.result = Encode::undefined(); + } else if (!scope.result.isObject()) { + scope.result = obj.asReturnedValue(); + } } -ReturnedValue ScriptFunction::call(const Managed *that, CallData *callData) +void ScriptFunction::call(const Managed *that, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = static_cast(that)->engine(); - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); Scoped f(scope, static_cast(that)); Scoped ctx(scope, v4->currentContext->newCallContext(f, callData)); v4->pushContext(ctx); - ScopedValue result(scope, Q_V4_PROFILE(v4, f->function())); + scope.result = Q_V4_PROFILE(v4, f->function()); if (f->function()->compiledFunction->hasQmlDependencies()) - QQmlPropertyCapture::registerQmlDependencies(scope.engine, f->function()->compiledFunction); - - return result->asReturnedValue(); + QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope); } DEFINE_OBJECT_VTABLE(SimpleScriptFunction); @@ -511,14 +510,15 @@ Heap::SimpleScriptFunction::SimpleScriptFunction(QV4::ExecutionContext *scope, F } } -ReturnedValue SimpleScriptFunction::construct(const Managed *that, CallData *callData) +void SimpleScriptFunction::construct(const Managed *that, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = static_cast(that)->engine(); - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); Scoped f(scope, static_cast(that)); @@ -542,24 +542,24 @@ ReturnedValue SimpleScriptFunction::construct(const Managed *that, CallData *cal v4->pushContext(&ctx); Q_ASSERT(v4->current == &ctx); - ScopedObject result(scope, Q_V4_PROFILE(v4, f->function())); + scope.result = Q_V4_PROFILE(v4, f->function()); if (f->function()->compiledFunction->hasQmlDependencies()) - QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction); + QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope); - if (!result) - return callData->thisObject.asReturnedValue(); - return result.asReturnedValue(); + if (!scope.result.isManaged() || !scope.result.managed()) + scope.result = callData->thisObject; } -ReturnedValue SimpleScriptFunction::call(const Managed *that, CallData *callData) +void SimpleScriptFunction::call(const Managed *that, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = static_cast(that)->internalClass()->engine; - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); Scoped f(scope, static_cast(that)); @@ -579,12 +579,10 @@ ReturnedValue SimpleScriptFunction::call(const Managed *that, CallData *callData v4->pushContext(&ctx); Q_ASSERT(v4->current == &ctx); - ScopedValue result(scope, Q_V4_PROFILE(v4, f->function())); + scope.result = Q_V4_PROFILE(v4, f->function()); if (f->function()->compiledFunction->hasQmlDependencies()) - QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction); - - return result->asReturnedValue(); + QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope); } Heap::Object *SimpleScriptFunction::protoForConstructor() @@ -606,20 +604,21 @@ Heap::BuiltinFunction::BuiltinFunction(QV4::ExecutionContext *scope, QV4::String { } -ReturnedValue BuiltinFunction::construct(const Managed *f, CallData *) +void BuiltinFunction::construct(const Managed *f, Scope &scope, CallData *) { - return static_cast(f)->internalClass()->engine->throwTypeError(); + scope.result = static_cast(f)->internalClass()->engine->throwTypeError(); } -ReturnedValue BuiltinFunction::call(const Managed *that, CallData *callData) +void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) { const BuiltinFunction *f = static_cast(that); - ExecutionEngine *v4 = f->internalClass()->engine; - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); CallContext::Data ctx(v4); @@ -630,18 +629,19 @@ ReturnedValue BuiltinFunction::call(const Managed *that, CallData *callData) v4->pushContext(&ctx); Q_ASSERT(v4->current == &ctx); - return f->d()->code(static_cast(v4->currentContext)); + scope.result = f->d()->code(static_cast(v4->currentContext)); } -ReturnedValue IndexedBuiltinFunction::call(const Managed *that, CallData *callData) +void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) { const IndexedBuiltinFunction *f = static_cast(that); - ExecutionEngine *v4 = f->internalClass()->engine; - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); CallContext::Data ctx(v4); @@ -652,7 +652,7 @@ ReturnedValue IndexedBuiltinFunction::call(const Managed *that, CallData *callDa v4->pushContext(&ctx); Q_ASSERT(v4->current == &ctx); - return f->d()->code(static_cast(v4->currentContext), f->d()->index); + scope.result = f->d()->code(static_cast(v4->currentContext), f->d()->index); } DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); @@ -685,12 +685,13 @@ Heap::BoundFunction::BoundFunction(QV4::ExecutionContext *scope, QV4::FunctionOb f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } -ReturnedValue BoundFunction::call(const Managed *that, CallData *dd) +void BoundFunction::call(const Managed *that, Scope &scope, CallData *dd) { const BoundFunction *f = static_cast(that); - Scope scope(f->engine()); - if (scope.hasException()) - return Encode::undefined(); + if (scope.hasException()) { + scope.result = Encode::undefined(); + return; + } Scoped boundArgs(scope, f->boundArgs()); ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc); @@ -702,15 +703,16 @@ ReturnedValue BoundFunction::call(const Managed *that, CallData *dd) } memcpy(argp, dd->args, dd->argc*sizeof(Value)); ScopedFunctionObject t(scope, f->target()); - return t->call(callData); + t->call(scope, callData); } -ReturnedValue BoundFunction::construct(const Managed *that, CallData *dd) +void BoundFunction::construct(const Managed *that, Scope &scope, CallData *dd) { const BoundFunction *f = static_cast(that); - Scope scope(f->engine()); - if (scope.hasException()) - return Encode::undefined(); + if (scope.hasException()) { + scope.result = Encode::undefined(); + return; + } Scoped boundArgs(scope, f->boundArgs()); ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc); @@ -721,7 +723,7 @@ ReturnedValue BoundFunction::construct(const Managed *that, CallData *dd) } memcpy(argp, dd->args, dd->argc*sizeof(Value)); ScopedFunctionObject t(scope, f->target()); - return t->construct(callData); + t->construct(scope, callData); } void BoundFunction::markObjects(Heap::Base *that, ExecutionEngine *e) diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 4a4545eca4..be80b87873 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -145,8 +145,6 @@ struct Q_QML_EXPORT FunctionObject: Object { void init(String *name, bool createProto); - ReturnedValue newInstance(); - using Object::construct; using Object::call; static ReturnedValue construct(const Managed *that, CallData *); @@ -178,8 +176,8 @@ struct FunctionCtor: FunctionObject { V4_OBJECT2(FunctionCtor, FunctionObject) - static ReturnedValue construct(const Managed *that, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *that, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct FunctionPrototype: FunctionObject @@ -202,20 +200,20 @@ struct Q_QML_EXPORT BuiltinFunction: FunctionObject { return scope->engine()->memoryManager->allocObject(scope, name, code); } - static ReturnedValue construct(const Managed *, CallData *); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct IndexedBuiltinFunction: FunctionObject { V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *) + static void construct(const Managed *m, Scope &scope, CallData *) { - return static_cast(m)->engine()->throwTypeError(); + scope.result = static_cast(m)->engine()->throwTypeError(); } - static ReturnedValue call(const Managed *that, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; Heap::IndexedBuiltinFunction::IndexedBuiltinFunction(QV4::ExecutionContext *scope, uint index, @@ -231,8 +229,8 @@ struct SimpleScriptFunction: FunctionObject { V4_OBJECT2(SimpleScriptFunction, FunctionObject) V4_INTERNALCLASS(simpleScriptFunctionClass) - static ReturnedValue construct(const Managed *, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); Heap::Object *protoForConstructor(); }; @@ -240,8 +238,8 @@ struct SimpleScriptFunction: FunctionObject { struct ScriptFunction: SimpleScriptFunction { V4_OBJECT2(ScriptFunction, FunctionObject) - static ReturnedValue construct(const Managed *, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; @@ -257,8 +255,8 @@ struct BoundFunction: FunctionObject { Value boundThis() const { return d()->boundThis; } Heap::MemberData *boundArgs() const { return d()->boundArgs; } - static ReturnedValue construct(const Managed *, CallData *d); - static ReturnedValue call(const Managed *that, CallData *dd); + static void construct(const Managed *, Scope &scope, CallData *d); + static void call(const Managed *that, Scope &scope, CallData *dd); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 2c767e3302..d8130c1cac 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -338,13 +338,14 @@ Heap::EvalFunction::EvalFunction(QV4::ExecutionContext *scope) f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); } -ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const +void EvalFunction::evalCall(Scope &scope, CallData *callData, bool directCall) const { - if (callData->argc < 1) - return Encode::undefined(); + if (callData->argc < 1) { + scope.result = Encode::undefined(); + return; + } ExecutionEngine *v4 = engine(); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); ExecutionContext *currentContext = v4->currentContext; @@ -356,8 +357,10 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const ctx = v4->pushGlobalContext(); } - if (!callData->args[0].isString()) - return callData->args[0].asReturnedValue(); + if (!callData->args[0].isString()) { + scope.result = callData->args[0].asReturnedValue(); + return; + } const QString code = callData->args[0].stringValue()->toQString(); bool inheritContext = !ctx->d()->strictMode; @@ -366,18 +369,23 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const script.strictMode = (directCall && currentContext->d()->strictMode); script.inheritContext = inheritContext; script.parse(); - if (v4->hasException) - return Encode::undefined(); + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } Function *function = script.function(); - if (!function) - return Encode::undefined(); + if (!function) { + scope.result = Encode::undefined(); + return; + } if (function->isStrict() || (ctx->d()->strictMode)) { ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function)); ScopedCallData callData(scope, 0); callData->thisObject = ctx->thisObject(); - return e->call(callData); + e->call(scope, callData); + return; } ContextStateSaver stateSaver(scope, ctx); @@ -386,14 +394,14 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const ctx->d()->strictMode = false; ctx->d()->compilationUnit = function->compilationUnit; - return Q_V4_PROFILE(ctx->engine(), function); + scope.result = Q_V4_PROFILE(ctx->engine(), function); } -ReturnedValue EvalFunction::call(const Managed *that, CallData *callData) +void EvalFunction::call(const Managed *that, Scope &scope, CallData *callData) { // indirect call - return static_cast(that)->evalCall(callData, false); + static_cast(that)->evalCall(scope, callData, false); } diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h index ea7a3b06ce..403639f8c1 100644 --- a/src/qml/jsruntime/qv4globalobject_p.h +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -69,9 +69,9 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject { V4_OBJECT2(EvalFunction, FunctionObject) - ReturnedValue evalCall(CallData *callData, bool directCall) const; + void evalCall(Scope &scope, CallData *callData, bool directCall) const; - static ReturnedValue call(const Managed *that, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct GlobalFunctions diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index b3462fe9b1..c33d2cad11 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -122,7 +122,7 @@ void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status) QV4::ScopedCallData callData(scope, 1); callData->thisObject = v4->globalObject->asReturnedValue(); callData->args[0] = status; - f->call(callData); + f->call(scope, callData); if (scope.hasException()) scope.engine->catchException(); } diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 0ae7c33dea..7661dd1903 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -685,53 +685,53 @@ static QString quote(const QString &str) QString Stringify::Str(const QString &key, const Value &v) { Scope scope(v4); + scope.result = v; - ScopedValue value(scope, v); - ScopedObject o(scope, value); + ScopedObject o(scope, scope.result); if (o) { ScopedString s(scope, v4->newString(QStringLiteral("toJSON"))); ScopedFunctionObject toJSON(scope, o->get(s)); if (!!toJSON) { ScopedCallData callData(scope, 1); - callData->thisObject = value; + callData->thisObject = scope.result; callData->args[0] = v4->newString(key); - value = toJSON->call(callData); + toJSON->call(scope, callData); } } if (replacerFunction) { ScopedObject holder(scope, v4->newObject()); - holder->put(scope.engine, QString(), value); + holder->put(scope.engine, QString(), scope.result); ScopedCallData callData(scope, 2); callData->args[0] = v4->newString(key); - callData->args[1] = value; + callData->args[1] = scope.result; callData->thisObject = holder; - value = replacerFunction->call(callData); + replacerFunction->call(scope, callData); } - o = value->asReturnedValue(); + o = scope.result.asReturnedValue(); if (o) { if (NumberObject *n = o->as()) - value = Encode(n->value()); + scope.result = Encode(n->value()); else if (StringObject *so = o->as()) - value = so->d()->string; + scope.result = so->d()->string; else if (BooleanObject *b = o->as()) - value = Encode(b->value()); + scope.result = Encode(b->value()); } - if (value->isNull()) + if (scope.result.isNull()) return QStringLiteral("null"); - if (value->isBoolean()) - return value->booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); - if (value->isString()) - return quote(value->stringValue()->toQString()); - - if (value->isNumber()) { - double d = value->toNumber(); - return std::isfinite(d) ? value->toQString() : QStringLiteral("null"); + if (scope.result.isBoolean()) + return scope.result.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); + if (scope.result.isString()) + return quote(scope.result.stringValue()->toQString()); + + if (scope.result.isNumber()) { + double d = scope.result.toNumber(); + return std::isfinite(d) ? scope.result.toQString() : QStringLiteral("null"); } - o = value->asReturnedValue(); + o = scope.result.asReturnedValue(); if (o) { if (!o->as()) { if (o->as()) { diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 46e47307ef..42e561bc7c 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -451,7 +451,8 @@ ReturnedValue Lookup::getterAccessor0(Lookup *l, ExecutionEngine *engine, const ScopedCallData callData(scope, 0); callData->thisObject = object; - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } l->getter = getterFallback; @@ -473,7 +474,8 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const ScopedCallData callData(scope, 0); callData->thisObject = object; - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } l->getter = getterFallback; @@ -498,7 +500,8 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const ScopedCallData callData(scope, 0); callData->thisObject = object; - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } } @@ -542,7 +545,8 @@ ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engin ScopedCallData callData(scope, 0); callData->thisObject = object; - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } l->getter = getterGeneric; @@ -562,7 +566,8 @@ ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engin ScopedCallData callData(scope, 0); callData->thisObject = object; - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } l->getter = getterGeneric; @@ -665,7 +670,8 @@ ReturnedValue Lookup::globalGetterAccessor0(Lookup *l, ExecutionEngine *engine) ScopedCallData callData(scope, 0); callData->thisObject = Primitive::undefinedValue(); - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -683,7 +689,8 @@ ReturnedValue Lookup::globalGetterAccessor1(Lookup *l, ExecutionEngine *engine) ScopedCallData callData(scope, 0); callData->thisObject = Primitive::undefinedValue(); - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -704,7 +711,8 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) ScopedCallData callData(scope, 0); callData->thisObject = Primitive::undefinedValue(); - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } } diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index f6b3e9a2b3..d27eef1d79 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -76,17 +76,16 @@ Heap::NumberCtor::NumberCtor(QV4::ExecutionContext *scope) { } -ReturnedValue NumberCtor::construct(const Managed *m, CallData *callData) +void NumberCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(m->cast()->engine()); double dbl = callData->argc ? callData->args[0].toNumber() : 0.; - return Encode(scope.engine->newNumberObject(dbl)); + scope.result = Encode(scope.engine->newNumberObject(dbl)); } -ReturnedValue NumberCtor::call(const Managed *, CallData *callData) +void NumberCtor::call(const Managed *, Scope &scope, CallData *callData) { double dbl = callData->argc ? callData->args[0].toNumber() : 0.; - return Encode(dbl); + scope.result = Encode(dbl); } void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index bbe22af4bb..2416165c78 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -79,8 +79,8 @@ struct NumberCtor: FunctionObject { V4_OBJECT2(NumberCtor, FunctionObject) - static ReturnedValue construct(const Managed *that, CallData *callData); - static ReturnedValue call(const Managed *, CallData *callData); + static void construct(const Managed *that, Scope &scope, CallData *callData); + static void call(const Managed *, Scope &scope, CallData *callData); }; struct NumberPrototype: NumberObject diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index ff3208485e..0fc5f95dad 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -109,7 +109,8 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property Scope scope(f->engine()); ScopedCallData callData(scope); callData->thisObject = thisObject; - return f->call(callData); + f->call(scope, callData); + return scope.result.asReturnedValue(); } void Object::putValue(uint memberIndex, const Value &value) @@ -128,7 +129,7 @@ void Object::putValue(uint memberIndex, const Value &value) ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; - setter->call(callData); + setter->call(scope, callData); return; } goto reject; @@ -389,14 +390,14 @@ bool Object::hasOwnProperty(uint index) const return false; } -ReturnedValue Object::construct(const Managed *m, CallData *) +void Object::construct(const Managed *m, Scope &scope, CallData *) { - return static_cast(m)->engine()->throwTypeError(); + scope.result = static_cast(m)->engine()->throwTypeError(); } -ReturnedValue Object::call(const Managed *m, CallData *) +void Object::call(const Managed *m, Scope &scope, CallData *) { - return static_cast(m)->engine()->throwTypeError(); + scope.result = static_cast(m)->engine()->throwTypeError(); } ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty) @@ -744,7 +745,7 @@ void Object::internalPut(String *name, const Value &value) ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; - setter->call(callData); + setter->call(scope, callData); return; } @@ -814,7 +815,7 @@ void Object::internalPutIndexed(uint index, const Value &value) ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; - setter->call(callData); + setter->call(scope, callData); return; } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index e4431a9fc9..114f542d18 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -112,8 +112,8 @@ struct Object : Base { struct ObjectVTable { VTable vTable; - ReturnedValue (*call)(const Managed *, CallData *data); - ReturnedValue (*construct)(const Managed *, CallData *data); + void (*call)(const Managed *, Scope &scope, CallData *data); + void (*construct)(const Managed *, Scope &scope, CallData *data); ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty); ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty); void (*put)(Managed *, String *name, const Value &value); @@ -324,14 +324,14 @@ public: { vtable()->advanceIterator(this, it, name, index, p, attributes); } uint getLength() const { return vtable()->getLength(this); } - inline ReturnedValue construct(CallData *d) const - { return vtable()->construct(this, d); } - inline ReturnedValue call(CallData *d) const - { return vtable()->call(this, d); } + inline void construct(Scope &scope, CallData *d) const + { return vtable()->construct(this, scope, d); } + inline void call(Scope &scope, CallData *d) const + { vtable()->call(this, scope, d); } protected: static void markObjects(Heap::Base *that, ExecutionEngine *e); - static ReturnedValue construct(const Managed *m, CallData *); - static ReturnedValue call(const Managed *m, CallData *); + static void construct(const Managed *m, Scope &scope, CallData *); + static void call(const Managed *m, Scope &scope, CallData *); static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static void put(Managed *m, String *name, const Value &value); diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 015294e48a..cfd76166f2 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -59,28 +59,30 @@ Heap::ObjectCtor::ObjectCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ObjectCtor::construct(const Managed *that, CallData *callData) +void ObjectCtor::construct(const Managed *that, Scope &scope, CallData *callData) { const ObjectCtor *ctor = static_cast(that); ExecutionEngine *v4 = ctor->engine(); - Scope scope(v4); if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) { ScopedObject obj(scope, v4->newObject()); ScopedObject proto(scope, ctor->get(v4->id_prototype())); if (!!proto) obj->setPrototype(proto); - return obj.asReturnedValue(); + scope.result = obj.asReturnedValue(); + } else { + scope.result = RuntimeHelpers::toObject(scope.engine, callData->args[0]); } - return RuntimeHelpers::toObject(scope.engine, callData->args[0]); } -ReturnedValue ObjectCtor::call(const Managed *m, CallData *callData) +void ObjectCtor::call(const Managed *m, Scope &scope, CallData *callData) { const ObjectCtor *ctor = static_cast(m); ExecutionEngine *v4 = ctor->engine(); - if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) - return v4->newObject()->asReturnedValue(); - return RuntimeHelpers::toObject(v4, callData->args[0]); + if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) { + scope.result = v4->newObject()->asReturnedValue(); + } else { + scope.result = RuntimeHelpers::toObject(v4, callData->args[0]); + } } void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) @@ -413,7 +415,8 @@ ReturnedValue ObjectPrototype::method_toLocaleString(CallContext *ctx) return ctx->engine()->throwTypeError(); ScopedCallData callData(scope); callData->thisObject = o; - return f->call(callData); + f->call(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue ObjectPrototype::method_valueOf(CallContext *ctx) diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index ac1964103e..47a37b196f 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -70,8 +70,8 @@ struct ObjectCtor: FunctionObject { V4_OBJECT2(ObjectCtor, FunctionObject) - static ReturnedValue construct(const Managed *that, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *that, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct ObjectPrototype: Object diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 44c7fc9823..deed9b86a2 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -816,7 +816,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase } } - f->call(callData); + f->call(scope, callData); if (scope.hasException()) { QQmlError error = v4->catchExceptionAsQmlError(); if (error.description().isEmpty()) { @@ -1750,7 +1750,7 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index) { Scope valueScope(scope); - Scoped method(valueScope, scope->d()->engine->memoryManager->allocObject(scope)); + Scoped method(valueScope, valueScope.engine->memoryManager->allocObject(scope)); method->d()->propertyCache = valueType->d()->propertyCache; method->d()->index = index; method->d()->valueTypeWrapper = valueType->d(); @@ -1810,25 +1810,32 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, con return Encode::undefined(); } -ReturnedValue QObjectMethod::call(const Managed *m, CallData *callData) +void QObjectMethod::call(const Managed *m, Scope &scope, CallData *callData) { const QObjectMethod *This = static_cast(m); - return This->callInternal(callData); + This->callInternal(callData, scope); } -ReturnedValue QObjectMethod::callInternal(CallData *callData) const +void QObjectMethod::callInternal(CallData *callData, Scope &scope) const { ExecutionEngine *v4 = engine(); ExecutionContext *context = v4->currentContext; - if (d()->index == DestroyMethod) - return method_destroy(context, callData->args, callData->argc); - else if (d()->index == ToStringMethod) - return method_toString(context); + if (d()->index == DestroyMethod) { + scope.result = method_destroy(context, callData->args, callData->argc); + return; + } + + else if (d()->index == ToStringMethod) { + scope.result = method_toString(context); + return; + } QQmlObjectOrGadget object(d()->object.data()); if (!d()->object) { - if (!d()->valueTypeWrapper) - return Encode::undefined(); + if (!d()->valueTypeWrapper) { + scope.result = Encode::undefined(); + return; + } object = QQmlObjectOrGadget(d()->propertyCache.data(), d()->valueTypeWrapper->gadgetPtr); } @@ -1837,16 +1844,20 @@ ReturnedValue QObjectMethod::callInternal(CallData *callData) const if (d()->propertyCache) { QQmlPropertyData *data = d()->propertyCache->method(d()->index); - if (!data) - return QV4::Encode::undefined(); + if (!data) { + scope.result = QV4::Encode::undefined(); + return; + } method = *data; } else { const QMetaObject *mo = d()->object->metaObject(); const QMetaMethod moMethod = mo->method(d()->index); method.load(moMethod); - if (method.coreIndex == -1) - return QV4::Encode::undefined(); + if (method.coreIndex == -1) { + scope.result = QV4::Encode::undefined(); + return; + } // Look for overloaded methods QByteArray methodName = moMethod.name(); @@ -1862,21 +1873,20 @@ ReturnedValue QObjectMethod::callInternal(CallData *callData) const } if (method.isV4Function()) { - Scope scope(v4); - QV4::ScopedValue rv(scope, QV4::Primitive::undefinedValue()); - QQmlV4Function func(callData, rv, v4); + scope.result = QV4::Encode::undefined(); + QQmlV4Function func(callData, &scope.result, v4); QQmlV4Function *funcptr = &func; void *args[] = { 0, &funcptr }; object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex, args); - return rv->asReturnedValue(); + return; } if (!method.isOverload()) { - return CallPrecise(object, method, v4, callData); + scope.result = CallPrecise(object, method, v4, callData); } else { - return CallOverloaded(object, method, v4, callData, d()->propertyCache); + scope.result = CallOverloaded(object, method, v4, callData, d()->propertyCache); } } @@ -1936,10 +1946,10 @@ void QMetaObjectWrapper::init(ExecutionEngine *) { } } -ReturnedValue QMetaObjectWrapper::construct(const Managed *m, CallData *callData) +void QMetaObjectWrapper::construct(const Managed *m, Scope &scope, CallData *callData) { const QMetaObjectWrapper *This = static_cast(m); - return This->constructInternal(callData); + scope.result = This->constructInternal(callData); } ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const { diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index c384321f1c..dab59886de 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -193,9 +193,9 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject QV4::ReturnedValue method_toString(QV4::ExecutionContext *ctx) const; QV4::ReturnedValue method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const; - static ReturnedValue call(const Managed *, CallData *callData); + static void call(const Managed *, Scope &scope, CallData *callData); - ReturnedValue callInternal(CallData *callData) const; + void callInternal(CallData *callData, Scope &scope) const; static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); @@ -209,7 +209,7 @@ struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject V4_NEEDS_DESTROY static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); - static ReturnedValue construct(const Managed *, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); static bool isEqualTo(Managed *a, Managed *b); const QMetaObject *metaObject() const { return d()->metaObject; } diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 05752dc6dc..ab00859b3b 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -231,34 +231,39 @@ void Heap::RegExpCtor::clearLastMatch() lastMatchEnd = 0; } -ReturnedValue RegExpCtor::construct(const Managed *m, CallData *callData) +void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); - ScopedValue r(scope, callData->argument(0)); ScopedValue f(scope, callData->argument(1)); Scoped re(scope, r); if (re) { - if (!f->isUndefined()) - return scope.engine->throwTypeError(); + if (!f->isUndefined()) { + scope.result = scope.engine->throwTypeError(); + return; + } Scoped regexp(scope, re->value()); - return Encode(scope.engine->newRegExpObject(regexp, re->global())); + scope.result = Encode(scope.engine->newRegExpObject(regexp, re->global())); + return; } QString pattern; if (!r->isUndefined()) pattern = r->toQString(); - if (scope.hasException()) - return Encode::undefined(); + if (scope.hasException()) { + scope.result = Encode::undefined(); + return; + } bool global = false; bool ignoreCase = false; bool multiLine = false; if (!f->isUndefined()) { f = RuntimeHelpers::toString(scope.engine, f); - if (scope.hasException()) - return Encode::undefined(); + if (scope.hasException()) { + scope.result = Encode::undefined(); + return; + } QString str = f->stringValue()->toQString(); for (int i = 0; i < str.length(); ++i) { if (str.at(i) == QLatin1Char('g') && !global) { @@ -268,26 +273,31 @@ ReturnedValue RegExpCtor::construct(const Managed *m, CallData *callData) } else if (str.at(i) == QLatin1Char('m') && !multiLine) { multiLine = true; } else { - return scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); + scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); + return; } } } Scoped regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine)); - if (!regexp->isValid()) - return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); + if (!regexp->isValid()) { + scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); + return; + } - return Encode(scope.engine->newRegExpObject(regexp, global)); + scope.result = Encode(scope.engine->newRegExpObject(regexp, global)); } -ReturnedValue RegExpCtor::call(const Managed *that, CallData *callData) +void RegExpCtor::call(const Managed *that, Scope &scope, CallData *callData) { if (callData->argc > 0 && callData->args[0].as()) { - if (callData->argc == 1 || callData->args[1].isUndefined()) - return callData->args[0].asReturnedValue(); + if (callData->argc == 1 || callData->args[1].isUndefined()) { + scope.result = callData->args[0]; + return; + } } - return construct(that, callData); + construct(that, scope, callData); } void RegExpCtor::markObjects(Heap::Base *that, ExecutionEngine *e) @@ -419,7 +429,8 @@ ReturnedValue RegExpPrototype::method_compile(CallContext *ctx) ScopedCallData callData(scope, ctx->argc()); memcpy(callData->args, ctx->args(), ctx->argc()*sizeof(Value)); - Scoped re(scope, ctx->d()->engine->regExpCtor()->as()->construct(callData)); + ctx->d()->engine->regExpCtor()->as()->construct(scope, callData); + Scoped re(scope, scope.result.asReturnedValue()); r->d()->value = re->value(); r->d()->global = re->global(); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 4bd91bbedd..655e120c16 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -140,8 +140,8 @@ struct RegExpCtor: FunctionObject int lastMatchStart() { return d()->lastMatchStart; } int lastMatchEnd() { return d()->lastMatchEnd; } - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 8f377dd664..c2dfdb1497 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -438,9 +438,9 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH ScopedValue conv(scope, object->get(meth1)); if (FunctionObject *o = conv->as()) { - ScopedValue r(scope, o->call(callData)); - if (r->isPrimitive()) - return r->asReturnedValue(); + o->call(scope, callData); + if (scope.result.isPrimitive()) + return scope.result.asReturnedValue(); } if (engine->hasException) @@ -448,9 +448,9 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH conv = object->get(meth2); if (FunctionObject *o = conv->as()) { - ScopedValue r(scope, o->call(callData)); - if (r->isPrimitive()) - return r->asReturnedValue(); + o->call(scope, callData); + if (scope.result.isPrimitive()) + return scope.result.asReturnedValue(); } return engine->throwTypeError(); @@ -943,10 +943,13 @@ ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint ind return engine->throwTypeError(); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); - if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) - return static_cast(o.getPointer())->evalCall(callData, true); + if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) { + static_cast(o.getPointer())->evalCall(scope, callData, true); + } else { + o->call(scope, callData); + } - return o->call(callData); + return scope.result.asReturnedValue(); } @@ -974,10 +977,12 @@ ReturnedValue Runtime::method_callActivationProperty(ExecutionEngine *engine, in } if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) { - return static_cast(o)->evalCall(callData, true); + static_cast(o)->evalCall(scope, callData, true); + } else { + o->call(scope, callData); } - return o->call(callData); + return scope.result.asReturnedValue(); } ReturnedValue Runtime::method_callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) @@ -988,7 +993,9 @@ ReturnedValue Runtime::method_callQmlScopeObjectProperty(ExecutionEngine *engine QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex); return engine->throwTypeError(error); } - return o->call(callData); + + o->call(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) @@ -1000,7 +1007,8 @@ ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engi return engine->throwTypeError(error); } - return o->call(callData); + o->call(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) @@ -1022,12 +1030,14 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, int nameInde } ScopedFunctionObject o(scope, baseObject->get(name)); - if (!o) { + if (o) { + o->call(scope, callData); + return scope.result.asReturnedValue(); + } else { QString error = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString(), callData->thisObject.toQStringNoThrow()); return engine->throwTypeError(error); } - return o->call(callData); } ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) @@ -1035,10 +1045,13 @@ ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, uint i Lookup *l = engine->current->lookups + index; Value v; v = l->getter(l, engine, callData->thisObject); - if (!v.isObject()) + if (v.isObject()) { + Scope scope(engine); + v.objectValue()->call(scope, callData); + return scope.result.asReturnedValue(); + } else { return engine->throwTypeError(); - - return v.objectValue()->call(callData); + } } ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, const Value &index, CallData *callData) @@ -1055,7 +1068,8 @@ ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, const Value & if (!o) return engine->throwTypeError(); - return o->call(callData); + o->call(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, CallData *callData) @@ -1063,7 +1077,9 @@ ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &fu if (!func.isObject()) return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); - return func.objectValue()->call(callData); + Scope scope(engine); + func.objectValue()->call(scope, callData); + return scope.result.asReturnedValue(); } @@ -1074,10 +1090,12 @@ ReturnedValue Runtime::method_constructGlobalLookup(ExecutionEngine *engine, uin Lookup *l = engine->current->lookups + index; ScopedObject f(scope, l->globalGetter(l, engine)); - if (!f) + if (f) { + f->construct(scope, callData); + return scope.result.asReturnedValue(); + } else { return engine->throwTypeError(); - - return f->construct(callData); + } } @@ -1093,7 +1111,8 @@ ReturnedValue Runtime::method_constructActivationProperty(ExecutionEngine *engin if (!f) return engine->throwTypeError(); - return f->construct(callData); + f->construct(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue Runtime::method_constructValue(ExecutionEngine *engine, const Value &func, CallData *callData) @@ -1102,7 +1121,9 @@ ReturnedValue Runtime::method_constructValue(ExecutionEngine *engine, const Valu if (!f) return engine->throwTypeError(); - return f->construct(callData); + Scope scope(engine); + f->construct(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue Runtime::method_constructProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) @@ -1114,10 +1135,13 @@ ReturnedValue Runtime::method_constructProperty(ExecutionEngine *engine, int nam return Encode::undefined(); ScopedObject f(scope, thisObject->get(name)); - if (!f) + if (f) { + Scope scope(engine); + f->construct(scope, callData); + return scope.result.asReturnedValue(); + } else { return engine->throwTypeError(); - - return f->construct(callData); + } } ReturnedValue Runtime::method_constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) @@ -1125,10 +1149,14 @@ ReturnedValue Runtime::method_constructPropertyLookup(ExecutionEngine *engine, u Lookup *l = engine->current->lookups + index; Value v; v = l->getter(l, engine, callData->thisObject); - if (!v.isObject()) + if (v.isObject()) { + Scope scope(engine); + ScopedValue result(scope); + v.objectValue()->construct(scope, callData); + return scope.result.asReturnedValue(); + } else { return engine->throwTypeError(); - - return v.objectValue()->construct(callData); + } } diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index a6f7f8e0e7..aa811ddba4 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -71,14 +71,18 @@ struct ScopedValue; struct Scope { inline Scope(ExecutionContext *ctx) : engine(ctx->d()->engine) + , mark(engine->jsStackTop) + , result(*engine->jsAlloca(1)) { - mark = engine->jsStackTop; + result = Encode::undefined(); } explicit Scope(ExecutionEngine *e) : engine(e) + , mark(engine->jsStackTop) + , result(*engine->jsAlloca(1)) { - mark = engine->jsStackTop; + result = Encode::undefined(); } ~Scope() { @@ -93,7 +97,7 @@ struct Scope { engine->jsStackTop = mark; } - Value *alloc(int nValues) { + Value *alloc(int nValues) const { return engine->jsAlloca(nValues); } @@ -103,6 +107,7 @@ struct Scope { ExecutionEngine *engine; Value *mark; + Value &result; private: Q_DISABLE_COPY(Scope) @@ -190,59 +195,59 @@ struct Scoped Scoped(const Scope &scope) { - ptr = scope.engine->jsStackTop++; - ptr->setM(0); + ptr = scope.engine->jsAlloca(1); } Scoped(const Scope &scope, const Value &v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(v.as()); } Scoped(const Scope &scope, Heap::Base *o) { Value v; v = o; - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(v.as()); } Scoped(const Scope &scope, const ScopedValue &v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(v.ptr->as()); } Scoped(const Scope &scope, const Value &v, ConvertType) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); ptr->setRawValue(value_convert(scope.engine, v)); } Scoped(const Scope &scope, const Value *v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(v ? v->as() : 0); } Scoped(const Scope &scope, T *t) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(t); } Scoped(const Scope &scope, typename T::Data *t) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); *ptr = t; } Scoped(const Scope &scope, const ReturnedValue &v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(QV4::Value::fromReturnedValue(v).as()); } + Scoped(const Scope &scope, const ReturnedValue &v, ConvertType) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); ptr->setRawValue(value_convert(scope.engine, QV4::Value::fromReturnedValue(v))); } @@ -311,7 +316,7 @@ struct Scoped }; struct ScopedCallData { - ScopedCallData(Scope &scope, int argc = 0) + ScopedCallData(const Scope &scope, int argc = 0) { int size = qMax(argc, (int)QV4::Global::ReservedArgumentCount) + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value); ptr = reinterpret_cast(scope.alloc(size)); @@ -362,19 +367,19 @@ struct ScopedProperty struct ExecutionContextSaver { - ExecutionEngine *engine; + Scope scope; // this makes sure that a reference to context on the JS stack goes out of scope as soon as the context is not used anymore. ExecutionContext *savedContext; - ExecutionContextSaver(Scope &scope) - : engine(scope.engine) + ExecutionContextSaver(const Scope &scope) + : scope(scope.engine) { - savedContext = engine->currentContext; + savedContext = scope.engine->currentContext; } ~ExecutionContextSaver() { - Q_ASSERT(engine->jsStackTop > engine->currentContext); - engine->currentContext = savedContext; - engine->current = savedContext->d(); + Q_ASSERT(scope.engine->jsStackTop > scope.engine->currentContext); + scope.engine->currentContext = savedContext; + scope.engine->current = savedContext->d(); } }; diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 4b847600b4..e8d562cf4c 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -91,7 +91,7 @@ Heap::CompilationUnitHolder::CompilationUnitHolder(CompiledData::CompilationUnit struct QmlBindingWrapper : FunctionObject { V4_OBJECT2(QmlBindingWrapper, FunctionObject) - static ReturnedValue call(const Managed *that, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; } @@ -113,27 +113,28 @@ Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::QmlContext *scope, Function *f) function->compilationUnit->addref(); } -ReturnedValue QmlBindingWrapper::call(const Managed *that, CallData *callData) +void QmlBindingWrapper::call(const Managed *that, Scope &scope, CallData *callData) { const QmlBindingWrapper *This = static_cast(that); ExecutionEngine *v4 = static_cast(that)->engine(); - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); QV4::Function *f = This->function(); - if (!f) - return QV4::Encode::undefined(); + if (!f) { + scope.result = QV4::Encode::undefined(); + return; + } Scoped ctx(scope, v4->currentContext->newCallContext(This, callData)); v4->pushContext(ctx); - ScopedValue result(scope, Q_V4_PROFILE(v4, f)); - - return result->asReturnedValue(); + scope.result = Q_V4_PROFILE(v4, f); } Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit) @@ -255,7 +256,8 @@ ReturnedValue Script::run() ScopedFunctionObject f(valueScope, engine->memoryManager->allocObject(qml, vmFunction)); ScopedCallData callData(valueScope); callData->thisObject = Primitive::undefinedValue(); - return f->call(callData); + f->call(valueScope, callData); + return valueScope.result.asReturnedValue(); } } diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index d7b82218e7..e13388393b 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -74,7 +74,7 @@ struct ContextStateSaver { CompiledData::CompilationUnit *compilationUnit; int lineNumber; - ContextStateSaver(Scope &scope, ExecutionContext *context) + ContextStateSaver(const Scope &scope, ExecutionContext *context) : savedContext(scope.alloc(1)) , strictMode(context->d()->strictMode) , lookups(context->d()->lookups) @@ -83,7 +83,7 @@ struct ContextStateSaver { { savedContext->setM(context->d()); } - ContextStateSaver(Scope &scope, Heap::ExecutionContext *context) + ContextStateSaver(const Scope &scope, Heap::ExecutionContext *context) : savedContext(scope.alloc(1)) , strictMode(context->strictMode) , lookups(context->lookups) diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index fa2409a85c..d3b021ac37 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -411,8 +411,8 @@ public: callData->args[0] = convertElementToValue(this->m_ctx->d()->engine, lhs); callData->args[1] = convertElementToValue(this->m_ctx->d()->engine, rhs); callData->thisObject = this->m_ctx->d()->engine->globalObject; - QV4::ScopedValue result(scope, compare->call(callData)); - return result->toNumber() < 0; + compare->call(scope, callData); + return scope.result.toNumber() < 0; } private: diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index b4f04bbc76..1989f747e9 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -157,28 +157,24 @@ Heap::StringCtor::StringCtor(QV4::ExecutionContext *scope) { } -ReturnedValue StringCtor::construct(const Managed *m, CallData *callData) +void StringCtor::construct(const Managed *m, Scope &scope, CallData *callData) { ExecutionEngine *v4 = static_cast(m)->engine(); - Scope scope(v4); ScopedString value(scope); if (callData->argc) value = callData->args[0].toString(v4); else value = v4->newString(); - return Encode(v4->newStringObject(value)); + scope.result = Encode(v4->newStringObject(value)); } -ReturnedValue StringCtor::call(const Managed *m, CallData *callData) +void StringCtor::call(const Managed *, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = static_cast(m)->engine(); - Scope scope(v4); - ScopedValue value(scope); + ExecutionEngine *v4 = scope.engine; if (callData->argc) - value = callData->args[0].toString(v4); + scope.result = callData->args[0].toString(v4); else - value = v4->newString(); - return value->asReturnedValue(); + scope.result = v4->newString(); } void StringPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -423,7 +419,8 @@ ReturnedValue StringPrototype::method_match(CallContext *context) if (!rx) { ScopedCallData callData(scope, 1); callData->args[0] = regexp; - rx = context->d()->engine->regExpCtor()->construct(callData); + context->d()->engine->regExpCtor()->construct(scope, callData); + rx = scope.result.asReturnedValue(); } if (!rx) @@ -439,8 +436,10 @@ ReturnedValue StringPrototype::method_match(CallContext *context) ScopedCallData callData(scope, 1); callData->thisObject = rx; callData->args[0] = s; - if (!global) - return exec->call(callData); + if (!global) { + exec->call(scope, callData); + return scope.result.asReturnedValue(); + } ScopedString lastIndex(scope, context->d()->engine->newString(QStringLiteral("lastIndex"))); rx->put(lastIndex, ScopedValue(scope, Primitive::fromInt32(0))); @@ -448,14 +447,13 @@ ReturnedValue StringPrototype::method_match(CallContext *context) double previousLastIndex = 0; uint n = 0; - ScopedValue result(scope); ScopedValue matchStr(scope); ScopedValue index(scope); while (1) { - result = exec->call(callData); - if (result->isNull()) + exec->call(scope, callData); + if (scope.result.isNull()) break; - assert(result->isObject()); + assert(scope.result.isObject()); index = rx->get(lastIndex, 0); double thisIndex = index->toInteger(); if (previousLastIndex == thisIndex) { @@ -464,7 +462,7 @@ ReturnedValue StringPrototype::method_match(CallContext *context) } else { previousLastIndex = thisIndex; } - matchStr = result->objectValue()->getIndexed(0); + matchStr = scope.result.objectValue()->getIndexed(0); a->arraySet(n, matchStr); ++n; } @@ -580,7 +578,6 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) } QString result; - ScopedValue replacement(scope); ScopedValue replaceValue(scope, ctx->argument(1)); ScopedFunctionObject searchCallback(scope, replaceValue); if (!!searchCallback) { @@ -605,9 +602,9 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) callData->args[numCaptures] = Primitive::fromUInt32(matchStart); callData->args[numCaptures + 1] = ctx->d()->engine->newString(string); - replacement = searchCallback->call(callData); + searchCallback->call(scope, callData); result += string.midRef(lastEnd, matchStart - lastEnd); - result += replacement->toQString(); + result += scope.result.toQString(); lastEnd = matchEnd; } result += string.midRef(lastEnd); @@ -640,17 +637,17 @@ ReturnedValue StringPrototype::method_search(CallContext *ctx) { Scope scope(ctx); QString string = getThisString(ctx); - ScopedValue regExpValue(scope, ctx->argument(0)); + scope.result = ctx->argument(0); if (scope.engine->hasException) return Encode::undefined(); - Scoped regExp(scope, regExpValue->as()); + Scoped regExp(scope, scope.result.as()); if (!regExp) { ScopedCallData callData(scope, 1); - callData->args[0] = regExpValue; - regExpValue = ctx->d()->engine->regExpCtor()->construct(callData); + callData->args[0] = scope.result; + ctx->d()->engine->regExpCtor()->construct(scope, callData); if (scope.engine->hasException) return Encode::undefined(); - regExp = regExpValue->as(); + regExp = scope.result.as(); Q_ASSERT(regExp); } Scoped re(scope, regExp->value()); diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index fb229d4aff..54425b0909 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -103,8 +103,8 @@ struct StringCtor: FunctionObject { V4_OBJECT2(StringCtor, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *, Scope &scope, CallData *callData); }; struct StringPrototype: StringObject diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index c86f252353..ae5ebcad1b 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -208,30 +208,34 @@ Heap::TypedArrayCtor::TypedArrayCtor(QV4::ExecutionContext *scope, TypedArray::T { } -ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) +void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData) { - Scope scope(static_cast(m)->engine()); Scoped that(scope, static_cast(m)); if (!callData->argc || !callData->args[0].isObject()) { // ECMA 6 22.2.1.1 double l = callData->argc ? callData->args[0].toNumber() : 0; - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } uint len = (uint)l; if (l != len) scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array.")); uint byteLength = len * operations[that->d()->type].bytesPerElement; Scoped buffer(scope, scope.engine->newArrayBuffer(byteLength)); - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } Scoped array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer = buffer->d(); array->d()->byteLength = byteLength; array->d()->byteOffset = 0; - return array.asReturnedValue(); + scope.result = array.asReturnedValue(); + return; } Scoped typedArray(scope, callData->argument(0)); if (!!typedArray) { @@ -243,8 +247,10 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) uint destByteLength = byteLength*destElementSize/srcElementSize; Scoped newBuffer(scope, scope.engine->newArrayBuffer(destByteLength)); - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } Scoped array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer = newBuffer->d(); @@ -269,7 +275,8 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) } } - return array.asReturnedValue(); + scope.result = array.asReturnedValue(); + return; } Scoped buffer(scope, callData->argument(0)); if (!!buffer) { @@ -278,21 +285,29 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) double dbyteOffset = callData->argc > 1 ? callData->args[1].toInteger() : 0; uint byteOffset = (uint)dbyteOffset; uint elementSize = operations[that->d()->type].bytesPerElement; - if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) - return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset")); + if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) { + scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset")); + return; + } uint byteLength; if (callData->argc < 3 || callData->args[2].isUndefined()) { byteLength = buffer->byteLength() - byteOffset; - if (buffer->byteLength() < byteOffset || byteLength % elementSize) - return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); + if (buffer->byteLength() < byteOffset || byteLength % elementSize) { + scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); + return; + } } else { double l = qBound(0., callData->args[2].toInteger(), (double)UINT_MAX); - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } l *= elementSize; - if (buffer->byteLength() - byteOffset < l) - return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); + if (buffer->byteLength() - byteOffset < l) { + scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); + return; + } byteLength = (uint)l; } @@ -300,20 +315,25 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) array->d()->buffer = buffer->d(); array->d()->byteLength = byteLength; array->d()->byteOffset = byteOffset; - return array.asReturnedValue(); + scope.result = array.asReturnedValue(); + return; } // ECMA 6 22.2.1.3 ScopedObject o(scope, callData->argument(0)); uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length()))->toInteger(), (double)UINT_MAX); - if (scope.engine->hasException) - return scope.engine->throwTypeError(); + if (scope.engine->hasException) { + scope.result = scope.engine->throwTypeError(); + return; + } uint elementSize = operations[that->d()->type].bytesPerElement; Scoped newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize)); - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } Scoped array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer = newBuffer->d(); @@ -326,19 +346,21 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) while (idx < l) { val = o->getIndexed(idx); array->d()->type->write(scope.engine, b, 0, val); - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } ++idx; b += elementSize; } - return array.asReturnedValue(); + scope.result = array.asReturnedValue(); } -ReturnedValue TypedArrayCtor::call(const Managed *that, CallData *callData) +void TypedArrayCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return construct(that, callData); + construct(that, scope, callData); } Heap::TypedArray::TypedArray(Type t) @@ -582,5 +604,6 @@ ReturnedValue TypedArrayPrototype::method_subarray(CallContext *ctx) callData->args[0] = buffer; callData->args[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement); callData->args[2] = Encode(newLen); - return constructor->construct(callData); + constructor->construct(scope, callData); + return scope.result.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index 757273e4ed..d96027b96a 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -140,8 +140,8 @@ struct TypedArrayCtor: FunctionObject { V4_OBJECT2(TypedArrayCtor, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index eacb928901..3940176107 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -189,11 +189,12 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) bool isUndefined = false; - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); + QV4::ScopedCallData callData(scope); + QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); bool error = false; if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) - error = !write(pd, result, isUndefined, flags); + error = !write(pd, scope.result, isUndefined, flags); if (!watcher.wasDeleted()) { @@ -377,11 +378,12 @@ QVariant QQmlBinding::evaluate() bool isUndefined = false; QV4::Scope scope(ep->v4engine()); - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); + QV4::ScopedCallData callData(scope); + QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); ep->dereferenceScarceResources(); - return scope.engine->toVariant(result, qMetaTypeId >()); + return scope.engine->toVariant(scope.result, qMetaTypeId >()); } QString QQmlBinding::expressionIdentifier() diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 7d6e1ffa1a..e3c4464584 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -234,7 +234,7 @@ void QQmlBoundSignalExpression::evaluate(void **a) } } - QQmlJavaScriptExpression::evaluate(callData, 0); + QQmlJavaScriptExpression::evaluate(callData, 0, scope); ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } @@ -256,7 +256,7 @@ void QQmlBoundSignalExpression::evaluate(const QList &args) callData->args[ii] = scope.engine->fromVariant(args[ii]); } - QQmlJavaScriptExpression::evaluate(callData, 0); + QQmlJavaScriptExpression::evaluate(callData, 0, scope); ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 52f6837842..854090f20b 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1527,7 +1527,7 @@ void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s) QV4::ScopedCallData callData(scope, 1); callData->thisObject = this; callData->args[0] = QV4::Primitive::fromUInt32(s); - f->call(callData); + f->call(scope, callData); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error); diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp index b3a0baffb4..d10a8c7718 100644 --- a/src/qml/qml/qqmldelayedcallqueue.cpp +++ b/src/qml/qml/qqmldelayedcallqueue.cpp @@ -74,7 +74,7 @@ void QQmlDelayedCallQueue::DelayedFunctionCall::execute(QV4::ExecutionEngine *en const QV4::FunctionObject *callback = m_function.as(); Q_ASSERT(callback); - callback->call(callData); + callback->call(scope, callData); if (scope.engine->hasException) { QQmlError error = scope.engine->catchExceptionAsQmlError(); diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index d48b6ad1d3..6afbd05e3e 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -245,14 +245,15 @@ void QQmlExpression::setExpression(const QString &expression) } // Must be called with a valid handle scope -QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined) +void QQmlExpressionPrivate::v4value(bool *isUndefined, QV4::Scope &scope) { if (!expressionFunctionValid) { createQmlBinding(context(), scopeObject(), expression, url, line); expressionFunctionValid = true; } - return evaluate(isUndefined); + QV4::ScopedCallData callData(scope); + evaluate(callData, isUndefined, scope); } QVariant QQmlExpressionPrivate::value(bool *isUndefined) @@ -271,9 +272,9 @@ QVariant QQmlExpressionPrivate::value(bool *isUndefined) { QV4::Scope scope(QV8Engine::getV4(ep->v8engine())); - QV4::ScopedValue result(scope, v4value(isUndefined)); + v4value(isUndefined, scope); if (!hasError()) - rv = scope.engine->toVariant(result, -1); + rv = scope.engine->toVariant(scope.result, -1); } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index ccc629a3a3..741c25e206 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -76,7 +76,7 @@ public: QVariant value(bool *isUndefined = 0); - QV4::ReturnedValue v4value(bool *isUndefined = 0); + void v4value(bool *isUndefined, QV4::Scope &scope); static inline QQmlExpressionPrivate *get(QQmlExpression *expr); static inline QQmlExpression *get(QQmlExpressionPrivate *expr); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 8ba4b5eba1..eff1a6e039 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -147,16 +147,9 @@ void QQmlJavaScriptExpression::refresh() { } -QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(bool *isUndefined) -{ - QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_context->engine); - QV4::Scope scope(v4); - QV4::ScopedCallData callData(scope); - return evaluate(callData, isUndefined); -} -QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined) +void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined, QV4::Scope &scope) { Q_ASSERT(m_context && m_context->engine); @@ -164,7 +157,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b if (!f || f->isUndefined()) { if (isUndefined) *isUndefined = true; - return QV4::Encode::undefined(); + return; } QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_context->engine); @@ -184,8 +177,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b capture.guards.copyAndClearPrepend(activeGuards); QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); - QV4::Scope scope(v4); - QV4::ScopedValue result(scope, QV4::Primitive::undefinedValue()); + scope.result = QV4::Primitive::undefinedValue(); callData->thisObject = v4->globalObject; if (scopeObject()) { QV4::ScopedValue value(scope, QV4::QObjectWrapper::wrap(v4, scopeObject())); @@ -193,7 +185,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b callData->thisObject = value; } - result = f->as()->call(callData); + f->as()->call(scope, callData); if (scope.hasException()) { if (watcher.wasDeleted()) scope.engine->catchException(); // ignore exception @@ -203,7 +195,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b *isUndefined = true; } else { if (isUndefined) - *isUndefined = result->isUndefined(); + *isUndefined = scope.result.isUndefined(); if (!watcher.wasDeleted() && hasDelayedError()) delayedError()->clearError(); @@ -220,8 +212,6 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b g->Delete(); ep->propertyCapture = lastPropertyCapture; - - return result->asReturnedValue(); } void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) @@ -294,11 +284,12 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n) } } -void QQmlPropertyCapture::registerQmlDependencies(QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction) +void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope) { // Let the caller check and avoid the function call :) Q_ASSERT(compiledFunction->hasQmlDependencies()); + QV4::ExecutionEngine *engine = scope.engine; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine()); if (!ep) return; @@ -306,7 +297,6 @@ void QQmlPropertyCapture::registerQmlDependencies(QV4::ExecutionEngine *engine, if (!capture) return; - QV4::Scope scope(engine); QV4::Scoped context(scope, engine->qmlContext()); QQmlContextData *qmlContext = context->qmlContext(); diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 94479f6c5e..4472a643ff 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -103,8 +103,7 @@ public: virtual QString expressionIdentifier() = 0; virtual void expressionChanged() = 0; - QV4::ReturnedValue evaluate(bool *isUndefined); - QV4::ReturnedValue evaluate(QV4::CallData *callData, bool *isUndefined); + void evaluate(QV4::CallData *callData, bool *isUndefined, QV4::Scope &scope); inline bool notifyOnValueChanged() const; @@ -181,7 +180,7 @@ public: void captureProperty(QQmlNotifier *); void captureProperty(QObject *, int, int); - static void registerQmlDependencies(QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction); + static void registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope); QQmlEngine *engine; QQmlJavaScriptExpression *expression; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 6907b26cd8..a9aec4cded 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -945,15 +945,14 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * for (uint ii = 0; ii < parameterCount; ++ii) callData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]); - QV4::ScopedValue result(scope); - result = function->call(callData); + function->call(scope, callData); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); if (error.isValid()) ep->warning(error); if (a[0]) *(QVariant *)a[0] = QVariant(); } else { - if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(result, 0); + if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(scope.result, 0); } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index e2784103b0..74de30c96c 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1560,7 +1560,7 @@ void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *cont QV4::ScopedCallData callData(scope); callData->thisObject = Encode::undefined(); - callback->call(callData); + callback->call(scope, callData); if (scope.engine->hasException) { QQmlError error = scope.engine->catchExceptionAsQmlError(); @@ -1621,22 +1621,23 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject c->proto->mark(e); FunctionObject::markObjects(that, e); } - static ReturnedValue construct(const Managed *that, QV4::CallData *) + static void construct(const Managed *that, Scope &scope, QV4::CallData *) { - Scope scope(static_cast(that)->engine()); Scoped ctor(scope, that->as()); - if (!ctor) - return scope.engine->throwTypeError(); + if (!ctor) { + scope.result = scope.engine->throwTypeError(); + return; + } QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager()); Scoped w(scope, scope.engine->memoryManager->allocObject(r)); ScopedObject proto(scope, ctor->d()->proto); w->setPrototype(proto); - return w.asReturnedValue(); + scope.result = w.asReturnedValue(); } - static ReturnedValue call(const Managed *, QV4::CallData *) { - return Primitive::undefinedValue().asReturnedValue(); + static void call(const Managed *, Scope &scope, QV4::CallData *) { + scope.result = Primitive::undefinedValue(); } void setupProto(); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 482da31779..a0dff482b1 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1285,11 +1285,10 @@ void QQmlBindingFunction::initBindingLocation() d()->bindingLocation.line = frame.line; } -ReturnedValue QQmlBindingFunction::call(const Managed *that, CallData *callData) +void QQmlBindingFunction::call(const Managed *that, Scope &scope, CallData *callData) { - Scope scope(static_cast(that)->engine()); ScopedFunctionObject function(scope, static_cast(that)->d()->originalFunction); - return function->call(callData); + function->call(scope, callData); } void QQmlBindingFunction::markObjects(Heap::Base *that, ExecutionEngine *e) diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index 5404ab3616..d29983c476 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -188,7 +188,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject void initBindingLocation(); // from caller stack trace - static ReturnedValue call(const Managed *that, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 1585f3eda0..9a1d9e50d1 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -91,17 +91,17 @@ struct DelegateModelGroupFunction : QV4::FunctionObject return scope->engine()->memoryManager->allocObject(scope, flag, code); } - static QV4::ReturnedValue call(const QV4::Managed *that, QV4::CallData *callData) + static void call(const QV4::Managed *that, QV4::Scope &scope, QV4::CallData *callData) { - QV4::ExecutionEngine *v4 = static_cast(that)->engine(); - QV4::Scope scope(v4); QV4::Scoped f(scope, static_cast(that)); QV4::Scoped o(scope, callData->thisObject); - if (!o) - return v4->throwTypeError(QStringLiteral("Not a valid VisualData object")); + if (!o) { + scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); + return; + } QV4::ScopedValue v(scope, callData->argument(0)); - return f->d()->code(o->d()->item, f->d()->flag, v); + scope.result = f->d()->code(o->d()->item, f->d()->flag, v); } }; diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index bc15b2fd9b..10666476fe 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -251,7 +251,8 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() QV4::ScopedCallData callData(scope, 1); callData->args[0] = function; callData->thisObject = global(); - createsend.set(scope.engine, createsendconstructor->call(callData)); + createsendconstructor->call(scope, callData); + createsend.set(scope.engine, scope.result.asReturnedValue()); } // Requires handle and context scope @@ -264,14 +265,13 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(i QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, createsend.value()); - QV4::ScopedValue v(scope); QV4::ScopedCallData callData(scope, 1); callData->args[0] = QV4::Primitive::fromInt32(id); callData->thisObject = global(); - v = f->call(callData); + f->call(scope, callData); if (scope.hasException()) - v = scope.engine->catchException(); - return v->asReturnedValue(); + scope.result = scope.engine->catchException(); + return scope.result.asReturnedValue(); } #ifndef QT_NO_NETWORK @@ -380,7 +380,7 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d callData->thisObject = workerEngine->global(); callData->args[0] = qmlContext->d()->qml; // ### callData->args[1] = value; - f->call(callData); + f->call(scope, callData); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); reportScriptException(script, error); diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index b340d9bf94..9d5ddab155 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -709,7 +709,7 @@ void QQuickCanvasItem::updatePolish() for (auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) { QV4::ScopedFunctionObject f(scope, it.value().value()); callData->args[0] = QV4::Primitive::fromUInt32(QDateTime::currentDateTimeUtc().toTime_t()); - f->call(callData); + f->call(scope, callData); } } else { diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 2f78df1f11..7abbd1938d 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -2312,7 +2312,7 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const QV4::ScopedCallData d(scope, 1); d->args[0] = o; d->thisObject = engine->global(); - function->call(d); + function->call(scope, d); if (scope.engine->hasException) { scope.engine->catchException(); return true; @@ -2338,16 +2338,15 @@ static inline bool evaluate_value(QV8Engine *engine, const QV4::Value &o, if (!function) return false; - QV4::ScopedValue value(scope); QV4::ScopedCallData d(scope, 1); d->args[0] = o; d->thisObject = engine->global(); - value = function->call(d); + function->call(scope, d); if (scope.engine->hasException) { scope.engine->catchException(); return false; } - return QV4::Runtime::method_strictEqual(value, result); + return QV4::Runtime::method_strictEqual(scope.result, result); } static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o, @@ -2371,12 +2370,12 @@ static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o QV4::ScopedCallData d(scope, 1); d->args[0] = o; d->thisObject = engine->global(); - QV4::ScopedValue result(scope, function->call(d)); + function->call(scope, d); if (scope.engine->hasException) { scope.engine->catchException(); return QV4::Encode::undefined(); } - return result->asReturnedValue(); + return scope.result.asReturnedValue(); } #define EVALUATE_ERROR(source) evaluate_error(engine, object, source) diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp index 68aa52ce91..4f79546bcc 100644 --- a/tools/qmljs/qmljs.cpp +++ b/tools/qmljs/qmljs.cpp @@ -68,7 +68,7 @@ struct Print: FunctionObject }; V4_OBJECT(FunctionObject) - static ReturnedValue call(const Managed *, CallData *callData) + static void call(const Managed *, Scope &scope, CallData *callData) { for (int i = 0; i < callData->argc; ++i) { QString s = callData->args[i].toQStringNoThrow(); @@ -77,7 +77,7 @@ struct Print: FunctionObject std::cout << qPrintable(s); } std::cout << std::endl; - return Encode::undefined(); + scope.result = Encode::undefined(); } }; @@ -94,10 +94,10 @@ struct GC: public FunctionObject }; V4_OBJECT(FunctionObject) - static ReturnedValue call(const Managed *m, CallData *) + static void call(const Managed *m, Scope &scope, CallData *) { static_cast(m)->engine()->memoryManager->runGC(); - return Encode::undefined(); + scope.result = Encode::undefined(); } }; -- cgit v1.2.3 From 65c877f40e965c368fe6e7ed730242461b5824b7 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 3 Jun 2016 13:49:51 +0200 Subject: Add cross-backend simple rect, texture, and ninepatch nodes QSGSimpleRectNode is deprecated -> use QSGRectangleNode via QQuickWindow::createRectangleNode() instead. QSGSimpleTextureNode is deprecated -> use QSGImageNode via QQuickWindow::createImageNode() instead. The OpenGL version of the simple rectangle node is switched over to the vertex color material instead of flat, to allow for better batching. Use the same concept for nine patch nodes. The "style" node from Quick Controls 1 is now QSGNinePatchNode in order to provide a proper cross-backend solution which is already necessary due to the software backend, but now generalize it to apply to the accelerated backends with proper materials as well. QC can now simply call createNinePatchNode() without further ado. Also fixes a bug with the D3D12 texture material not enabling blending when needed. When it comes to the internal class names, QSGRectangleNode and QSGImageNode get the Internal prefix in the adaptation layer in order to differentiate from the public API. This involves quite a lot of renaming, but results in a nice and clean public API. Change-Id: Iddf9f9412377843ea6d652bcf25e68d1d74659ea Reviewed-by: Gunnar Sletta --- src/plugins/scenegraph/d3d12/d3d12.pro | 14 +- .../scenegraph/d3d12/qsgd3d12builtinmaterials.cpp | 11 + .../scenegraph/d3d12/qsgd3d12builtinmaterials_p.h | 3 +- src/plugins/scenegraph/d3d12/qsgd3d12context.cpp | 33 +- src/plugins/scenegraph/d3d12/qsgd3d12context_p.h | 8 +- src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp | 123 ---- src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h | 82 --- .../scenegraph/d3d12/qsgd3d12internalimagenode.cpp | 123 ++++ .../scenegraph/d3d12/qsgd3d12internalimagenode_p.h | 82 +++ .../d3d12/qsgd3d12internalrectanglenode.cpp | 72 +++ .../d3d12/qsgd3d12internalrectanglenode_p.h | 74 +++ .../scenegraph/d3d12/qsgd3d12painternode.cpp | 3 +- .../scenegraph/d3d12/qsgd3d12painternode_p.h | 1 + .../scenegraph/d3d12/qsgd3d12publicnodes.cpp | 240 +++++++ .../scenegraph/d3d12/qsgd3d12publicnodes_p.h | 133 ++++ .../scenegraph/d3d12/qsgd3d12rectanglenode.cpp | 72 --- .../scenegraph/d3d12/qsgd3d12rectanglenode_p.h | 74 --- src/quick/items/context2d/qquickcanvasitem.cpp | 6 +- src/quick/items/qquickborderimage.cpp | 4 +- src/quick/items/qquickimage.cpp | 4 +- src/quick/items/qquickmousearea.cpp | 4 +- src/quick/items/qquickmultipointtoucharea.cpp | 4 +- src/quick/items/qquickrectangle.cpp | 4 +- src/quick/items/qquickshadereffectmesh.cpp | 6 +- src/quick/items/qquickshadereffectsource.cpp | 4 +- src/quick/items/qquickshadereffectsource_p.h | 2 +- src/quick/items/qquicktextedit.cpp | 8 +- src/quick/items/qquicktextnode.cpp | 6 +- src/quick/items/qquicktextnode_p.h | 6 +- src/quick/items/qquickwindow.cpp | 39 ++ src/quick/items/qquickwindow.h | 7 + .../adaptations/software/qsgsoftwarecontext.cpp | 39 +- .../adaptations/software/qsgsoftwarecontext_p.h | 8 +- .../adaptations/software/qsgsoftwareimagenode.cpp | 501 --------------- .../adaptations/software/qsgsoftwareimagenode_p.h | 147 ----- .../software/qsgsoftwareinternalimagenode.cpp | 501 +++++++++++++++ .../software/qsgsoftwareinternalimagenode_p.h | 147 +++++ .../software/qsgsoftwareinternalrectanglenode.cpp | 451 ++++++++++++++ .../software/qsgsoftwareinternalrectanglenode_p.h | 103 +++ .../software/qsgsoftwareninepatchnode.cpp | 109 ---- .../software/qsgsoftwareninepatchnode_p.h | 82 --- .../software/qsgsoftwarepublicnodes.cpp | 146 +++++ .../software/qsgsoftwarepublicnodes_p.h | 137 ++++ .../software/qsgsoftwarerectanglenode.cpp | 451 -------------- .../software/qsgsoftwarerectanglenode_p.h | 103 --- .../software/qsgsoftwarerenderablenode.cpp | 38 +- .../software/qsgsoftwarerenderablenode_p.h | 18 +- .../software/qsgsoftwarerenderablenodeupdater.cpp | 29 +- .../software/qsgsoftwarerenderablenodeupdater_p.h | 10 +- .../software/qsgsoftwarerenderlistbuilder.cpp | 23 +- .../software/qsgsoftwarerenderlistbuilder_p.h | 10 +- .../scenegraph/adaptations/software/software.pri | 12 +- src/quick/scenegraph/coreapi/qsggeometry.cpp | 21 + src/quick/scenegraph/coreapi/qsggeometry.h | 1 + src/quick/scenegraph/coreapi/qsgnode.cpp | 2 - src/quick/scenegraph/qsgadaptationlayer_p.h | 32 +- src/quick/scenegraph/qsgbasicimagenode.cpp | 559 ----------------- src/quick/scenegraph/qsgbasicimagenode_p.h | 109 ---- src/quick/scenegraph/qsgbasicinternalimagenode.cpp | 559 +++++++++++++++++ src/quick/scenegraph/qsgbasicinternalimagenode_p.h | 109 ++++ .../scenegraph/qsgbasicinternalrectanglenode.cpp | 687 +++++++++++++++++++++ .../scenegraph/qsgbasicinternalrectanglenode_p.h | 99 +++ src/quick/scenegraph/qsgbasicrectanglenode.cpp | 687 --------------------- src/quick/scenegraph/qsgbasicrectanglenode_p.h | 99 --- src/quick/scenegraph/qsgcontext.cpp | 4 +- src/quick/scenegraph/qsgcontext_p.h | 19 +- src/quick/scenegraph/qsgdefaultcontext.cpp | 43 +- src/quick/scenegraph/qsgdefaultcontext_p.h | 8 +- src/quick/scenegraph/qsgdefaultimagenode.cpp | 226 ------- src/quick/scenegraph/qsgdefaultimagenode_p.h | 97 --- .../scenegraph/qsgdefaultinternalimagenode.cpp | 226 +++++++ .../scenegraph/qsgdefaultinternalimagenode_p.h | 97 +++ .../scenegraph/qsgdefaultinternalrectanglenode.cpp | 159 +++++ .../scenegraph/qsgdefaultinternalrectanglenode_p.h | 90 +++ src/quick/scenegraph/qsgdefaultrectanglenode.cpp | 159 ----- src/quick/scenegraph/qsgdefaultrectanglenode_p.h | 90 --- src/quick/scenegraph/scenegraph.pri | 33 +- src/quick/scenegraph/util/qsgdefaultimagenode.cpp | 190 ++++++ src/quick/scenegraph/util/qsgdefaultimagenode_p.h | 104 ++++ .../scenegraph/util/qsgdefaultninepatchnode.cpp | 136 ++++ .../scenegraph/util/qsgdefaultninepatchnode_p.h | 86 +++ .../scenegraph/util/qsgdefaultrectanglenode.cpp | 95 +++ .../scenegraph/util/qsgdefaultrectanglenode_p.h | 79 +++ src/quick/scenegraph/util/qsgengine.cpp | 41 ++ src/quick/scenegraph/util/qsgengine.h | 6 + src/quick/scenegraph/util/qsgimagenode.cpp | 175 ++++++ src/quick/scenegraph/util/qsgimagenode.h | 85 +++ src/quick/scenegraph/util/qsgninepatchnode.cpp | 77 +++ src/quick/scenegraph/util/qsgninepatchnode.h | 62 ++ src/quick/scenegraph/util/qsgrectanglenode.cpp | 86 +++ src/quick/scenegraph/util/qsgrectanglenode.h | 62 ++ src/quick/scenegraph/util/qsgsimplerectnode.cpp | 4 + src/quick/scenegraph/util/qsgsimpletexturenode.cpp | 5 + tests/auto/quick/nokeywords/tst_nokeywords.cpp | 4 +- 94 files changed, 5859 insertions(+), 3970 deletions(-) delete mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp delete mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h create mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode.cpp create mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_p.h create mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.cpp create mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode_p.h create mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp create mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h delete mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp delete mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h delete mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp delete mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h create mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp create mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h create mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp create mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h delete mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp delete mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h create mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp create mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h delete mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp delete mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h delete mode 100644 src/quick/scenegraph/qsgbasicimagenode.cpp delete mode 100644 src/quick/scenegraph/qsgbasicimagenode_p.h create mode 100644 src/quick/scenegraph/qsgbasicinternalimagenode.cpp create mode 100644 src/quick/scenegraph/qsgbasicinternalimagenode_p.h create mode 100644 src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp create mode 100644 src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h delete mode 100644 src/quick/scenegraph/qsgbasicrectanglenode.cpp delete mode 100644 src/quick/scenegraph/qsgbasicrectanglenode_p.h delete mode 100644 src/quick/scenegraph/qsgdefaultimagenode.cpp delete mode 100644 src/quick/scenegraph/qsgdefaultimagenode_p.h create mode 100644 src/quick/scenegraph/qsgdefaultinternalimagenode.cpp create mode 100644 src/quick/scenegraph/qsgdefaultinternalimagenode_p.h create mode 100644 src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp create mode 100644 src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h delete mode 100644 src/quick/scenegraph/qsgdefaultrectanglenode.cpp delete mode 100644 src/quick/scenegraph/qsgdefaultrectanglenode_p.h create mode 100644 src/quick/scenegraph/util/qsgdefaultimagenode.cpp create mode 100644 src/quick/scenegraph/util/qsgdefaultimagenode_p.h create mode 100644 src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp create mode 100644 src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h create mode 100644 src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp create mode 100644 src/quick/scenegraph/util/qsgdefaultrectanglenode_p.h create mode 100644 src/quick/scenegraph/util/qsgimagenode.cpp create mode 100644 src/quick/scenegraph/util/qsgimagenode.h create mode 100644 src/quick/scenegraph/util/qsgninepatchnode.cpp create mode 100644 src/quick/scenegraph/util/qsgninepatchnode.h create mode 100644 src/quick/scenegraph/util/qsgrectanglenode.cpp create mode 100644 src/quick/scenegraph/util/qsgrectanglenode.h diff --git a/src/plugins/scenegraph/d3d12/d3d12.pro b/src/plugins/scenegraph/d3d12/d3d12.pro index 6ba18acf22..c814c11de0 100644 --- a/src/plugins/scenegraph/d3d12/d3d12.pro +++ b/src/plugins/scenegraph/d3d12/d3d12.pro @@ -15,16 +15,17 @@ SOURCES += \ $$PWD/qsgd3d12renderer.cpp \ $$PWD/qsgd3d12context.cpp \ $$PWD/qsgd3d12rendercontext.cpp \ - $$PWD/qsgd3d12rectanglenode.cpp \ + $$PWD/qsgd3d12internalrectanglenode.cpp \ $$PWD/qsgd3d12material.cpp \ $$PWD/qsgd3d12builtinmaterials.cpp \ $$PWD/qsgd3d12texture.cpp \ - $$PWD/qsgd3d12imagenode.cpp \ + $$PWD/qsgd3d12internalimagenode.cpp \ $$PWD/qsgd3d12glyphnode.cpp \ $$PWD/qsgd3d12glyphcache.cpp \ $$PWD/qsgd3d12layer.cpp \ $$PWD/qsgd3d12shadereffectnode.cpp \ - $$PWD/qsgd3d12painternode.cpp + $$PWD/qsgd3d12painternode.cpp \ + $$PWD/qsgd3d12publicnodes.cpp NO_PCH_SOURCES += \ $$PWD/qsgd3d12engine.cpp @@ -37,16 +38,17 @@ HEADERS += \ $$PWD/qsgd3d12rendercontext_p.h \ $$PWD/qsgd3d12engine_p.h \ $$PWD/qsgd3d12engine_p_p.h \ - $$PWD/qsgd3d12rectanglenode_p.h \ + $$PWD/qsgd3d12internalrectanglenode_p.h \ $$PWD/qsgd3d12material_p.h \ $$PWD/qsgd3d12builtinmaterials_p.h \ $$PWD/qsgd3d12texture_p.h \ - $$PWD/qsgd3d12imagenode_p.h \ + $$PWD/qsgd3d12internalimagenode_p.h \ $$PWD/qsgd3d12glyphnode_p.h \ $$PWD/qsgd3d12glyphcache_p.h \ $$PWD/qsgd3d12layer_p.h \ $$PWD/qsgd3d12shadereffectnode_p.h \ - $$PWD/qsgd3d12painternode_p.h + $$PWD/qsgd3d12painternode_p.h \ + $$PWD/qsgd3d12publicnodes_p.h LIBS += -ldxgi -ld3d12 -ld3dcompiler diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp index ca92062120..3ff8ce6e53 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp @@ -148,6 +148,11 @@ QSGD3D12Material::UpdateResults QSGD3D12VertexColorMaterial::updatePipeline(cons return r; } +QSGD3D12FlatColorMaterial::QSGD3D12FlatColorMaterial() + : m_color(QColor(255, 255, 255)) +{ +} + QSGMaterialType QSGD3D12FlatColorMaterial::mtype; QSGMaterialType *QSGD3D12FlatColorMaterial::type() const @@ -353,6 +358,12 @@ QSGD3D12Material::UpdateResults QSGD3D12TextureMaterial::updatePipeline(const QS return r; } +void QSGD3D12TextureMaterial::setTexture(QSGTexture *texture) +{ + m_texture = texture; + setFlag(Blending, m_texture ? m_texture->hasAlphaChannel() : false); +} + QSGD3D12SmoothTextureMaterial::QSGD3D12SmoothTextureMaterial() { setFlag(RequiresFullMatrixExceptTranslate, true); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h index 34ae73d2d6..8e488f8cd1 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h @@ -80,6 +80,7 @@ private: class QSGD3D12FlatColorMaterial : public QSGD3D12Material { public: + QSGD3D12FlatColorMaterial(); QSGMaterialType *type() const override; int compare(const QSGMaterial *other) const override; @@ -129,7 +130,7 @@ public: ExtraState *extraState, quint8 *constantBuffer) override; - void setTexture(QSGTexture *texture) { m_texture = texture; } + void setTexture(QSGTexture *texture); QSGTexture *texture() const { return m_texture; } void setMipmapFiltering(QSGTexture::Filtering filter) { m_mipmap_filtering = filter; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp index ce44bc7a38..07c9287f80 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp @@ -39,12 +39,13 @@ #include "qsgd3d12context_p.h" #include "qsgd3d12rendercontext_p.h" -#include "qsgd3d12rectanglenode_p.h" -#include "qsgd3d12imagenode_p.h" +#include "qsgd3d12internalrectanglenode_p.h" +#include "qsgd3d12internalimagenode_p.h" #include "qsgd3d12glyphnode_p.h" #include "qsgd3d12layer_p.h" #include "qsgd3d12shadereffectnode_p.h" #include "qsgd3d12painternode_p.h" +#include "qsgd3d12publicnodes_p.h" QT_BEGIN_NAMESPACE @@ -53,14 +54,14 @@ QSGRenderContext *QSGD3D12Context::createRenderContext() return new QSGD3D12RenderContext(this); } -QSGRectangleNode *QSGD3D12Context::createRectangleNode() +QSGInternalRectangleNode *QSGD3D12Context::createInternalRectangleNode() { - return new QSGD3D12RectangleNode; + return new QSGD3D12InternalRectangleNode; } -QSGImageNode *QSGD3D12Context::createImageNode() +QSGInternalImageNode *QSGD3D12Context::createInternalImageNode() { - return new QSGD3D12ImageNode; + return new QSGD3D12InternalImageNode; } QSGPainterNode *QSGD3D12Context::createPainterNode(QQuickPaintedItem *item) @@ -77,11 +78,6 @@ QSGGlyphNode *QSGD3D12Context::createGlyphNode(QSGRenderContext *renderContext, return new QSGD3D12GlyphNode(rc); } -QSGNinePatchNode *QSGD3D12Context::createNinePatchNode() -{ - return nullptr; -} - QSGLayer *QSGD3D12Context::createLayer(QSGRenderContext *renderContext) { QSGD3D12RenderContext *rc = static_cast(renderContext); @@ -121,4 +117,19 @@ QSGRendererInterface *QSGD3D12Context::rendererInterface(QSGRenderContext *rende return rc->engine(); } +QSGRectangleNode *QSGD3D12Context::createRectangleNode() +{ + return new QSGD3D12RectangleNode; +} + +QSGImageNode *QSGD3D12Context::createImageNode() +{ + return new QSGD3D12ImageNode; +} + +QSGNinePatchNode *QSGD3D12Context::createNinePatchNode() +{ + return new QSGD3D12NinePatchNode; +} + QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h index 0564c4edac..f4d9429c7b 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h @@ -61,11 +61,10 @@ public: QSGD3D12Context(QObject *parent = 0) : QSGContext(parent) { } QSGRenderContext *createRenderContext() override; - QSGRectangleNode *createRectangleNode() override; - QSGImageNode *createImageNode() override; + QSGInternalRectangleNode *createInternalRectangleNode() override; + QSGInternalImageNode *createInternalImageNode() override; QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override; QSGGlyphNode *createGlyphNode(QSGRenderContext *renderContext, bool preferNativeGlyphNode) override; - QSGNinePatchNode *createNinePatchNode() override; QSGLayer *createLayer(QSGRenderContext *renderContext) override; QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager() override; QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext, @@ -73,6 +72,9 @@ public: QSize minimumFBOSize() const override; QSurfaceFormat defaultSurfaceFormat() const override; QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override; + QSGRectangleNode *createRectangleNode() override; + QSGImageNode *createImageNode() override; + QSGNinePatchNode *createNinePatchNode() override; }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp deleted file mode 100644 index 9bb360bc5e..0000000000 --- a/src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qsgd3d12imagenode_p.h" - -QT_BEGIN_NAMESPACE - -QSGD3D12ImageNode::QSGD3D12ImageNode() -{ - setMaterial(&m_material); -} - -void QSGD3D12ImageNode::setFiltering(QSGTexture::Filtering filtering) -{ - if (m_material.filtering() == filtering) - return; - - m_material.setFiltering(filtering); - m_smoothMaterial.setFiltering(filtering); - markDirty(DirtyMaterial); -} - -void QSGD3D12ImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) -{ - if (m_material.mipmapFiltering() == filtering) - return; - - m_material.setMipmapFiltering(filtering); - m_smoothMaterial.setMipmapFiltering(filtering); - markDirty(DirtyMaterial); -} - -void QSGD3D12ImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) -{ - if (m_material.verticalWrapMode() == wrapMode) - return; - - m_material.setVerticalWrapMode(wrapMode); - m_smoothMaterial.setVerticalWrapMode(wrapMode); - markDirty(DirtyMaterial); -} - -void QSGD3D12ImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) -{ - if (m_material.horizontalWrapMode() == wrapMode) - return; - - m_material.setHorizontalWrapMode(wrapMode); - m_smoothMaterial.setHorizontalWrapMode(wrapMode); - markDirty(DirtyMaterial); -} - -void QSGD3D12ImageNode::updateMaterialAntialiasing() -{ - if (m_antialiasing) - setMaterial(&m_smoothMaterial); - else - setMaterial(&m_material); -} - -void QSGD3D12ImageNode::setMaterialTexture(QSGTexture *texture) -{ - m_material.setTexture(texture); - m_smoothMaterial.setTexture(texture); -} - -QSGTexture *QSGD3D12ImageNode::materialTexture() const -{ - return m_material.texture(); -} - -bool QSGD3D12ImageNode::updateMaterialBlending() -{ - const bool alpha = m_material.flags() & QSGMaterial::Blending; - if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) { - m_material.setFlag(QSGMaterial::Blending, !alpha); - return true; - } - return false; -} - -bool QSGD3D12ImageNode::supportsWrap(const QSize &) const -{ - return true; -} - -QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h deleted file mode 100644 index ef4b38884a..0000000000 --- a/src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#ifndef QSGD3D12IMAGENODE_P_H -#define QSGD3D12IMAGENODE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include "qsgd3d12builtinmaterials_p.h" - -QT_BEGIN_NAMESPACE - -class QSGD3D12ImageNode : public QSGBasicImageNode -{ -public: - QSGD3D12ImageNode(); - - void setMipmapFiltering(QSGTexture::Filtering filtering) override; - void setFiltering(QSGTexture::Filtering filtering) override; - void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override; - void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override; - - void updateMaterialAntialiasing() override; - void setMaterialTexture(QSGTexture *texture) override; - QSGTexture *materialTexture() const override; - bool updateMaterialBlending() override; - bool supportsWrap(const QSize &size) const override; - -private: - QSGD3D12TextureMaterial m_material; - QSGD3D12SmoothTextureMaterial m_smoothMaterial; -}; - -QT_END_NAMESPACE - -#endif // QSGD3D12IMAGENODE_P_H diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode.cpp new file mode 100644 index 0000000000..aa163cacbf --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgd3d12internalimagenode_p.h" + +QT_BEGIN_NAMESPACE + +QSGD3D12InternalImageNode::QSGD3D12InternalImageNode() +{ + setMaterial(&m_material); +} + +void QSGD3D12InternalImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.filtering() == filtering) + return; + + m_material.setFiltering(filtering); + m_smoothMaterial.setFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGD3D12InternalImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.mipmapFiltering() == filtering) + return; + + m_material.setMipmapFiltering(filtering); + m_smoothMaterial.setMipmapFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGD3D12InternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) +{ + if (m_material.verticalWrapMode() == wrapMode) + return; + + m_material.setVerticalWrapMode(wrapMode); + m_smoothMaterial.setVerticalWrapMode(wrapMode); + markDirty(DirtyMaterial); +} + +void QSGD3D12InternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) +{ + if (m_material.horizontalWrapMode() == wrapMode) + return; + + m_material.setHorizontalWrapMode(wrapMode); + m_smoothMaterial.setHorizontalWrapMode(wrapMode); + markDirty(DirtyMaterial); +} + +void QSGD3D12InternalImageNode::updateMaterialAntialiasing() +{ + if (m_antialiasing) + setMaterial(&m_smoothMaterial); + else + setMaterial(&m_material); +} + +void QSGD3D12InternalImageNode::setMaterialTexture(QSGTexture *texture) +{ + m_material.setTexture(texture); + m_smoothMaterial.setTexture(texture); +} + +QSGTexture *QSGD3D12InternalImageNode::materialTexture() const +{ + return m_material.texture(); +} + +bool QSGD3D12InternalImageNode::updateMaterialBlending() +{ + const bool alpha = m_material.flags() & QSGMaterial::Blending; + if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) { + m_material.setFlag(QSGMaterial::Blending, !alpha); + return true; + } + return false; +} + +bool QSGD3D12InternalImageNode::supportsWrap(const QSize &) const +{ + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_p.h new file mode 100644 index 0000000000..26284740ee --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGD3D12INTERNALIMAGENODE_P_H +#define QSGD3D12INTERNALIMAGENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include "qsgd3d12builtinmaterials_p.h" + +QT_BEGIN_NAMESPACE + +class QSGD3D12InternalImageNode : public QSGBasicInternalImageNode +{ +public: + QSGD3D12InternalImageNode(); + + void setMipmapFiltering(QSGTexture::Filtering filtering) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override; + void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override; + + void updateMaterialAntialiasing() override; + void setMaterialTexture(QSGTexture *texture) override; + QSGTexture *materialTexture() const override; + bool updateMaterialBlending() override; + bool supportsWrap(const QSize &size) const override; + +private: + QSGD3D12TextureMaterial m_material; + QSGD3D12SmoothTextureMaterial m_smoothMaterial; +}; + +QT_END_NAMESPACE + +#endif // QSGD3D12INTERNALIMAGENODE_P_H diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.cpp new file mode 100644 index 0000000000..2d9c5b55d1 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgd3d12internalrectanglenode_p.h" + +QT_BEGIN_NAMESPACE + +QSGD3D12InternalRectangleNode::QSGD3D12InternalRectangleNode() +{ + setMaterial(&m_material); +} + +void QSGD3D12InternalRectangleNode::updateMaterialAntialiasing() +{ + if (m_antialiasing) + setMaterial(&m_smoothMaterial); + else + setMaterial(&m_material); +} + +void QSGD3D12InternalRectangleNode::updateMaterialBlending(QSGNode::DirtyState *state) +{ + // smoothed material is always blended, so no change in material state + if (material() == &m_material) { + bool wasBlending = (m_material.flags() & QSGMaterial::Blending); + bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque) + || (m_color.alpha() < 255 && m_color.alpha() != 0) + || (m_pen_width > 0 && m_border_color.alpha() < 255); + if (wasBlending != isBlending) { + m_material.setFlag(QSGMaterial::Blending, isBlending); + *state |= QSGNode::DirtyMaterial; + } + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode_p.h new file mode 100644 index 0000000000..2fc3c69285 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGD3D12INTERNALRECTANGLENODE_P_H +#define QSGD3D12INTERNALRECTANGLENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include "qsgd3d12builtinmaterials_p.h" + +QT_BEGIN_NAMESPACE + +class QSGD3D12InternalRectangleNode : public QSGBasicInternalRectangleNode +{ +public: + QSGD3D12InternalRectangleNode(); + +private: + void updateMaterialAntialiasing() override; + void updateMaterialBlending(QSGNode::DirtyState *state) override; + + QSGD3D12VertexColorMaterial m_material; + QSGD3D12SmoothColorMaterial m_smoothMaterial; +}; + +QT_END_NAMESPACE + +#endif // QSGD3D12INTERNALRECTANGLENODE_P_H diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp index 7837c3a2d3..b22c42f2e5 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp @@ -84,10 +84,9 @@ QSGD3D12PainterNode::QSGD3D12PainterNode(QQuickPaintedItem *item) m_dirtyGeometry(false), m_dirtyContents(false) { - m_material.setFlag(QSGMaterial::Blending); + setGeometry(&m_geometry); m_material.setTexture(m_texture); setMaterial(&m_material); - setGeometry(&m_geometry); } QSGD3D12PainterNode::~QSGD3D12PainterNode() diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h index 67246cc3cd..7f4842b3a6 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h @@ -65,6 +65,7 @@ public: QSGD3D12PainterTexture(QSGD3D12Engine *engine); void bind() override; + bool hasAlphaChannel() const override { return true; } QImage *image() { return &m_image; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp new file mode 100644 index 0000000000..de9869aee8 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgd3d12publicnodes_p.h" + +// for rebuildGeometry +#include +#include + +QT_BEGIN_NAMESPACE + +QSGD3D12RectangleNode::QSGD3D12RectangleNode() + : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) +{ + QSGGeometry::updateRectGeometry(&m_geometry, QRectF()); + setMaterial(&m_material); + setGeometry(&m_geometry); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("rectangle")); +#endif +} + +void QSGD3D12RectangleNode::setRect(const QRectF &rect) +{ + QSGGeometry::updateRectGeometry(&m_geometry, rect); + markDirty(QSGNode::DirtyGeometry); +} + +QRectF QSGD3D12RectangleNode::rect() const +{ + const QSGGeometry::Point2D *pts = m_geometry.vertexDataAsPoint2D(); + return QRectF(pts[0].x, + pts[0].y, + pts[3].x - pts[0].x, + pts[3].y - pts[0].y); +} + +void QSGD3D12RectangleNode::setColor(const QColor &color) +{ + if (color != m_material.color()) { + m_material.setColor(color); + markDirty(QSGNode::DirtyMaterial); + } +} + +QColor QSGD3D12RectangleNode::color() const +{ + return m_material.color(); +} + +QSGD3D12ImageNode::QSGD3D12ImageNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4), + m_texCoordMode(QSGD3D12ImageNode::NoTransform), + m_isAtlasTexture(false), + m_ownsTexture(false) +{ + setGeometry(&m_geometry); + setMaterial(&m_material); + m_material.setMipmapFiltering(QSGTexture::None); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("image")); +#endif +} + +QSGD3D12ImageNode::~QSGD3D12ImageNode() +{ + if (m_ownsTexture) + delete m_material.texture(); +} + +void QSGD3D12ImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.filtering() == filtering) + return; + + m_material.setFiltering(filtering); + markDirty(DirtyMaterial); +} + +QSGTexture::Filtering QSGD3D12ImageNode::filtering() const +{ + return m_material.filtering(); +} + +void QSGD3D12ImageNode::setRect(const QRectF &r) +{ + if (m_rect == r) + return; + + m_rect = r; + QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyGeometry); +} + +QRectF QSGD3D12ImageNode::rect() const +{ + return m_rect; +} + +void QSGD3D12ImageNode::setSourceRect(const QRectF &r) +{ + if (m_sourceRect == r) + return; + + m_sourceRect = r; + QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyGeometry); +} + +QRectF QSGD3D12ImageNode::sourceRect() const +{ + return m_sourceRect; +} + +void QSGD3D12ImageNode::setTexture(QSGTexture *texture) +{ + Q_ASSERT(texture); + + if (m_ownsTexture) + delete m_material.texture(); + + m_material.setTexture(texture); + QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture, m_rect, m_sourceRect, m_texCoordMode); + + DirtyState dirty = DirtyMaterial; + const bool wasAtlas = m_isAtlasTexture; + m_isAtlasTexture = texture->isAtlasTexture(); + if (wasAtlas || m_isAtlasTexture) + dirty |= DirtyGeometry; + + markDirty(dirty); +} + +QSGTexture *QSGD3D12ImageNode::texture() const +{ + return m_material.texture(); +} + +void QSGD3D12ImageNode::setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) +{ + if (m_texCoordMode == mode) + return; + + m_texCoordMode = mode; + QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyMaterial); +} + +QSGD3D12ImageNode::TextureCoordinatesTransformMode QSGD3D12ImageNode::textureCoordinatesTransform() const +{ + return m_texCoordMode; +} + +void QSGD3D12ImageNode::setOwnsTexture(bool owns) +{ + m_ownsTexture = owns; +} + +bool QSGD3D12ImageNode::ownsTexture() const +{ + return m_ownsTexture; +} + +QSGD3D12NinePatchNode::QSGD3D12NinePatchNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) +{ + m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip); + setGeometry(&m_geometry); + setMaterial(&m_material); +} + +QSGD3D12NinePatchNode::~QSGD3D12NinePatchNode() +{ + delete m_material.texture(); +} + +void QSGD3D12NinePatchNode::setTexture(QSGTexture *texture) +{ + delete m_material.texture(); + m_material.setTexture(texture); +} + +void QSGD3D12NinePatchNode::setBounds(const QRectF &bounds) +{ + m_bounds = bounds; +} + +void QSGD3D12NinePatchNode::setDevicePixelRatio(qreal ratio) +{ + m_devicePixelRatio = ratio; +} + +void QSGD3D12NinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) +{ + m_padding = QVector4D(left, top, right, bottom); +} + +void QSGD3D12NinePatchNode::update() +{ + QSGDefaultNinePatchNode::rebuildGeometry(m_material.texture(), &m_geometry, m_padding, m_bounds, m_devicePixelRatio); + markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial); +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h new file mode 100644 index 0000000000..14e34bc0ac --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGD3D12PUBLICNODES_P_H +#define QSGD3D12PUBLICNODES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include "qsgd3d12builtinmaterials_p.h" + +QT_BEGIN_NAMESPACE + +class QSGD3D12RectangleNode : public QSGRectangleNode +{ +public: + QSGD3D12RectangleNode(); + + void setRect(const QRectF &rect) override; + QRectF rect() const override; + + void setColor(const QColor &color) override; + QColor color() const override; + +private: + QSGGeometry m_geometry; + QSGD3D12FlatColorMaterial m_material; +}; + +class QSGD3D12ImageNode : public QSGImageNode +{ +public: + QSGD3D12ImageNode(); + ~QSGD3D12ImageNode(); + + void setRect(const QRectF &rect) override; + QRectF rect() const override; + + void setSourceRect(const QRectF &r) override; + QRectF sourceRect() const override; + + void setTexture(QSGTexture *texture) override; + QSGTexture *texture() const override; + + void setFiltering(QSGTexture::Filtering filtering) override; + QSGTexture::Filtering filtering() const override; + + void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) override; + TextureCoordinatesTransformMode textureCoordinatesTransform() const override; + + void setOwnsTexture(bool owns) override; + bool ownsTexture() const override; + +private: + QSGGeometry m_geometry; + QSGD3D12TextureMaterial m_material; + QRectF m_rect; + QRectF m_sourceRect; + TextureCoordinatesTransformMode m_texCoordMode; + uint m_isAtlasTexture : 1; + uint m_ownsTexture : 1; +}; + +class QSGD3D12NinePatchNode : public QSGNinePatchNode +{ +public: + QSGD3D12NinePatchNode(); + ~QSGD3D12NinePatchNode(); + + void setTexture(QSGTexture *texture) override; + void setBounds(const QRectF &bounds) override; + void setDevicePixelRatio(qreal ratio) override; + void setPadding(qreal left, qreal top, qreal right, qreal bottom) override; + void update() override; + +private: + QSGGeometry m_geometry; + QSGD3D12TextureMaterial m_material; + QRectF m_bounds; + qreal m_devicePixelRatio; + QVector4D m_padding; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp deleted file mode 100644 index 7548f5cbc0..0000000000 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qsgd3d12rectanglenode_p.h" - -QT_BEGIN_NAMESPACE - -QSGD3D12RectangleNode::QSGD3D12RectangleNode() -{ - setMaterial(&m_material); -} - -void QSGD3D12RectangleNode::updateMaterialAntialiasing() -{ - if (m_antialiasing) - setMaterial(&m_smoothMaterial); - else - setMaterial(&m_material); -} - -void QSGD3D12RectangleNode::updateMaterialBlending(QSGNode::DirtyState *state) -{ - // smoothed material is always blended, so no change in material state - if (material() == &m_material) { - bool wasBlending = (m_material.flags() & QSGMaterial::Blending); - bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque) - || (m_color.alpha() < 255 && m_color.alpha() != 0) - || (m_pen_width > 0 && m_border_color.alpha() < 255); - if (wasBlending != isBlending) { - m_material.setFlag(QSGMaterial::Blending, isBlending); - *state |= QSGNode::DirtyMaterial; - } - } -} - -QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h deleted file mode 100644 index 95f734bc4c..0000000000 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#ifndef QSGD3D12RECTANGLENODE_P_H -#define QSGD3D12RECTANGLENODE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include "qsgd3d12builtinmaterials_p.h" - -QT_BEGIN_NAMESPACE - -class QSGD3D12RectangleNode : public QSGBasicRectangleNode -{ -public: - QSGD3D12RectangleNode(); - -private: - void updateMaterialAntialiasing() override; - void updateMaterialBlending(QSGNode::DirtyState *state) override; - - QSGD3D12VertexColorMaterial m_material; - QSGD3D12SmoothColorMaterial m_smoothMaterial; -}; - -QT_END_NAMESPACE - -#endif // QSGD3D12RECTANGLENODE_P_H diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index 9d5ddab155..8d91df5a74 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -174,7 +174,7 @@ public: QUrl baseUrl; QMap animationCallbacks; mutable QQuickCanvasTextureProvider *textureProvider; - QSGImageNode *node; + QSGInternalImageNode *node; QSGTexture *nodeTexture; }; @@ -743,9 +743,9 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData return 0; } - QSGImageNode *node = static_cast(oldNode); + QSGInternalImageNode *node = static_cast(oldNode); if (!node) { - node = QQuickWindowPrivate::get(window())->context->sceneGraphContext()->createImageNode(); + node = QQuickWindowPrivate::get(window())->context->sceneGraphContext()->createInternalImageNode(); d->node = node; } diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp index b3a35e6219..ca5ad8dbbd 100644 --- a/src/quick/items/qquickborderimage.cpp +++ b/src/quick/items/qquickborderimage.cpp @@ -631,12 +631,12 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat return 0; } - QSGImageNode *node = static_cast(oldNode); + QSGInternalImageNode *node = static_cast(oldNode); bool updatePixmap = d->pixmapChanged; d->pixmapChanged = false; if (!node) { - node = d->sceneGraphContext()->createImageNode(); + node = d->sceneGraphContext()->createInternalImageNode(); updatePixmap = true; } diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp index a168b43fd1..e36c070248 100644 --- a/src/quick/items/qquickimage.cpp +++ b/src/quick/items/qquickimage.cpp @@ -614,10 +614,10 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) return 0; } - QSGImageNode *node = static_cast(oldNode); + QSGInternalImageNode *node = static_cast(oldNode); if (!node) { d->pixmapChanged = true; - node = d->sceneGraphContext()->createImageNode(); + node = d->sceneGraphContext()->createInternalImageNode(); } QRectF targetRect; diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 297a57e672..33cc6c9a63 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -1354,8 +1354,8 @@ QSGNode *QQuickMouseArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData if (!qmlVisualTouchDebugging()) return 0; - QSGRectangleNode *rectangle = static_cast(oldNode); - if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode(); + QSGInternalRectangleNode *rectangle = static_cast(oldNode); + if (!rectangle) rectangle = d->sceneGraphContext()->createInternalRectangleNode(); rectangle->setRect(QRectF(0, 0, width(), height())); rectangle->setColor(QColor(255, 0, 0, 50)); diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index 9d8e7aedd4..ac5598767a 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -940,8 +940,8 @@ QSGNode *QQuickMultiPointTouchArea::updatePaintNode(QSGNode *oldNode, UpdatePain if (!qmlVisualTouchDebugging()) return 0; - QSGRectangleNode *rectangle = static_cast(oldNode); - if (!rectangle) rectangle = QQuickItemPrivate::get(this)->sceneGraphContext()->createRectangleNode(); + QSGInternalRectangleNode *rectangle = static_cast(oldNode); + if (!rectangle) rectangle = QQuickItemPrivate::get(this)->sceneGraphContext()->createInternalRectangleNode(); rectangle->setRect(QRectF(0, 0, width(), height())); rectangle->setColor(QColor(255, 0, 0, 50)); diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp index b8c680433e..223976c32f 100644 --- a/src/quick/items/qquickrectangle.cpp +++ b/src/quick/items/qquickrectangle.cpp @@ -482,8 +482,8 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData return 0; } - QSGRectangleNode *rectangle = static_cast(oldNode); - if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode(); + QSGInternalRectangleNode *rectangle = static_cast(oldNode); + if (!rectangle) rectangle = d->sceneGraphContext()->createInternalRectangleNode(); rectangle->setRect(QRectF(0, 0, width(), height())); rectangle->setColor(d->color); diff --git a/src/quick/items/qquickshadereffectmesh.cpp b/src/quick/items/qquickshadereffectmesh.cpp index f5cc19c877..8616bf8022 100644 --- a/src/quick/items/qquickshadereffectmesh.cpp +++ b/src/quick/items/qquickshadereffectmesh.cpp @@ -42,7 +42,7 @@ #include "qquickshadereffect_p.h" #include "qquickscalegrid_p_p.h" #include "qquickborderimage_p_p.h" -#include +#include QT_BEGIN_NAMESPACE @@ -335,8 +335,8 @@ QSGGeometry *QQuickBorderImageMesh::updateGeometry(QSGGeometry *geometry, int at innerSourceRect.width() * sourceRect.width(), innerSourceRect.height() * sourceRect.height()); - geometry = QSGBasicImageNode::updateGeometry(targetRect, innerTargetRect, sourceRect, - modifiedInnerSourceRect, subSourceRect, geometry); + geometry = QSGBasicInternalImageNode::updateGeometry(targetRect, innerTargetRect, sourceRect, + modifiedInnerSourceRect, subSourceRect, geometry); return geometry; } diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp index 338e4dc3a7..d74dd99fe8 100644 --- a/src/quick/items/qquickshadereffectsource.cpp +++ b/src/quick/items/qquickshadereffectsource.cpp @@ -708,9 +708,9 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint return 0; } - QSGImageNode *node = static_cast(oldNode); + QSGInternalImageNode *node = static_cast(oldNode); if (!node) { - node = d->sceneGraphContext()->createImageNode(); + node = d->sceneGraphContext()->createInternalImageNode(); node->setFlag(QSGNode::UsePreprocess); node->setTexture(m_texture); QQuickShaderSourceAttachedNode *attached = new QQuickShaderSourceAttachedNode; diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h index 680ba85aa1..de62c21488 100644 --- a/src/quick/items/qquickshadereffectsource_p.h +++ b/src/quick/items/qquickshadereffectsource_p.h @@ -55,7 +55,7 @@ #include #include #include -#include +#include #include #include "qpointer.h" diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 36eb5d3cde..eb568c48d4 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -153,7 +153,7 @@ namespace { newNode->setFlag(QSGNode::OwnedByParent); } - void resetCursorNode(QSGRectangleNode* newNode) + void resetCursorNode(QSGInternalRectangleNode* newNode) { if (cursorNode) removeChildNode(cursorNode); @@ -165,7 +165,7 @@ namespace { } } - QSGRectangleNode *cursorNode; + QSGInternalRectangleNode *cursorNode; QQuickTextNode* frameDecorationsNode; }; @@ -2125,9 +2125,9 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * } if (d->cursorComponent == 0) { - QSGRectangleNode* cursor = 0; + QSGInternalRectangleNode* cursor = 0; if (!isReadOnly() && d->cursorVisible && d->control->cursorOn()) - cursor = d->sceneGraphContext()->createRectangleNode(d->control->cursorRect(), d->color); + cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color); rootNode->resetCursorNode(cursor); } diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp index 7ee01b5398..0e9d1e3951 100644 --- a/src/quick/items/qquicktextnode.cpp +++ b/src/quick/items/qquicktextnode.cpp @@ -136,7 +136,7 @@ void QQuickTextNode::setCursor(const QRectF &rect, const QColor &color) delete m_cursorNode; QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext(); - m_cursorNode = sg->sceneGraphContext()->createRectangleNode(rect, color); + m_cursorNode = sg->sceneGraphContext()->createInternalRectangleNode(rect, color); appendChildNode(m_cursorNode); } @@ -151,14 +151,14 @@ void QQuickTextNode::clearCursor() void QQuickTextNode::addRectangleNode(const QRectF &rect, const QColor &color) { QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext(); - appendChildNode(sg->sceneGraphContext()->createRectangleNode(rect, color)); + appendChildNode(sg->sceneGraphContext()->createInternalRectangleNode(rect, color)); } void QQuickTextNode::addImage(const QRectF &rect, const QImage &image) { QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext(); - QSGImageNode *node = sg->sceneGraphContext()->createImageNode(); + QSGInternalImageNode *node = sg->sceneGraphContext()->createInternalImageNode(); QSGTexture *texture = sg->createTexture(image); m_textures.append(texture); node->setTargetRect(rect); diff --git a/src/quick/items/qquicktextnode_p.h b/src/quick/items/qquicktextnode_p.h index 0006cf1156..fb30956fea 100644 --- a/src/quick/items/qquicktextnode_p.h +++ b/src/quick/items/qquicktextnode_p.h @@ -68,7 +68,7 @@ class QColor; class QTextDocument; class QSGContext; class QRawFont; -class QSGRectangleNode; +class QSGInternalRectangleNode; class QSGClipNode; class QSGTexture; @@ -106,7 +106,7 @@ public: void setCursor(const QRectF &rect, const QColor &color); void clearCursor(); - QSGRectangleNode *cursorNode() const { return m_cursorNode; } + QSGInternalRectangleNode *cursorNode() const { return m_cursorNode; } QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color, QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(), @@ -118,7 +118,7 @@ public: void setUseNativeRenderer(bool on) { m_useNativeRenderer = on; } private: - QSGRectangleNode *m_cursorNode; + QSGInternalRectangleNode *m_cursorNode; QList m_textures; QQuickItem *m_ownerElement; bool m_useNativeRenderer; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 98e450abdb..10b769b5b0 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -4513,6 +4513,45 @@ void QQuickWindow::setSceneGraphBackend(const QString &backend) QSGContext::setBackend(backend); } +/*! + Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null. + + This is cross-backend alternative to constructing a QSGSimpleRectNode directly. + + \since 5.8 + \sa QSGRectangleNode + */ +QSGRectangleNode *QQuickWindow::createRectangleNode() const +{ + Q_D(const QQuickWindow); + return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createRectangleNode() : nullptr; +} + +/*! + Creates a simple image node. When the scenegraph is not initialized, the return value is null. + + This is cross-backend alternative to constructing a QSGSimpleTextureNode directly. + + \since 5.8 + \sa QSGImageNode + */ +QSGImageNode *QQuickWindow::createImageNode() const +{ + Q_D(const QQuickWindow); + return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createImageNode() : nullptr; +} + +/*! + Creates a nine patch node. When the scenegraph is not initialized, the return value is null. + + \since 5.8 + */ +QSGNinePatchNode *QQuickWindow::createNinePatchNode() const +{ + Q_D(const QQuickWindow); + return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createNinePatchNode() : nullptr; +} + #include "moc_qquickwindow.cpp" QT_END_NAMESPACE diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index c741772253..cfadadec2d 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -61,6 +61,9 @@ class QQmlIncubationController; class QInputMethodEvent; class QQuickCloseEvent; class QQuickRenderControl; +class QSGRectangleNode; +class QSGImageNode; +class QSGNinePatchNode; class Q_QUICK_EXPORT QQuickWindow : public QWindow { @@ -159,6 +162,10 @@ public: static void setSceneGraphBackend(QSGRendererInterface::GraphicsApi api); static void setSceneGraphBackend(const QString &backend); + QSGRectangleNode *createRectangleNode() const; + QSGImageNode *createImageNode() const; + QSGNinePatchNode *createNinePatchNode() const; + Q_SIGNALS: void frameSwapped(); Q_REVISION(2) void openglContextCreated(QOpenGLContext *context); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp index 15bc32ecb2..921071e32e 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp @@ -39,12 +39,12 @@ #include "qsgsoftwarecontext_p.h" -#include "qsgsoftwarerectanglenode_p.h" -#include "qsgsoftwareimagenode_p.h" +#include "qsgsoftwareinternalrectanglenode_p.h" +#include "qsgsoftwareinternalimagenode_p.h" #include "qsgsoftwarepainternode_p.h" #include "qsgsoftwarepixmaptexture_p.h" #include "qsgsoftwareglyphnode_p.h" -#include "qsgsoftwareninepatchnode_p.h" +#include "qsgsoftwarepublicnodes_p.h" #include "qsgsoftwarelayer_p.h" #include "qsgsoftwarerenderer_p.h" @@ -53,11 +53,6 @@ #include -#include -#include -#include -#include - // Used for very high-level info about the renderering and gl context // Includes GL_VERSION, type of render loop, atlas size, etc. Q_LOGGING_CATEGORY(QSG_RASTER_LOG_INFO, "qt.scenegraph.info") @@ -93,14 +88,14 @@ QSGSoftwareContext::QSGSoftwareContext(QObject *parent) { } -QSGRectangleNode *QSGSoftwareContext::createRectangleNode() +QSGInternalRectangleNode *QSGSoftwareContext::createInternalRectangleNode() { - return new QSGSoftwareRectangleNode(); + return new QSGSoftwareInternalRectangleNode(); } -QSGImageNode *QSGSoftwareContext::createImageNode() +QSGInternalImageNode *QSGSoftwareContext::createInternalImageNode() { - return new QSGSoftwareImageNode(); + return new QSGSoftwareInternalImageNode(); } QSGPainterNode *QSGSoftwareContext::createPainterNode(QQuickPaintedItem *item) @@ -115,11 +110,6 @@ QSGGlyphNode *QSGSoftwareContext::createGlyphNode(QSGRenderContext *rc, bool pre return new QSGSoftwareGlyphNode(); } -QSGNinePatchNode *QSGSoftwareContext::createNinePatchNode() -{ - return new QSGSoftwareNinePatchNode(); -} - QSGLayer *QSGSoftwareContext::createLayer(QSGRenderContext *renderContext) { return new QSGSoftwareLayer(renderContext); @@ -169,6 +159,21 @@ QSGRendererInterface *QSGSoftwareContext::rendererInterface(QSGRenderContext *re return this; } +QSGRectangleNode *QSGSoftwareContext::createRectangleNode() +{ + return new QSGSoftwareRectangleNode; +} + +QSGImageNode *QSGSoftwareContext::createImageNode() +{ + return new QSGSoftwareImageNode; +} + +QSGNinePatchNode *QSGSoftwareContext::createNinePatchNode() +{ + return new QSGSoftwareNinePatchNode; +} + QSGRendererInterface::GraphicsApi QSGSoftwareContext::graphicsApi() const { return Software; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h index 992f6f5677..9a939a0948 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h @@ -86,14 +86,16 @@ public: explicit QSGSoftwareContext(QObject *parent = nullptr); QSGRenderContext *createRenderContext() override { return new QSGSoftwareRenderContext(this); } - QSGRectangleNode *createRectangleNode() override; - QSGImageNode *createImageNode() override; + QSGInternalRectangleNode *createInternalRectangleNode() override; + QSGInternalImageNode *createInternalImageNode() override; QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override; QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override; - QSGNinePatchNode *createNinePatchNode() override; QSGLayer *createLayer(QSGRenderContext *renderContext) override; QSurfaceFormat defaultSurfaceFormat() const override; QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override; + QSGRectangleNode *createRectangleNode() override; + QSGImageNode *createImageNode() override; + QSGNinePatchNode *createNinePatchNode() override; GraphicsApi graphicsApi() const override; ShaderType shaderType() const override; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp deleted file mode 100644 index c92a032623..0000000000 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp +++ /dev/null @@ -1,501 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qsgsoftwareimagenode_p.h" - -#include "qsgsoftwarepixmaptexture_p.h" -#include "qsgsoftwarelayer_p.h" -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QSGSoftwareHelpers { -// Helper from widgets/styles/qdrawutil.cpp - -static inline QMargins normalizedMargins(const QMargins &m) -{ - return QMargins(qMax(m.left(), 0), qMax(m.top(), 0), qMax(m.right(), 0), qMax(m.bottom(), 0)); -} - -void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMarginsIn, - const QPixmap &pixmap, const QRect &sourceRect, const QMargins &sourceMarginsIn, - const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints) -{ - QPainter::PixmapFragment d; - d.opacity = 1.0; - d.rotation = 0.0; - - QPixmapFragmentsArray opaqueData; - QPixmapFragmentsArray translucentData; - - QMargins sourceMargins = normalizedMargins(sourceMarginsIn); - QMargins targetMargins = normalizedMargins(targetMarginsIn); - - // source center - const int sourceCenterTop = sourceRect.top() + sourceMargins.top(); - const int sourceCenterLeft = sourceRect.left() + sourceMargins.left(); - const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1; - const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1; - const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft; - const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop; - // target center - const int targetCenterTop = targetRect.top() + targetMargins.top(); - const int targetCenterLeft = targetRect.left() + targetMargins.left(); - const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1; - const int targetCenterRight = targetRect.right() - targetMargins.right() + 1; - const int targetCenterWidth = targetCenterRight - targetCenterLeft; - const int targetCenterHeight = targetCenterBottom - targetCenterTop; - - QVarLengthArray xTarget; // x-coordinates of target rectangles - QVarLengthArray yTarget; // y-coordinates of target rectangles - - int columns = 3; - int rows = 3; - if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0) - columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth))); - if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0) - rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight))); - - xTarget.resize(columns + 1); - yTarget.resize(rows + 1); - - bool oldAA = painter->testRenderHint(QPainter::Antialiasing); - if (painter->paintEngine()->type() != QPaintEngine::OpenGL - && painter->paintEngine()->type() != QPaintEngine::OpenGL2 - && oldAA && painter->combinedTransform().type() != QTransform::TxNone) { - painter->setRenderHint(QPainter::Antialiasing, false); - } - - xTarget[0] = targetRect.left(); - xTarget[1] = targetCenterLeft; - xTarget[columns - 1] = targetCenterRight; - xTarget[columns] = targetRect.left() + targetRect.width(); - - yTarget[0] = targetRect.top(); - yTarget[1] = targetCenterTop; - yTarget[rows - 1] = targetCenterBottom; - yTarget[rows] = targetRect.top() + targetRect.height(); - - qreal dx = targetCenterWidth; - qreal dy = targetCenterHeight; - - switch (rules.horizontal) { - case Qt::StretchTile: - dx = targetCenterWidth; - break; - case Qt::RepeatTile: - dx = sourceCenterWidth; - break; - case Qt::RoundTile: - dx = targetCenterWidth / qreal(columns - 2); - break; - } - - for (int i = 2; i < columns - 1; ++i) - xTarget[i] = xTarget[i - 1] + dx; - - switch (rules.vertical) { - case Qt::StretchTile: - dy = targetCenterHeight; - break; - case Qt::RepeatTile: - dy = sourceCenterHeight; - break; - case Qt::RoundTile: - dy = targetCenterHeight / qreal(rows - 2); - break; - } - - for (int i = 2; i < rows - 1; ++i) - yTarget[i] = yTarget[i - 1] + dy; - - // corners - if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left - d.x = (0.5 * (xTarget[1] + xTarget[0])); - d.y = (0.5 * (yTarget[1] + yTarget[0])); - d.sourceLeft = sourceRect.left(); - d.sourceTop = sourceRect.top(); - d.width = sourceMargins.left(); - d.height = sourceMargins.top(); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueTopLeft) - opaqueData.append(d); - else - translucentData.append(d); - } - if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right - d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.y = (0.5 * (yTarget[1] + yTarget[0])); - d.sourceLeft = sourceCenterRight; - d.sourceTop = sourceRect.top(); - d.width = sourceMargins.right(); - d.height = sourceMargins.top(); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueTopRight) - opaqueData.append(d); - else - translucentData.append(d); - } - if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left - d.x = (0.5 * (xTarget[1] + xTarget[0])); - d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.sourceLeft = sourceRect.left(); - d.sourceTop = sourceCenterBottom; - d.width = sourceMargins.left(); - d.height = sourceMargins.bottom(); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueBottomLeft) - opaqueData.append(d); - else - translucentData.append(d); - } - if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right - d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.sourceLeft = sourceCenterRight; - d.sourceTop = sourceCenterBottom; - d.width = sourceMargins.right(); - d.height = sourceMargins.bottom(); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueBottomRight) - opaqueData.append(d); - else - translucentData.append(d); - } - - // horizontal edges - if (targetCenterWidth > 0 && sourceCenterWidth > 0) { - if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData; - d.sourceLeft = sourceCenterLeft; - d.sourceTop = sourceRect.top(); - d.width = sourceCenterWidth; - d.height = sourceMargins.top(); - d.y = (0.5 * (yTarget[1] + yTarget[0])); - d.scaleX = dx / d.width; - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; - for (int i = 1; i < columns - 1; ++i) { - d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); - data.append(d); - } - if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); - } - if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData; - d.sourceLeft = sourceCenterLeft; - d.sourceTop = sourceCenterBottom; - d.width = sourceCenterWidth; - d.height = sourceMargins.bottom(); - d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.scaleX = dx / d.width; - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; - for (int i = 1; i < columns - 1; ++i) { - d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); - data.append(d); - } - if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); - } - } - - // vertical edges - if (targetCenterHeight > 0 && sourceCenterHeight > 0) { - if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData; - d.sourceLeft = sourceRect.left(); - d.sourceTop = sourceCenterTop; - d.width = sourceMargins.left(); - d.height = sourceCenterHeight; - d.x = (0.5 * (xTarget[1] + xTarget[0])); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; - d.scaleY = dy / d.height; - for (int i = 1; i < rows - 1; ++i) { - d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); - data.append(d); - } - if (rules.vertical == Qt::RepeatTile) - data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); - } - if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData; - d.sourceLeft = sourceCenterRight; - d.sourceTop = sourceCenterTop; - d.width = sourceMargins.right(); - d.height = sourceCenterHeight; - d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; - d.scaleY = dy / d.height; - for (int i = 1; i < rows - 1; ++i) { - d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); - data.append(d); - } - if (rules.vertical == Qt::RepeatTile) - data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); - } - } - - // center - if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) { - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData; - d.sourceLeft = sourceCenterLeft; - d.sourceTop = sourceCenterTop; - d.width = sourceCenterWidth; - d.height = sourceCenterHeight; - d.scaleX = dx / d.width; - d.scaleY = dy / d.height; - - qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX; - qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY; - - for (int j = 1; j < rows - 1; ++j) { - d.y = (0.5 * (yTarget[j + 1] + yTarget[j])); - for (int i = 1; i < columns - 1; ++i) { - d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); - data.append(d); - } - if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].width = repeatWidth; - } - if (rules.vertical == Qt::RepeatTile) { - for (int i = 1; i < columns - 1; ++i) - data[data.size() - i].height = repeatHeight; - } - } - - if (opaqueData.size()) - painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint); - if (translucentData.size()) - painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap); - - if (oldAA) - painter->setRenderHint(QPainter::Antialiasing, true); -} - -} // QSGSoftwareHelpers namespace - -QSGSoftwareImageNode::QSGSoftwareImageNode() - : m_innerSourceRect(0, 0, 1, 1) - , m_subSourceRect(0, 0, 1, 1) - , m_texture(0) - , m_mirror(false) - , m_smooth(true) - , m_tileHorizontal(false) - , m_tileVertical(false) - , m_cachedMirroredPixmapIsDirty(false) -{ - setMaterial((QSGMaterial*)1); - setGeometry((QSGGeometry*)1); -} - - -void QSGSoftwareImageNode::setTargetRect(const QRectF &rect) -{ - if (rect == m_targetRect) - return; - m_targetRect = rect; - markDirty(DirtyGeometry); -} - -void QSGSoftwareImageNode::setInnerTargetRect(const QRectF &rect) -{ - if (rect == m_innerTargetRect) - return; - m_innerTargetRect = rect; - markDirty(DirtyGeometry); -} - -void QSGSoftwareImageNode::setInnerSourceRect(const QRectF &rect) -{ - if (rect == m_innerSourceRect) - return; - m_innerSourceRect = rect; - markDirty(DirtyGeometry); -} - -void QSGSoftwareImageNode::setSubSourceRect(const QRectF &rect) -{ - if (rect == m_subSourceRect) - return; - m_subSourceRect = rect; - markDirty(DirtyGeometry); -} - -void QSGSoftwareImageNode::setTexture(QSGTexture *texture) -{ - m_texture = texture; - m_cachedMirroredPixmapIsDirty = true; - markDirty(DirtyMaterial); -} - -void QSGSoftwareImageNode::setMirror(bool mirror) -{ - if (m_mirror != mirror) { - m_mirror = mirror; - m_cachedMirroredPixmapIsDirty = true; - markDirty(DirtyMaterial); - } -} - -void QSGSoftwareImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/) -{ -} - -void QSGSoftwareImageNode::setFiltering(QSGTexture::Filtering filtering) -{ - bool smooth = (filtering == QSGTexture::Linear); - if (smooth == m_smooth) - return; - - m_smooth = smooth; - markDirty(DirtyMaterial); -} - -void QSGSoftwareImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) -{ - bool tileHorizontal = (wrapMode == QSGTexture::Repeat); - if (tileHorizontal == m_tileHorizontal) - return; - - m_tileHorizontal = tileHorizontal; - markDirty(DirtyMaterial); -} - -void QSGSoftwareImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) -{ - bool tileVertical = (wrapMode == QSGTexture::Repeat); - if (tileVertical == m_tileVertical) - return; - - m_tileVertical = (wrapMode == QSGTexture::Repeat); - markDirty(DirtyMaterial); -} - -void QSGSoftwareImageNode::update() -{ - if (m_cachedMirroredPixmapIsDirty) { - if (m_mirror) { - m_cachedMirroredPixmap = pixmap().transformed(QTransform(-1, 0, 0, 1, 0, 0)); - } else { - //Cleanup cached pixmap if necessary - if (!m_cachedMirroredPixmap.isNull()) - m_cachedMirroredPixmap = QPixmap(); - } - m_cachedMirroredPixmapIsDirty = false; - } -} - -void QSGSoftwareImageNode::preprocess() -{ - bool doDirty = false; - QSGLayer *t = qobject_cast(m_texture); - if (t) { - doDirty = t->updateTexture(); - markDirty(DirtyGeometry); - } - if (doDirty) - markDirty(DirtyMaterial); -} - -static Qt::TileRule getTileRule(qreal factor) -{ - int ifactor = qRound(factor); - if (qFuzzyCompare(factor, ifactor )) { - if (ifactor == 1 || ifactor == 0) - return Qt::StretchTile; - return Qt::RoundTile; - } - return Qt::RepeatTile; -} - - -void QSGSoftwareImageNode::paint(QPainter *painter) -{ - painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth); - - const QPixmap &pm = m_mirror ? m_cachedMirroredPixmap : pixmap(); - - if (m_innerTargetRect != m_targetRect) { - // border image - QMargins margins(m_innerTargetRect.left() - m_targetRect.left(), m_innerTargetRect.top() - m_targetRect.top(), - m_targetRect.right() - m_innerTargetRect.right(), m_targetRect.bottom() - m_innerTargetRect.bottom()); - QSGSoftwareHelpers::QTileRules tilerules(getTileRule(m_subSourceRect.width()), getTileRule(m_subSourceRect.height())); - QSGSoftwareHelpers::qDrawBorderPixmap(painter, m_targetRect.toRect(), margins, pm, QRect(0, 0, pm.width(), pm.height()), - margins, tilerules, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(0)); - return; - } - - if (m_tileHorizontal || m_tileVertical) { - painter->save(); - qreal sx = m_targetRect.width()/(m_subSourceRect.width()*pm.width()); - qreal sy = m_targetRect.height()/(m_subSourceRect.height()*pm.height()); - QMatrix transform(sx, 0, 0, sy, 0, 0); - painter->setMatrix(transform, true); - painter->drawTiledPixmap(QRectF(m_targetRect.x()/sx, m_targetRect.y()/sy, m_targetRect.width()/sx, m_targetRect.height()/sy), - pm, - QPointF(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height())); - painter->restore(); - } else { - QRectF sr(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height(), - m_subSourceRect.width()*pm.width(), m_subSourceRect.height()*pm.height()); - painter->drawPixmap(m_targetRect, pm, sr); - } -} - -QRectF QSGSoftwareImageNode::rect() const -{ - return m_targetRect; -} - -const QPixmap &QSGSoftwareImageNode::pixmap() const -{ - if (QSGSoftwarePixmapTexture *pt = qobject_cast(m_texture)) { - return pt->pixmap(); - } else { - QSGSoftwareLayer *layer = qobject_cast(m_texture); - return layer->pixmap(); - } -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h deleted file mode 100644 index e42f616757..0000000000 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h +++ /dev/null @@ -1,147 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#ifndef QSGSOFTWAREIMAGENODE_H -#define QSGSOFTWAREIMAGENODE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QSGSoftwareHelpers { - -typedef QVarLengthArray QPixmapFragmentsArray; - -struct QTileRules -{ - inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule) - : horizontal(horizontalRule), vertical(verticalRule) {} - inline QTileRules(Qt::TileRule rule = Qt::StretchTile) - : horizontal(rule), vertical(rule) {} - Qt::TileRule horizontal; - Qt::TileRule vertical; -}; - -#ifndef Q_QDOC -// For internal use only. -namespace QDrawBorderPixmap -{ - enum DrawingHint - { - OpaqueTopLeft = 0x0001, - OpaqueTop = 0x0002, - OpaqueTopRight = 0x0004, - OpaqueLeft = 0x0008, - OpaqueCenter = 0x0010, - OpaqueRight = 0x0020, - OpaqueBottomLeft = 0x0040, - OpaqueBottom = 0x0080, - OpaqueBottomRight = 0x0100, - OpaqueCorners = OpaqueTopLeft | OpaqueTopRight | OpaqueBottomLeft | OpaqueBottomRight, - OpaqueEdges = OpaqueTop | OpaqueLeft | OpaqueRight | OpaqueBottom, - OpaqueFrame = OpaqueCorners | OpaqueEdges, - OpaqueAll = OpaqueCenter | OpaqueFrame - }; - - Q_DECLARE_FLAGS(DrawingHints, DrawingHint) -} -#endif - -void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins, - const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins, - const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints); - -} // QSGSoftwareHelpers namespace - -class QSGSoftwareImageNode : public QSGImageNode -{ -public: - QSGSoftwareImageNode(); - - void setTargetRect(const QRectF &rect) override; - void setInnerTargetRect(const QRectF &rect) override; - void setInnerSourceRect(const QRectF &rect) override; - void setSubSourceRect(const QRectF &rect) override; - void setTexture(QSGTexture *texture) override; - void setMirror(bool mirror) override; - void setMipmapFiltering(QSGTexture::Filtering filtering) override; - void setFiltering(QSGTexture::Filtering filtering) override; - void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override; - void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override; - void update() override; - - void preprocess() override; - - void paint(QPainter *painter); - - QRectF rect() const; - -private: - const QPixmap &pixmap() const; - - QRectF m_targetRect; - QRectF m_innerTargetRect; - QRectF m_innerSourceRect; - QRectF m_subSourceRect; - - QSGTexture *m_texture; - QPixmap m_cachedMirroredPixmap; - - bool m_mirror; - bool m_smooth; - bool m_tileHorizontal; - bool m_tileVertical; - bool m_cachedMirroredPixmapIsDirty; -}; - -QT_END_NAMESPACE - -#endif // QSGSOFTWAREIMAGENODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp new file mode 100644 index 0000000000..10291b9cb5 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp @@ -0,0 +1,501 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgsoftwareinternalimagenode_p.h" + +#include "qsgsoftwarepixmaptexture_p.h" +#include "qsgsoftwarelayer_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QSGSoftwareHelpers { +// Helper from widgets/styles/qdrawutil.cpp + +static inline QMargins normalizedMargins(const QMargins &m) +{ + return QMargins(qMax(m.left(), 0), qMax(m.top(), 0), qMax(m.right(), 0), qMax(m.bottom(), 0)); +} + +void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMarginsIn, + const QPixmap &pixmap, const QRect &sourceRect, const QMargins &sourceMarginsIn, + const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints) +{ + QPainter::PixmapFragment d; + d.opacity = 1.0; + d.rotation = 0.0; + + QPixmapFragmentsArray opaqueData; + QPixmapFragmentsArray translucentData; + + QMargins sourceMargins = normalizedMargins(sourceMarginsIn); + QMargins targetMargins = normalizedMargins(targetMarginsIn); + + // source center + const int sourceCenterTop = sourceRect.top() + sourceMargins.top(); + const int sourceCenterLeft = sourceRect.left() + sourceMargins.left(); + const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1; + const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1; + const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft; + const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop; + // target center + const int targetCenterTop = targetRect.top() + targetMargins.top(); + const int targetCenterLeft = targetRect.left() + targetMargins.left(); + const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1; + const int targetCenterRight = targetRect.right() - targetMargins.right() + 1; + const int targetCenterWidth = targetCenterRight - targetCenterLeft; + const int targetCenterHeight = targetCenterBottom - targetCenterTop; + + QVarLengthArray xTarget; // x-coordinates of target rectangles + QVarLengthArray yTarget; // y-coordinates of target rectangles + + int columns = 3; + int rows = 3; + if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0) + columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth))); + if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0) + rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight))); + + xTarget.resize(columns + 1); + yTarget.resize(rows + 1); + + bool oldAA = painter->testRenderHint(QPainter::Antialiasing); + if (painter->paintEngine()->type() != QPaintEngine::OpenGL + && painter->paintEngine()->type() != QPaintEngine::OpenGL2 + && oldAA && painter->combinedTransform().type() != QTransform::TxNone) { + painter->setRenderHint(QPainter::Antialiasing, false); + } + + xTarget[0] = targetRect.left(); + xTarget[1] = targetCenterLeft; + xTarget[columns - 1] = targetCenterRight; + xTarget[columns] = targetRect.left() + targetRect.width(); + + yTarget[0] = targetRect.top(); + yTarget[1] = targetCenterTop; + yTarget[rows - 1] = targetCenterBottom; + yTarget[rows] = targetRect.top() + targetRect.height(); + + qreal dx = targetCenterWidth; + qreal dy = targetCenterHeight; + + switch (rules.horizontal) { + case Qt::StretchTile: + dx = targetCenterWidth; + break; + case Qt::RepeatTile: + dx = sourceCenterWidth; + break; + case Qt::RoundTile: + dx = targetCenterWidth / qreal(columns - 2); + break; + } + + for (int i = 2; i < columns - 1; ++i) + xTarget[i] = xTarget[i - 1] + dx; + + switch (rules.vertical) { + case Qt::StretchTile: + dy = targetCenterHeight; + break; + case Qt::RepeatTile: + dy = sourceCenterHeight; + break; + case Qt::RoundTile: + dy = targetCenterHeight / qreal(rows - 2); + break; + } + + for (int i = 2; i < rows - 1; ++i) + yTarget[i] = yTarget[i - 1] + dy; + + // corners + if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceRect.top(); + d.width = sourceMargins.left(); + d.height = sourceMargins.top(); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueTopLeft) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceRect.top(); + d.width = sourceMargins.right(); + d.height = sourceMargins.top(); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueTopRight) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceCenterBottom; + d.width = sourceMargins.left(); + d.height = sourceMargins.bottom(); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueBottomLeft) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceCenterBottom; + d.width = sourceMargins.right(); + d.height = sourceMargins.bottom(); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueBottomRight) + opaqueData.append(d); + else + translucentData.append(d); + } + + // horizontal edges + if (targetCenterWidth > 0 && sourceCenterWidth > 0) { + if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceRect.top(); + d.width = sourceCenterWidth; + d.height = sourceMargins.top(); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.scaleX = dx / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); + } + if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceCenterBottom; + d.width = sourceCenterWidth; + d.height = sourceMargins.bottom(); + d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.scaleX = dx / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); + } + } + + // vertical edges + if (targetCenterHeight > 0 && sourceCenterHeight > 0) { + if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData; + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceCenterTop; + d.width = sourceMargins.left(); + d.height = sourceCenterHeight; + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = dy / d.height; + for (int i = 1; i < rows - 1; ++i) { + d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); + data.append(d); + } + if (rules.vertical == Qt::RepeatTile) + data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); + } + if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData; + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceCenterTop; + d.width = sourceMargins.right(); + d.height = sourceCenterHeight; + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = dy / d.height; + for (int i = 1; i < rows - 1; ++i) { + d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); + data.append(d); + } + if (rules.vertical == Qt::RepeatTile) + data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); + } + } + + // center + if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) { + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceCenterTop; + d.width = sourceCenterWidth; + d.height = sourceCenterHeight; + d.scaleX = dx / d.width; + d.scaleY = dy / d.height; + + qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX; + qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY; + + for (int j = 1; j < rows - 1; ++j) { + d.y = (0.5 * (yTarget[j + 1] + yTarget[j])); + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = repeatWidth; + } + if (rules.vertical == Qt::RepeatTile) { + for (int i = 1; i < columns - 1; ++i) + data[data.size() - i].height = repeatHeight; + } + } + + if (opaqueData.size()) + painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint); + if (translucentData.size()) + painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap); + + if (oldAA) + painter->setRenderHint(QPainter::Antialiasing, true); +} + +} // QSGSoftwareHelpers namespace + +QSGSoftwareInternalImageNode::QSGSoftwareInternalImageNode() + : m_innerSourceRect(0, 0, 1, 1) + , m_subSourceRect(0, 0, 1, 1) + , m_texture(0) + , m_mirror(false) + , m_smooth(true) + , m_tileHorizontal(false) + , m_tileVertical(false) + , m_cachedMirroredPixmapIsDirty(false) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + + +void QSGSoftwareInternalImageNode::setTargetRect(const QRectF &rect) +{ + if (rect == m_targetRect) + return; + m_targetRect = rect; + markDirty(DirtyGeometry); +} + +void QSGSoftwareInternalImageNode::setInnerTargetRect(const QRectF &rect) +{ + if (rect == m_innerTargetRect) + return; + m_innerTargetRect = rect; + markDirty(DirtyGeometry); +} + +void QSGSoftwareInternalImageNode::setInnerSourceRect(const QRectF &rect) +{ + if (rect == m_innerSourceRect) + return; + m_innerSourceRect = rect; + markDirty(DirtyGeometry); +} + +void QSGSoftwareInternalImageNode::setSubSourceRect(const QRectF &rect) +{ + if (rect == m_subSourceRect) + return; + m_subSourceRect = rect; + markDirty(DirtyGeometry); +} + +void QSGSoftwareInternalImageNode::setTexture(QSGTexture *texture) +{ + m_texture = texture; + m_cachedMirroredPixmapIsDirty = true; + markDirty(DirtyMaterial); +} + +void QSGSoftwareInternalImageNode::setMirror(bool mirror) +{ + if (m_mirror != mirror) { + m_mirror = mirror; + m_cachedMirroredPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/) +{ +} + +void QSGSoftwareInternalImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + bool smooth = (filtering == QSGTexture::Linear); + if (smooth == m_smooth) + return; + + m_smooth = smooth; + markDirty(DirtyMaterial); +} + +void QSGSoftwareInternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) +{ + bool tileHorizontal = (wrapMode == QSGTexture::Repeat); + if (tileHorizontal == m_tileHorizontal) + return; + + m_tileHorizontal = tileHorizontal; + markDirty(DirtyMaterial); +} + +void QSGSoftwareInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) +{ + bool tileVertical = (wrapMode == QSGTexture::Repeat); + if (tileVertical == m_tileVertical) + return; + + m_tileVertical = (wrapMode == QSGTexture::Repeat); + markDirty(DirtyMaterial); +} + +void QSGSoftwareInternalImageNode::update() +{ + if (m_cachedMirroredPixmapIsDirty) { + if (m_mirror) { + m_cachedMirroredPixmap = pixmap().transformed(QTransform(-1, 0, 0, 1, 0, 0)); + } else { + //Cleanup cached pixmap if necessary + if (!m_cachedMirroredPixmap.isNull()) + m_cachedMirroredPixmap = QPixmap(); + } + m_cachedMirroredPixmapIsDirty = false; + } +} + +void QSGSoftwareInternalImageNode::preprocess() +{ + bool doDirty = false; + QSGLayer *t = qobject_cast(m_texture); + if (t) { + doDirty = t->updateTexture(); + markDirty(DirtyGeometry); + } + if (doDirty) + markDirty(DirtyMaterial); +} + +static Qt::TileRule getTileRule(qreal factor) +{ + int ifactor = qRound(factor); + if (qFuzzyCompare(factor, ifactor )) { + if (ifactor == 1 || ifactor == 0) + return Qt::StretchTile; + return Qt::RoundTile; + } + return Qt::RepeatTile; +} + + +void QSGSoftwareInternalImageNode::paint(QPainter *painter) +{ + painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth); + + const QPixmap &pm = m_mirror ? m_cachedMirroredPixmap : pixmap(); + + if (m_innerTargetRect != m_targetRect) { + // border image + QMargins margins(m_innerTargetRect.left() - m_targetRect.left(), m_innerTargetRect.top() - m_targetRect.top(), + m_targetRect.right() - m_innerTargetRect.right(), m_targetRect.bottom() - m_innerTargetRect.bottom()); + QSGSoftwareHelpers::QTileRules tilerules(getTileRule(m_subSourceRect.width()), getTileRule(m_subSourceRect.height())); + QSGSoftwareHelpers::qDrawBorderPixmap(painter, m_targetRect.toRect(), margins, pm, QRect(0, 0, pm.width(), pm.height()), + margins, tilerules, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(0)); + return; + } + + if (m_tileHorizontal || m_tileVertical) { + painter->save(); + qreal sx = m_targetRect.width()/(m_subSourceRect.width()*pm.width()); + qreal sy = m_targetRect.height()/(m_subSourceRect.height()*pm.height()); + QMatrix transform(sx, 0, 0, sy, 0, 0); + painter->setMatrix(transform, true); + painter->drawTiledPixmap(QRectF(m_targetRect.x()/sx, m_targetRect.y()/sy, m_targetRect.width()/sx, m_targetRect.height()/sy), + pm, + QPointF(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height())); + painter->restore(); + } else { + QRectF sr(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height(), + m_subSourceRect.width()*pm.width(), m_subSourceRect.height()*pm.height()); + painter->drawPixmap(m_targetRect, pm, sr); + } +} + +QRectF QSGSoftwareInternalImageNode::rect() const +{ + return m_targetRect; +} + +const QPixmap &QSGSoftwareInternalImageNode::pixmap() const +{ + if (QSGSoftwarePixmapTexture *pt = qobject_cast(m_texture)) { + return pt->pixmap(); + } else { + QSGSoftwareLayer *layer = qobject_cast(m_texture); + return layer->pixmap(); + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h new file mode 100644 index 0000000000..f21667fdf7 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGSOFTWAREINTERNALIMAGENODE_H +#define QSGSOFTWAREINTERNALIMAGENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QSGSoftwareHelpers { + +typedef QVarLengthArray QPixmapFragmentsArray; + +struct QTileRules +{ + inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule) + : horizontal(horizontalRule), vertical(verticalRule) {} + inline QTileRules(Qt::TileRule rule = Qt::StretchTile) + : horizontal(rule), vertical(rule) {} + Qt::TileRule horizontal; + Qt::TileRule vertical; +}; + +#ifndef Q_QDOC +// For internal use only. +namespace QDrawBorderPixmap +{ + enum DrawingHint + { + OpaqueTopLeft = 0x0001, + OpaqueTop = 0x0002, + OpaqueTopRight = 0x0004, + OpaqueLeft = 0x0008, + OpaqueCenter = 0x0010, + OpaqueRight = 0x0020, + OpaqueBottomLeft = 0x0040, + OpaqueBottom = 0x0080, + OpaqueBottomRight = 0x0100, + OpaqueCorners = OpaqueTopLeft | OpaqueTopRight | OpaqueBottomLeft | OpaqueBottomRight, + OpaqueEdges = OpaqueTop | OpaqueLeft | OpaqueRight | OpaqueBottom, + OpaqueFrame = OpaqueCorners | OpaqueEdges, + OpaqueAll = OpaqueCenter | OpaqueFrame + }; + + Q_DECLARE_FLAGS(DrawingHints, DrawingHint) +} +#endif + +void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins, + const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins, + const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints); + +} // QSGSoftwareHelpers namespace + +class QSGSoftwareInternalImageNode : public QSGInternalImageNode +{ +public: + QSGSoftwareInternalImageNode(); + + void setTargetRect(const QRectF &rect) override; + void setInnerTargetRect(const QRectF &rect) override; + void setInnerSourceRect(const QRectF &rect) override; + void setSubSourceRect(const QRectF &rect) override; + void setTexture(QSGTexture *texture) override; + void setMirror(bool mirror) override; + void setMipmapFiltering(QSGTexture::Filtering filtering) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override; + void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override; + void update() override; + + void preprocess() override; + + void paint(QPainter *painter); + + QRectF rect() const; + +private: + const QPixmap &pixmap() const; + + QRectF m_targetRect; + QRectF m_innerTargetRect; + QRectF m_innerSourceRect; + QRectF m_subSourceRect; + + QSGTexture *m_texture; + QPixmap m_cachedMirroredPixmap; + + bool m_mirror; + bool m_smooth; + bool m_tileHorizontal; + bool m_tileVertical; + bool m_cachedMirroredPixmapIsDirty; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWAREINTERNALIMAGENODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp new file mode 100644 index 0000000000..ea5124edf9 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgsoftwareinternalrectanglenode_p.h" +#include + +#include + +QT_BEGIN_NAMESPACE + +QSGSoftwareInternalRectangleNode::QSGSoftwareInternalRectangleNode() + : m_penWidth(0) + , m_radius(0) + , m_cornerPixmapIsDirty(true) + , m_devicePixelRatio(1) +{ + m_pen.setJoinStyle(Qt::MiterJoin); + m_pen.setMiterLimit(0); + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void QSGSoftwareInternalRectangleNode::setRect(const QRectF &rect) +{ + QRect alignedRect = rect.toAlignedRect(); + if (m_rect != alignedRect) { + m_rect = alignedRect; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setPenColor(const QColor &color) +{ + if (m_penColor != color) { + m_penColor = color; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setPenWidth(qreal width) +{ + if (m_penWidth != width) { + m_penWidth = width; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +//Move first stop by pos relative to seconds +static QGradientStop interpolateStop(const QGradientStop &firstStop, const QGradientStop &secondStop, double newPos) +{ + double distance = secondStop.first - firstStop.first; + double distanceDelta = newPos - firstStop.first; + double modifierValue = distanceDelta / distance; + int redDelta = (secondStop.second.red() - firstStop.second.red()) * modifierValue; + int greenDelta = (secondStop.second.green() - firstStop.second.green()) * modifierValue; + int blueDelta = (secondStop.second.blue() - firstStop.second.blue()) * modifierValue; + int alphaDelta = (secondStop.second.alpha() - firstStop.second.alpha()) * modifierValue; + + QGradientStop newStop; + newStop.first = newPos; + newStop.second = QColor(firstStop.second.red() + redDelta, + firstStop.second.green() + greenDelta, + firstStop.second.blue() + blueDelta, + firstStop.second.alpha() + alphaDelta); + + return newStop; +} + +void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &stops) +{ + //normalize stops + bool needsNormalization = false; + foreach (const QGradientStop &stop, stops) { + if (stop.first < 0.0 || stop.first > 1.0) { + needsNormalization = true; + continue; + } + } + + if (needsNormalization) { + QGradientStops normalizedStops; + if (stops.count() == 1) { + //If there is only one stop, then the position does not matter + //It is just treated as a color + QGradientStop stop = stops.at(0); + stop.first = 0.0; + normalizedStops.append(stop); + } else { + //Clip stops to only the first below 0.0 and above 1.0 + int below = -1; + int above = -1; + QVector between; + for (int i = 0; i < stops.count(); ++i) { + if (stops.at(i).first < 0.0) { + below = i; + } else if (stops.at(i).first > 1.0) { + above = i; + break; + } else { + between.append(i); + } + } + + //Interpoloate new color values for above and below + if (below != -1 ) { + //If there are more than one stops left, interpolate + if (below + 1 < stops.count()) { + normalizedStops.append(interpolateStop(stops.at(below), stops.at(below + 1), 0.0)); + } else { + QGradientStop singleStop; + singleStop.first = 0.0; + singleStop.second = stops.at(below).second; + normalizedStops.append(singleStop); + } + } + + for (int i = 0; i < between.count(); ++i) + normalizedStops.append(stops.at(between.at(i))); + + if (above != -1) { + //If there stops before above, interpolate + if (above >= 1) { + normalizedStops.append(interpolateStop(stops.at(above), stops.at(above - 1), 1.0)); + } else { + QGradientStop singleStop; + singleStop.first = 1.0; + singleStop.second = stops.at(above).second; + normalizedStops.append(singleStop); + } + } + } + + m_stops = normalizedStops; + + } else { + m_stops = stops; + } + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); +} + +void QSGSoftwareInternalRectangleNode::setRadius(qreal radius) +{ + if (m_radius != radius) { + m_radius = radius; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setAligned(bool /*aligned*/) +{ +} + +void QSGSoftwareInternalRectangleNode::update() +{ + if (!m_penWidth || m_penColor == Qt::transparent) { + m_pen = Qt::NoPen; + } else { + m_pen = QPen(m_penColor); + m_pen.setWidthF(m_penWidth); + } + + if (!m_stops.isEmpty()) { + QLinearGradient gradient(QPoint(0,0), QPoint(0,1)); + gradient.setStops(m_stops); + gradient.setCoordinateMode(QGradient::ObjectBoundingMode); + m_brush = QBrush(gradient); + } else { + m_brush = QBrush(m_color); + } + + if (m_cornerPixmapIsDirty) { + generateCornerPixmap(); + m_cornerPixmapIsDirty = false; + } +} + +void QSGSoftwareInternalRectangleNode::paint(QPainter *painter) +{ + //We can only check for a device pixel ratio change when we know what + //paint device is being used. + if (painter->device()->devicePixelRatio() != m_devicePixelRatio) { + m_devicePixelRatio = painter->device()->devicePixelRatio(); + generateCornerPixmap(); + } + + if (painter->transform().isRotating()) { + //Rotated rectangles lose the benefits of direct rendering, and have poor rendering + //quality when using only blits and fills. + + if (m_radius == 0 && m_penWidth == 0) { + //Non-Rounded Rects without borders (fall back to drawRect) + //Most common case + painter->setPen(Qt::NoPen); + painter->setBrush(m_brush); + painter->drawRect(m_rect); + } else { + //Rounded Rects and Rects with Borders + //Avoids broken behaviors of QPainter::drawRect/roundedRect + QPixmap pixmap = QPixmap(m_rect.width() * m_devicePixelRatio, m_rect.height() * m_devicePixelRatio); + pixmap.fill(Qt::transparent); + pixmap.setDevicePixelRatio(m_devicePixelRatio); + QPainter pixmapPainter(&pixmap); + paintRectangle(&pixmapPainter, QRect(0, 0, m_rect.width(), m_rect.height())); + + QPainter::RenderHints previousRenderHints = painter->renderHints(); + painter->setRenderHint(QPainter::SmoothPixmapTransform, true); + painter->drawPixmap(m_rect, pixmap); + painter->setRenderHints(previousRenderHints); + } + + + } else { + //Paint directly + paintRectangle(painter, m_rect); + } + +} + +bool QSGSoftwareInternalRectangleNode::isOpaque() const +{ + if (m_radius > 0.0f) + return false; + if (m_color.alpha() < 255) + return false; + if (m_penWidth > 0.0f && m_penColor.alpha() < 255) + return false; + if (m_stops.count() > 0) { + foreach (QGradientStop stop, m_stops) { + if (stop.second.alpha() < 255) + return false; + } + } + + return true; +} + +QRectF QSGSoftwareInternalRectangleNode::rect() const +{ + //TODO: double check that this is correct. + return m_rect; +} + +void QSGSoftwareInternalRectangleNode::paintRectangle(QPainter *painter, const QRect &rect) +{ + //Radius should never exceeds half of the width or half of the height + int radius = qFloor(qMin(qMin(rect.width(), rect.height()) * 0.5, m_radius)); + + QPainter::RenderHints previousRenderHints = painter->renderHints(); + painter->setRenderHint(QPainter::Antialiasing, false); + + if (m_penWidth > 0) { + //Fill border Rects + + //Borders can not be more than half the height/width of a rect + double borderWidth = qMin(m_penWidth, rect.width() * 0.5); + double borderHeight = qMin(m_penWidth, rect.height() * 0.5); + + + + if (borderWidth > radius) { + //4 Rects + QRectF borderTopOutside(QPointF(rect.x() + radius, rect.y()), + QPointF(rect.x() + rect.width() - radius, rect.y() + radius)); + QRectF borderTopInside(QPointF(rect.x() + borderWidth, rect.y() + radius), + QPointF(rect.x() + rect.width() - borderWidth, rect.y() + borderHeight)); + QRectF borderBottomOutside(QPointF(rect.x() + radius, rect.y() + rect.height() - radius), + QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height())); + QRectF borderBottomInside(QPointF(rect.x() + borderWidth, rect.y() + rect.height() - borderHeight), + QPointF(rect.x() + rect.width() - borderWidth, rect.y() + rect.height() - radius)); + + if (borderTopOutside.isValid()) + painter->fillRect(borderTopOutside, m_penColor); + if (borderTopInside.isValid()) + painter->fillRect(borderTopInside, m_penColor); + if (borderBottomOutside.isValid()) + painter->fillRect(borderBottomOutside, m_penColor); + if (borderBottomInside.isValid()) + painter->fillRect(borderBottomInside, m_penColor); + + } else { + //2 Rects + QRectF borderTop(QPointF(rect.x() + radius, rect.y()), + QPointF(rect.x() + rect.width() - radius, rect.y() + borderHeight)); + QRectF borderBottom(QPointF(rect.x() + radius, rect.y() + rect.height() - borderHeight), + QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height())); + if (borderTop.isValid()) + painter->fillRect(borderTop, m_penColor); + if (borderBottom.isValid()) + painter->fillRect(borderBottom, m_penColor); + } + QRectF borderLeft(QPointF(rect.x(), rect.y() + radius), + QPointF(rect.x() + borderWidth, rect.y() + rect.height() - radius)); + QRectF borderRight(QPointF(rect.x() + rect.width() - borderWidth, rect.y() + radius), + QPointF(rect.x() + rect.width(), rect.y() + rect.height() - radius)); + if (borderLeft.isValid()) + painter->fillRect(borderLeft, m_penColor); + if (borderRight.isValid()) + painter->fillRect(borderRight, m_penColor); + } + + + if (radius > 0) { + + if (radius * 2 >= rect.width() && radius * 2 >= rect.height()) { + //Blit whole pixmap for circles + painter->drawPixmap(rect, m_cornerPixmap, m_cornerPixmap.rect()); + } else { + + //blit 4 corners to border + int scaledRadius = radius * m_devicePixelRatio; + QRectF topLeftCorner(QPointF(rect.x(), rect.y()), + QPointF(rect.x() + radius, rect.y() + radius)); + painter->drawPixmap(topLeftCorner, m_cornerPixmap, QRectF(0, 0, scaledRadius, scaledRadius)); + QRectF topRightCorner(QPointF(rect.x() + rect.width() - radius, rect.y()), + QPointF(rect.x() + rect.width(), rect.y() + radius)); + painter->drawPixmap(topRightCorner, m_cornerPixmap, QRectF(scaledRadius, 0, scaledRadius, scaledRadius)); + QRectF bottomLeftCorner(QPointF(rect.x(), rect.y() + rect.height() - radius), + QPointF(rect.x() + radius, rect.y() + rect.height())); + painter->drawPixmap(bottomLeftCorner, m_cornerPixmap, QRectF(0, scaledRadius, scaledRadius, scaledRadius)); + QRectF bottomRightCorner(QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height() - radius), + QPointF(rect.x() + rect.width(), rect.y() + rect.height())); + painter->drawPixmap(bottomRightCorner, m_cornerPixmap, QRectF(scaledRadius, scaledRadius, scaledRadius, scaledRadius)); + + } + + } + + QRectF brushRect = QRectF(rect).marginsRemoved(QMarginsF(m_penWidth, m_penWidth, m_penWidth, m_penWidth)); + if (brushRect.width() < 0) + brushRect.setWidth(0); + if (brushRect.height() < 0) + brushRect.setHeight(0); + double innerRectRadius = qMax(0.0, radius - m_penWidth); + + //If not completely transparent or has a gradient + if (m_color.alpha() > 0 || !m_stops.empty()) { + if (innerRectRadius > 0) { + //Rounded Rect + if (m_stops.empty()) { + //Rounded Rects without gradient need 3 blits + QRectF centerRect(QPointF(brushRect.x() + innerRectRadius, brushRect.y()), + QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + brushRect.height())); + painter->fillRect(centerRect, m_color); + QRectF leftRect(QPointF(brushRect.x(), brushRect.y() + innerRectRadius), + QPointF(brushRect.x() + innerRectRadius, brushRect.y() + brushRect.height() - innerRectRadius)); + painter->fillRect(leftRect, m_color); + QRectF rightRect(QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + innerRectRadius), + QPointF(brushRect.x() + brushRect.width(), brushRect.y() + brushRect.height() - innerRectRadius)); + painter->fillRect(rightRect, m_color); + } else { + //Rounded Rect with gradient (slow) + painter->setPen(Qt::NoPen); + painter->setBrush(m_brush); + painter->drawRoundedRect(brushRect, innerRectRadius, innerRectRadius); + } + } else { + //non-rounded rects only need 1 blit + painter->fillRect(brushRect, m_brush); + } + } + + painter->setRenderHints(previousRenderHints); +} + +void QSGSoftwareInternalRectangleNode::generateCornerPixmap() +{ + //Generate new corner Pixmap + int radius = qFloor(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius)); + + m_cornerPixmap = QPixmap(radius * 2 * m_devicePixelRatio, radius * 2 * m_devicePixelRatio); + m_cornerPixmap.setDevicePixelRatio(m_devicePixelRatio); + m_cornerPixmap.fill(Qt::transparent); + + if (radius > 0) { + QPainter cornerPainter(&m_cornerPixmap); + cornerPainter.setRenderHint(QPainter::Antialiasing); + cornerPainter.setCompositionMode(QPainter::CompositionMode_Source); + + //Paint outer cicle + if (m_penWidth > 0) { + cornerPainter.setPen(Qt::NoPen); + cornerPainter.setBrush(m_penColor); + cornerPainter.drawRoundedRect(QRectF(0, 0, radius * 2, radius *2), radius, radius); + } + + //Paint inner circle + if (radius > m_penWidth) { + cornerPainter.setPen(Qt::NoPen); + if (m_stops.isEmpty()) + cornerPainter.setBrush(m_brush); + else + cornerPainter.setBrush(Qt::transparent); + + QMarginsF adjustmentMargins(m_penWidth, m_penWidth, m_penWidth, m_penWidth); + QRectF cornerCircleRect = QRectF(0, 0, radius * 2, radius * 2).marginsRemoved(adjustmentMargins); + cornerPainter.drawRoundedRect(cornerCircleRect, radius, radius); + } + cornerPainter.end(); + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h new file mode 100644 index 0000000000..f363e279e1 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGSOFTWAREINTERNALRECTANGLENODE_H +#define QSGSOFTWAREINTERNALRECTANGLENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGSoftwareInternalRectangleNode : public QSGInternalRectangleNode +{ +public: + QSGSoftwareInternalRectangleNode(); + + void setRect(const QRectF &rect) override; + void setColor(const QColor &color) override; + void setPenColor(const QColor &color) override; + void setPenWidth(qreal width) override; + void setGradientStops(const QGradientStops &stops) override; + void setRadius(qreal radius) override; + void setAntialiasing(bool antialiasing) override { Q_UNUSED(antialiasing) } + void setAligned(bool aligned) override; + + void update() override; + + void paint(QPainter *); + + bool isOpaque() const; + QRectF rect() const; +private: + void paintRectangle(QPainter *painter, const QRect &rect); + void generateCornerPixmap(); + + QRect m_rect; + QColor m_color; + QColor m_penColor; + double m_penWidth; + QGradientStops m_stops; + double m_radius; + QPen m_pen; + QBrush m_brush; + + bool m_cornerPixmapIsDirty; + QPixmap m_cornerPixmap; + + int m_devicePixelRatio; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWAREINTERNALRECTANGLENODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp deleted file mode 100644 index 36ff1f2229..0000000000 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qsgsoftwareninepatchnode_p.h" -#include "qsgsoftwarepixmaptexture_p.h" -#include "qsgsoftwareimagenode_p.h" - -QT_BEGIN_NAMESPACE - -QSGSoftwareNinePatchNode::QSGSoftwareNinePatchNode() -{ - setMaterial((QSGMaterial*)1); - setGeometry((QSGGeometry*)1); -} - -void QSGSoftwareNinePatchNode::setTexture(QSGTexture *texture) -{ - QSGSoftwarePixmapTexture *pt = qobject_cast(texture); - if (!pt) { - qWarning() << "Image used with invalid texture format."; - return; - } - m_pixmap = pt->pixmap(); - markDirty(DirtyMaterial); -} - -void QSGSoftwareNinePatchNode::setBounds(const QRectF &bounds) -{ - if (m_bounds == bounds) - return; - - m_bounds = bounds; - markDirty(DirtyGeometry); -} - -void QSGSoftwareNinePatchNode::setDevicePixelRatio(qreal ratio) -{ - if (m_pixelRatio == ratio) - return; - - m_pixelRatio = ratio; - markDirty(DirtyGeometry); -} - -void QSGSoftwareNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) -{ - QMargins margins(qRound(left), qRound(top), qRound(right), qRound(bottom)); - if (m_margins == margins) - return; - - m_margins = QMargins(qRound(left), qRound(top), qRound(right), qRound(bottom)); - markDirty(DirtyGeometry); -} - -void QSGSoftwareNinePatchNode::update() -{ -} - -void QSGSoftwareNinePatchNode::paint(QPainter *painter) -{ - if (m_margins.isNull()) - painter->drawPixmap(m_bounds, m_pixmap, QRectF(0, 0, m_pixmap.width(), m_pixmap.height())); - else - QSGSoftwareHelpers::qDrawBorderPixmap(painter, m_bounds.toRect(), m_margins, m_pixmap, QRect(0, 0, m_pixmap.width(), m_pixmap.height()), - m_margins, Qt::StretchTile, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(0)); -} - -QRectF QSGSoftwareNinePatchNode::bounds() const -{ - return m_bounds; -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h deleted file mode 100644 index bc7aec1b5a..0000000000 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#ifndef QSGSOFTWARENINEPATCHNODE_H -#define QSGSOFTWARENINEPATCHNODE_H - -#include - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_BEGIN_NAMESPACE - -class QSGSoftwareNinePatchNode : public QSGNinePatchNode -{ -public: - QSGSoftwareNinePatchNode(); - - void setTexture(QSGTexture *texture) override; - void setBounds(const QRectF &bounds) override; - void setDevicePixelRatio(qreal ratio) override; - void setPadding(qreal left, qreal top, qreal right, qreal bottom) override; - void update() override; - - void paint(QPainter *painter); - - QRectF bounds() const; - -private: - QPixmap m_pixmap; - QRectF m_bounds; - qreal m_pixelRatio; - QMargins m_margins; -}; - -QT_END_NAMESPACE - -#endif // QSGSOFTWARENINEPATCHNODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp new file mode 100644 index 0000000000..53974a77bf --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgsoftwarepublicnodes_p.h" +#include "qsgsoftwarepixmaptexture_p.h" +#include "qsgsoftwareinternalimagenode_p.h" + +QT_BEGIN_NAMESPACE + +QSGSoftwareRectangleNode::QSGSoftwareRectangleNode() + : m_color(QColor(255, 255, 255)) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void QSGSoftwareRectangleNode::paint(QPainter *painter) +{ + painter->fillRect(m_rect, m_color); +} + +QSGSoftwareImageNode::QSGSoftwareImageNode() + : m_texture(nullptr), + m_owns(false) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +QSGSoftwareImageNode::~QSGSoftwareImageNode() +{ + if (m_owns) + delete m_texture; +} + +void QSGSoftwareImageNode::paint(QPainter *painter) +{ + if (QSGSoftwarePixmapTexture *pt = dynamic_cast(m_texture)) { + const QPixmap &pm = pt->pixmap(); + painter->drawPixmap(m_rect, pm, QRectF(0, 0, pm.width(), pm.height())); + } else if (QSGPlainTexture *pt = dynamic_cast(m_texture)) { + const QImage &im = pt->image(); + painter->drawImage(m_rect, im, QRectF(0, 0, im.width(), im.height())); + } +} + +QSGSoftwareNinePatchNode::QSGSoftwareNinePatchNode() +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void QSGSoftwareNinePatchNode::setTexture(QSGTexture *texture) +{ + QSGSoftwarePixmapTexture *pt = qobject_cast(texture); + if (!pt) { + qWarning() << "Image used with invalid texture format."; + return; + } + m_pixmap = pt->pixmap(); + markDirty(DirtyMaterial); +} + +void QSGSoftwareNinePatchNode::setBounds(const QRectF &bounds) +{ + if (m_bounds == bounds) + return; + + m_bounds = bounds; + markDirty(DirtyGeometry); +} + +void QSGSoftwareNinePatchNode::setDevicePixelRatio(qreal ratio) +{ + if (m_pixelRatio == ratio) + return; + + m_pixelRatio = ratio; + markDirty(DirtyGeometry); +} + +void QSGSoftwareNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) +{ + QMargins margins(qRound(left), qRound(top), qRound(right), qRound(bottom)); + if (m_margins == margins) + return; + + m_margins = QMargins(qRound(left), qRound(top), qRound(right), qRound(bottom)); + markDirty(DirtyGeometry); +} + +void QSGSoftwareNinePatchNode::update() +{ +} + +void QSGSoftwareNinePatchNode::paint(QPainter *painter) +{ + if (m_margins.isNull()) + painter->drawPixmap(m_bounds, m_pixmap, QRectF(0, 0, m_pixmap.width(), m_pixmap.height())); + else + QSGSoftwareHelpers::qDrawBorderPixmap(painter, m_bounds.toRect(), m_margins, m_pixmap, QRect(0, 0, m_pixmap.width(), m_pixmap.height()), + m_margins, Qt::StretchTile, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(0)); +} + +QRectF QSGSoftwareNinePatchNode::bounds() const +{ + return m_bounds; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h new file mode 100644 index 0000000000..8f5216d03a --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGSOFTWAREPUBLICNODES_H +#define QSGSOFTWAREPUBLICNODES_H + +#include +#include +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QSGSoftwareRectangleNode : public QSGRectangleNode +{ +public: + QSGSoftwareRectangleNode(); + + void setRect(const QRectF &rect) override { m_rect = rect; markDirty(DirtyMaterial); } + QRectF rect() const override { return m_rect; } + + void setColor(const QColor &color) override { m_color = color; markDirty(DirtyMaterial); } + QColor color() const override { return m_color; } + + void paint(QPainter *painter); + +private: + QRectF m_rect; + QColor m_color; +}; + +class QSGSoftwareImageNode : public QSGImageNode +{ +public: + QSGSoftwareImageNode(); + ~QSGSoftwareImageNode(); + + void setRect(const QRectF &rect) override { m_rect = rect; markDirty(DirtyMaterial); } + QRectF rect() const override { return m_rect; } + + void setSourceRect(const QRectF &r) override { m_sourceRect = r; } + QRectF sourceRect() const override { return m_sourceRect; } + + void setTexture(QSGTexture *texture) override { m_texture = texture; markDirty(DirtyMaterial); } + QSGTexture *texture() const override { return m_texture; } + + void setFiltering(QSGTexture::Filtering) override { } + QSGTexture::Filtering filtering() const override { return QSGTexture::None; } + + void setTextureCoordinatesTransform(TextureCoordinatesTransformMode) override { } + TextureCoordinatesTransformMode textureCoordinatesTransform() const override { return NoTransform; } + + void setOwnsTexture(bool owns) override { m_owns = owns; } + bool ownsTexture() const override { return m_owns; } + + void paint(QPainter *painter); + +private: + QPixmap m_pixmap; + QSGTexture *m_texture; + QRectF m_rect; + QRectF m_sourceRect; + bool m_owns; +}; + +class QSGSoftwareNinePatchNode : public QSGNinePatchNode +{ +public: + QSGSoftwareNinePatchNode(); + + void setTexture(QSGTexture *texture) override; + void setBounds(const QRectF &bounds) override; + void setDevicePixelRatio(qreal ratio) override; + void setPadding(qreal left, qreal top, qreal right, qreal bottom) override; + void update() override; + + void paint(QPainter *painter); + + QRectF bounds() const; + +private: + QPixmap m_pixmap; + QRectF m_bounds; + qreal m_pixelRatio; + QMargins m_margins; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWAREPUBLICNODES_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp deleted file mode 100644 index 7672b14371..0000000000 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qsgsoftwarerectanglenode_p.h" -#include - -#include - -QT_BEGIN_NAMESPACE - -QSGSoftwareRectangleNode::QSGSoftwareRectangleNode() - : m_penWidth(0) - , m_radius(0) - , m_cornerPixmapIsDirty(true) - , m_devicePixelRatio(1) -{ - m_pen.setJoinStyle(Qt::MiterJoin); - m_pen.setMiterLimit(0); - setMaterial((QSGMaterial*)1); - setGeometry((QSGGeometry*)1); -} - -void QSGSoftwareRectangleNode::setRect(const QRectF &rect) -{ - QRect alignedRect = rect.toAlignedRect(); - if (m_rect != alignedRect) { - m_rect = alignedRect; - markDirty(DirtyMaterial); - } -} - -void QSGSoftwareRectangleNode::setColor(const QColor &color) -{ - if (m_color != color) { - m_color = color; - m_cornerPixmapIsDirty = true; - markDirty(DirtyMaterial); - } -} - -void QSGSoftwareRectangleNode::setPenColor(const QColor &color) -{ - if (m_penColor != color) { - m_penColor = color; - m_cornerPixmapIsDirty = true; - markDirty(DirtyMaterial); - } -} - -void QSGSoftwareRectangleNode::setPenWidth(qreal width) -{ - if (m_penWidth != width) { - m_penWidth = width; - m_cornerPixmapIsDirty = true; - markDirty(DirtyMaterial); - } -} - -//Move first stop by pos relative to seconds -static QGradientStop interpolateStop(const QGradientStop &firstStop, const QGradientStop &secondStop, double newPos) -{ - double distance = secondStop.first - firstStop.first; - double distanceDelta = newPos - firstStop.first; - double modifierValue = distanceDelta / distance; - int redDelta = (secondStop.second.red() - firstStop.second.red()) * modifierValue; - int greenDelta = (secondStop.second.green() - firstStop.second.green()) * modifierValue; - int blueDelta = (secondStop.second.blue() - firstStop.second.blue()) * modifierValue; - int alphaDelta = (secondStop.second.alpha() - firstStop.second.alpha()) * modifierValue; - - QGradientStop newStop; - newStop.first = newPos; - newStop.second = QColor(firstStop.second.red() + redDelta, - firstStop.second.green() + greenDelta, - firstStop.second.blue() + blueDelta, - firstStop.second.alpha() + alphaDelta); - - return newStop; -} - -void QSGSoftwareRectangleNode::setGradientStops(const QGradientStops &stops) -{ - //normalize stops - bool needsNormalization = false; - foreach (const QGradientStop &stop, stops) { - if (stop.first < 0.0 || stop.first > 1.0) { - needsNormalization = true; - continue; - } - } - - if (needsNormalization) { - QGradientStops normalizedStops; - if (stops.count() == 1) { - //If there is only one stop, then the position does not matter - //It is just treated as a color - QGradientStop stop = stops.at(0); - stop.first = 0.0; - normalizedStops.append(stop); - } else { - //Clip stops to only the first below 0.0 and above 1.0 - int below = -1; - int above = -1; - QVector between; - for (int i = 0; i < stops.count(); ++i) { - if (stops.at(i).first < 0.0) { - below = i; - } else if (stops.at(i).first > 1.0) { - above = i; - break; - } else { - between.append(i); - } - } - - //Interpoloate new color values for above and below - if (below != -1 ) { - //If there are more than one stops left, interpolate - if (below + 1 < stops.count()) { - normalizedStops.append(interpolateStop(stops.at(below), stops.at(below + 1), 0.0)); - } else { - QGradientStop singleStop; - singleStop.first = 0.0; - singleStop.second = stops.at(below).second; - normalizedStops.append(singleStop); - } - } - - for (int i = 0; i < between.count(); ++i) - normalizedStops.append(stops.at(between.at(i))); - - if (above != -1) { - //If there stops before above, interpolate - if (above >= 1) { - normalizedStops.append(interpolateStop(stops.at(above), stops.at(above - 1), 1.0)); - } else { - QGradientStop singleStop; - singleStop.first = 1.0; - singleStop.second = stops.at(above).second; - normalizedStops.append(singleStop); - } - } - } - - m_stops = normalizedStops; - - } else { - m_stops = stops; - } - m_cornerPixmapIsDirty = true; - markDirty(DirtyMaterial); -} - -void QSGSoftwareRectangleNode::setRadius(qreal radius) -{ - if (m_radius != radius) { - m_radius = radius; - m_cornerPixmapIsDirty = true; - markDirty(DirtyMaterial); - } -} - -void QSGSoftwareRectangleNode::setAligned(bool /*aligned*/) -{ -} - -void QSGSoftwareRectangleNode::update() -{ - if (!m_penWidth || m_penColor == Qt::transparent) { - m_pen = Qt::NoPen; - } else { - m_pen = QPen(m_penColor); - m_pen.setWidthF(m_penWidth); - } - - if (!m_stops.isEmpty()) { - QLinearGradient gradient(QPoint(0,0), QPoint(0,1)); - gradient.setStops(m_stops); - gradient.setCoordinateMode(QGradient::ObjectBoundingMode); - m_brush = QBrush(gradient); - } else { - m_brush = QBrush(m_color); - } - - if (m_cornerPixmapIsDirty) { - generateCornerPixmap(); - m_cornerPixmapIsDirty = false; - } -} - -void QSGSoftwareRectangleNode::paint(QPainter *painter) -{ - //We can only check for a device pixel ratio change when we know what - //paint device is being used. - if (painter->device()->devicePixelRatio() != m_devicePixelRatio) { - m_devicePixelRatio = painter->device()->devicePixelRatio(); - generateCornerPixmap(); - } - - if (painter->transform().isRotating()) { - //Rotated rectangles lose the benefits of direct rendering, and have poor rendering - //quality when using only blits and fills. - - if (m_radius == 0 && m_penWidth == 0) { - //Non-Rounded Rects without borders (fall back to drawRect) - //Most common case - painter->setPen(Qt::NoPen); - painter->setBrush(m_brush); - painter->drawRect(m_rect); - } else { - //Rounded Rects and Rects with Borders - //Avoids broken behaviors of QPainter::drawRect/roundedRect - QPixmap pixmap = QPixmap(m_rect.width() * m_devicePixelRatio, m_rect.height() * m_devicePixelRatio); - pixmap.fill(Qt::transparent); - pixmap.setDevicePixelRatio(m_devicePixelRatio); - QPainter pixmapPainter(&pixmap); - paintRectangle(&pixmapPainter, QRect(0, 0, m_rect.width(), m_rect.height())); - - QPainter::RenderHints previousRenderHints = painter->renderHints(); - painter->setRenderHint(QPainter::SmoothPixmapTransform, true); - painter->drawPixmap(m_rect, pixmap); - painter->setRenderHints(previousRenderHints); - } - - - } else { - //Paint directly - paintRectangle(painter, m_rect); - } - -} - -bool QSGSoftwareRectangleNode::isOpaque() const -{ - if (m_radius > 0.0f) - return false; - if (m_color.alpha() < 255) - return false; - if (m_penWidth > 0.0f && m_penColor.alpha() < 255) - return false; - if (m_stops.count() > 0) { - foreach (QGradientStop stop, m_stops) { - if (stop.second.alpha() < 255) - return false; - } - } - - return true; -} - -QRectF QSGSoftwareRectangleNode::rect() const -{ - //TODO: double check that this is correct. - return m_rect; -} - -void QSGSoftwareRectangleNode::paintRectangle(QPainter *painter, const QRect &rect) -{ - //Radius should never exceeds half of the width or half of the height - int radius = qFloor(qMin(qMin(rect.width(), rect.height()) * 0.5, m_radius)); - - QPainter::RenderHints previousRenderHints = painter->renderHints(); - painter->setRenderHint(QPainter::Antialiasing, false); - - if (m_penWidth > 0) { - //Fill border Rects - - //Borders can not be more than half the height/width of a rect - double borderWidth = qMin(m_penWidth, rect.width() * 0.5); - double borderHeight = qMin(m_penWidth, rect.height() * 0.5); - - - - if (borderWidth > radius) { - //4 Rects - QRectF borderTopOutside(QPointF(rect.x() + radius, rect.y()), - QPointF(rect.x() + rect.width() - radius, rect.y() + radius)); - QRectF borderTopInside(QPointF(rect.x() + borderWidth, rect.y() + radius), - QPointF(rect.x() + rect.width() - borderWidth, rect.y() + borderHeight)); - QRectF borderBottomOutside(QPointF(rect.x() + radius, rect.y() + rect.height() - radius), - QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height())); - QRectF borderBottomInside(QPointF(rect.x() + borderWidth, rect.y() + rect.height() - borderHeight), - QPointF(rect.x() + rect.width() - borderWidth, rect.y() + rect.height() - radius)); - - if (borderTopOutside.isValid()) - painter->fillRect(borderTopOutside, m_penColor); - if (borderTopInside.isValid()) - painter->fillRect(borderTopInside, m_penColor); - if (borderBottomOutside.isValid()) - painter->fillRect(borderBottomOutside, m_penColor); - if (borderBottomInside.isValid()) - painter->fillRect(borderBottomInside, m_penColor); - - } else { - //2 Rects - QRectF borderTop(QPointF(rect.x() + radius, rect.y()), - QPointF(rect.x() + rect.width() - radius, rect.y() + borderHeight)); - QRectF borderBottom(QPointF(rect.x() + radius, rect.y() + rect.height() - borderHeight), - QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height())); - if (borderTop.isValid()) - painter->fillRect(borderTop, m_penColor); - if (borderBottom.isValid()) - painter->fillRect(borderBottom, m_penColor); - } - QRectF borderLeft(QPointF(rect.x(), rect.y() + radius), - QPointF(rect.x() + borderWidth, rect.y() + rect.height() - radius)); - QRectF borderRight(QPointF(rect.x() + rect.width() - borderWidth, rect.y() + radius), - QPointF(rect.x() + rect.width(), rect.y() + rect.height() - radius)); - if (borderLeft.isValid()) - painter->fillRect(borderLeft, m_penColor); - if (borderRight.isValid()) - painter->fillRect(borderRight, m_penColor); - } - - - if (radius > 0) { - - if (radius * 2 >= rect.width() && radius * 2 >= rect.height()) { - //Blit whole pixmap for circles - painter->drawPixmap(rect, m_cornerPixmap, m_cornerPixmap.rect()); - } else { - - //blit 4 corners to border - int scaledRadius = radius * m_devicePixelRatio; - QRectF topLeftCorner(QPointF(rect.x(), rect.y()), - QPointF(rect.x() + radius, rect.y() + radius)); - painter->drawPixmap(topLeftCorner, m_cornerPixmap, QRectF(0, 0, scaledRadius, scaledRadius)); - QRectF topRightCorner(QPointF(rect.x() + rect.width() - radius, rect.y()), - QPointF(rect.x() + rect.width(), rect.y() + radius)); - painter->drawPixmap(topRightCorner, m_cornerPixmap, QRectF(scaledRadius, 0, scaledRadius, scaledRadius)); - QRectF bottomLeftCorner(QPointF(rect.x(), rect.y() + rect.height() - radius), - QPointF(rect.x() + radius, rect.y() + rect.height())); - painter->drawPixmap(bottomLeftCorner, m_cornerPixmap, QRectF(0, scaledRadius, scaledRadius, scaledRadius)); - QRectF bottomRightCorner(QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height() - radius), - QPointF(rect.x() + rect.width(), rect.y() + rect.height())); - painter->drawPixmap(bottomRightCorner, m_cornerPixmap, QRectF(scaledRadius, scaledRadius, scaledRadius, scaledRadius)); - - } - - } - - QRectF brushRect = QRectF(rect).marginsRemoved(QMarginsF(m_penWidth, m_penWidth, m_penWidth, m_penWidth)); - if (brushRect.width() < 0) - brushRect.setWidth(0); - if (brushRect.height() < 0) - brushRect.setHeight(0); - double innerRectRadius = qMax(0.0, radius - m_penWidth); - - //If not completely transparent or has a gradient - if (m_color.alpha() > 0 || !m_stops.empty()) { - if (innerRectRadius > 0) { - //Rounded Rect - if (m_stops.empty()) { - //Rounded Rects without gradient need 3 blits - QRectF centerRect(QPointF(brushRect.x() + innerRectRadius, brushRect.y()), - QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + brushRect.height())); - painter->fillRect(centerRect, m_color); - QRectF leftRect(QPointF(brushRect.x(), brushRect.y() + innerRectRadius), - QPointF(brushRect.x() + innerRectRadius, brushRect.y() + brushRect.height() - innerRectRadius)); - painter->fillRect(leftRect, m_color); - QRectF rightRect(QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + innerRectRadius), - QPointF(brushRect.x() + brushRect.width(), brushRect.y() + brushRect.height() - innerRectRadius)); - painter->fillRect(rightRect, m_color); - } else { - //Rounded Rect with gradient (slow) - painter->setPen(Qt::NoPen); - painter->setBrush(m_brush); - painter->drawRoundedRect(brushRect, innerRectRadius, innerRectRadius); - } - } else { - //non-rounded rects only need 1 blit - painter->fillRect(brushRect, m_brush); - } - } - - painter->setRenderHints(previousRenderHints); -} - -void QSGSoftwareRectangleNode::generateCornerPixmap() -{ - //Generate new corner Pixmap - int radius = qFloor(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius)); - - m_cornerPixmap = QPixmap(radius * 2 * m_devicePixelRatio, radius * 2 * m_devicePixelRatio); - m_cornerPixmap.setDevicePixelRatio(m_devicePixelRatio); - m_cornerPixmap.fill(Qt::transparent); - - if (radius > 0) { - QPainter cornerPainter(&m_cornerPixmap); - cornerPainter.setRenderHint(QPainter::Antialiasing); - cornerPainter.setCompositionMode(QPainter::CompositionMode_Source); - - //Paint outer cicle - if (m_penWidth > 0) { - cornerPainter.setPen(Qt::NoPen); - cornerPainter.setBrush(m_penColor); - cornerPainter.drawRoundedRect(QRectF(0, 0, radius * 2, radius *2), radius, radius); - } - - //Paint inner circle - if (radius > m_penWidth) { - cornerPainter.setPen(Qt::NoPen); - if (m_stops.isEmpty()) - cornerPainter.setBrush(m_brush); - else - cornerPainter.setBrush(Qt::transparent); - - QMarginsF adjustmentMargins(m_penWidth, m_penWidth, m_penWidth, m_penWidth); - QRectF cornerCircleRect = QRectF(0, 0, radius * 2, radius * 2).marginsRemoved(adjustmentMargins); - cornerPainter.drawRoundedRect(cornerCircleRect, radius, radius); - } - cornerPainter.end(); - } -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h deleted file mode 100644 index 9cc0823325..0000000000 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#ifndef QSGSOFTWARERECTANGLENODE_H -#define QSGSOFTWARERECTANGLENODE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGSoftwareRectangleNode : public QSGRectangleNode -{ -public: - QSGSoftwareRectangleNode(); - - void setRect(const QRectF &rect) override; - void setColor(const QColor &color) override; - void setPenColor(const QColor &color) override; - void setPenWidth(qreal width) override; - void setGradientStops(const QGradientStops &stops) override; - void setRadius(qreal radius) override; - void setAntialiasing(bool antialiasing) override { Q_UNUSED(antialiasing) } - void setAligned(bool aligned) override; - - void update() override; - - void paint(QPainter *); - - bool isOpaque() const; - QRectF rect() const; -private: - void paintRectangle(QPainter *painter, const QRect &rect); - void generateCornerPixmap(); - - QRect m_rect; - QColor m_color; - QColor m_penColor; - double m_penWidth; - QGradientStops m_stops; - double m_radius; - QPen m_pen; - QBrush m_brush; - - bool m_cornerPixmapIsDirty; - QPixmap m_cornerPixmap; - - int m_devicePixelRatio; -}; - -QT_END_NAMESPACE - -#endif // QSGSOFTWARERECTANGLENODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 063242c63b..571f242b67 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -39,10 +39,10 @@ #include "qsgsoftwarerenderablenode_p.h" -#include "qsgsoftwareimagenode_p.h" -#include "qsgsoftwarerectanglenode_p.h" +#include "qsgsoftwareinternalimagenode_p.h" +#include "qsgsoftwareinternalrectanglenode_p.h" #include "qsgsoftwareglyphnode_p.h" -#include "qsgsoftwareninepatchnode_p.h" +#include "qsgsoftwarepublicnodes_p.h" #include "qsgsoftwarepainternode_p.h" #include "qsgsoftwarepixmaptexture_p.h" @@ -68,13 +68,13 @@ QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *nod m_handle.simpleTextureNode = static_cast(node); break; case QSGSoftwareRenderableNode::Image: - m_handle.imageNode = static_cast(node); + m_handle.imageNode = static_cast(node); break; case QSGSoftwareRenderableNode::Painter: m_handle.painterNode = static_cast(node); break; case QSGSoftwareRenderableNode::Rectangle: - m_handle.rectangleNode = static_cast(node); + m_handle.rectangleNode = static_cast(node); break; case QSGSoftwareRenderableNode::Glyph: m_handle.glpyhNode = static_cast(node); @@ -82,6 +82,12 @@ QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *nod case QSGSoftwareRenderableNode::NinePatch: m_handle.ninePatchNode = static_cast(node); break; + case QSGSoftwareRenderableNode::SimpleRectangle: + m_handle.simpleRectangleNode = static_cast(node); + break; + case QSGSoftwareRenderableNode::SimpleImage: + m_handle.simpleImageNode = static_cast(node); + break; case QSGSoftwareRenderableNode::Invalid: m_handle.simpleRectNode = nullptr; break; @@ -151,6 +157,22 @@ void QSGSoftwareRenderableNode::update() boundingRect = m_handle.ninePatchNode->bounds().toRect(); break; + case QSGSoftwareRenderableNode::SimpleRectangle: + if (m_handle.simpleRectangleNode->color().alpha() == 255 && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.simpleRectangleNode->rect().toRect(); + break; + case QSGSoftwareRenderableNode::SimpleImage: + if (!m_handle.simpleImageNode->texture()->hasAlphaChannel() && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.simpleImageNode->rect().toRect(); + break; default: break; } @@ -223,6 +245,12 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu case QSGSoftwareRenderableNode::NinePatch: m_handle.ninePatchNode->paint(painter); break; + case QSGSoftwareRenderableNode::SimpleRectangle: + static_cast(m_handle.simpleRectangleNode)->paint(painter); + break; + case QSGSoftwareRenderableNode::SimpleImage: + static_cast(m_handle.simpleImageNode)->paint(painter); + break; default: break; } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h index 9a5e0a5683..d1a71cd580 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h @@ -54,15 +54,17 @@ #include #include #include +#include +#include +#include QT_BEGIN_NAMESPACE -class QSGNode; class QSGSimpleRectNode; class QSGSimpleTextureNode; -class QSGSoftwareImageNode; +class QSGSoftwareInternalImageNode; class QSGSoftwarePainterNode; -class QSGSoftwareRectangleNode; +class QSGSoftwareInternalRectangleNode; class QSGSoftwareGlyphNode; class QSGSoftwareNinePatchNode; @@ -77,7 +79,9 @@ public: Painter, Rectangle, Glyph, - NinePatch + NinePatch, + SimpleRectangle, + SimpleImage }; QSGSoftwareRenderableNode(NodeType type, QSGNode *node); @@ -112,11 +116,13 @@ private: union RenderableNodeHandle { QSGSimpleRectNode *simpleRectNode; QSGSimpleTextureNode *simpleTextureNode; - QSGSoftwareImageNode *imageNode; + QSGSoftwareInternalImageNode *imageNode; QSGSoftwarePainterNode *painterNode; - QSGSoftwareRectangleNode *rectangleNode; + QSGSoftwareInternalRectangleNode *rectangleNode; QSGSoftwareGlyphNode *glpyhNode; QSGSoftwareNinePatchNode *ninePatchNode; + QSGRectangleNode *simpleRectangleNode; + QSGImageNode *simpleImageNode; }; const NodeType m_nodeType; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp index 3d5fe21f7c..1681f3c06b 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp @@ -40,10 +40,10 @@ #include "qsgsoftwarerenderablenodeupdater_p.h" #include "qsgabstractsoftwarerenderer_p.h" -#include "qsgsoftwareimagenode_p.h" -#include "qsgsoftwarerectanglenode_p.h" +#include "qsgsoftwareinternalimagenode_p.h" +#include "qsgsoftwareinternalrectanglenode_p.h" #include "qsgsoftwareglyphnode_p.h" -#include "qsgsoftwareninepatchnode_p.h" +#include "qsgsoftwarepublicnodes_p.h" #include "qsgsoftwarepainternode_p.h" #include "qsgsoftwarepixmaptexture_p.h" @@ -100,6 +100,12 @@ bool QSGSoftwareRenderableNodeUpdater::visit(QSGGeometryNode *node) return updateRenderableNode(QSGSoftwareRenderableNode::SimpleRect, rectNode); } else if (QSGSimpleTextureNode *tn = dynamic_cast(node)) { return updateRenderableNode(QSGSoftwareRenderableNode::SimpleTexture, tn); + } else if (QSGNinePatchNode *nn = dynamic_cast(node)) { + return updateRenderableNode(QSGSoftwareRenderableNode::NinePatch, nn); + } else if (QSGRectangleNode *rn = dynamic_cast(node)) { + return updateRenderableNode(QSGSoftwareRenderableNode::SimpleRectangle, rn); + } else if (QSGImageNode *n = dynamic_cast(node)) { + return updateRenderableNode(QSGSoftwareRenderableNode::SimpleImage, n); } else { // We dont know, so skip return false; @@ -122,12 +128,12 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGOpacityNode *) m_opacityState.pop(); } -bool QSGSoftwareRenderableNodeUpdater::visit(QSGImageNode *node) +bool QSGSoftwareRenderableNodeUpdater::visit(QSGInternalImageNode *node) { return updateRenderableNode(QSGSoftwareRenderableNode::Image, node); } -void QSGSoftwareRenderableNodeUpdater::endVisit(QSGImageNode *) +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGInternalImageNode *) { } @@ -140,12 +146,12 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGPainterNode *) { } -bool QSGSoftwareRenderableNodeUpdater::visit(QSGRectangleNode *node) +bool QSGSoftwareRenderableNodeUpdater::visit(QSGInternalRectangleNode *node) { return updateRenderableNode(QSGSoftwareRenderableNode::Rectangle, node); } -void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRectangleNode *) +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGInternalRectangleNode *) { } @@ -158,15 +164,6 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGGlyphNode *) { } -bool QSGSoftwareRenderableNodeUpdater::visit(QSGNinePatchNode *node) -{ - return updateRenderableNode(QSGSoftwareRenderableNode::NinePatch, node); -} - -void QSGSoftwareRenderableNodeUpdater::endVisit(QSGNinePatchNode *) -{ -} - bool QSGSoftwareRenderableNodeUpdater::visit(QSGRootNode *node) { m_stateMap[node] = currentState(node); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h index 562d15769e..97492d4347 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h @@ -76,16 +76,14 @@ public: void endVisit(QSGGeometryNode *) override; bool visit(QSGOpacityNode *) override; void endVisit(QSGOpacityNode *) override; - bool visit(QSGImageNode *) override; - void endVisit(QSGImageNode *) override; + bool visit(QSGInternalImageNode *) override; + void endVisit(QSGInternalImageNode *) override; bool visit(QSGPainterNode *) override; void endVisit(QSGPainterNode *) override; - bool visit(QSGRectangleNode *) override; - void endVisit(QSGRectangleNode *) override; + bool visit(QSGInternalRectangleNode *) override; + void endVisit(QSGInternalRectangleNode *) override; bool visit(QSGGlyphNode *) override; void endVisit(QSGGlyphNode *) override; - bool visit(QSGNinePatchNode *) override; - void endVisit(QSGNinePatchNode *) override; bool visit(QSGRootNode *) override; void endVisit(QSGRootNode *) override; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp index af81ff61c3..ede2005918 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp @@ -41,10 +41,10 @@ #include "qsgsoftwarerenderablenode_p.h" #include "qsgabstractsoftwarerenderer_p.h" -#include "qsgsoftwareimagenode_p.h" -#include "qsgsoftwarerectanglenode_p.h" +#include "qsgsoftwareinternalimagenode_p.h" +#include "qsgsoftwareinternalrectanglenode_p.h" #include "qsgsoftwareglyphnode_p.h" -#include "qsgsoftwareninepatchnode_p.h" +#include "qsgsoftwarepublicnodes_p.h" #include "qsgsoftwarepainternode_p.h" #include "qsgsoftwarepixmaptexture_p.h" @@ -95,12 +95,12 @@ void QSGSoftwareRenderListBuilder::endVisit(QSGOpacityNode *) { } -bool QSGSoftwareRenderListBuilder::visit(QSGImageNode *node) +bool QSGSoftwareRenderListBuilder::visit(QSGInternalImageNode *node) { return addRenderableNode(node); } -void QSGSoftwareRenderListBuilder::endVisit(QSGImageNode *) +void QSGSoftwareRenderListBuilder::endVisit(QSGInternalImageNode *) { } @@ -113,12 +113,12 @@ void QSGSoftwareRenderListBuilder::endVisit(QSGPainterNode *) { } -bool QSGSoftwareRenderListBuilder::visit(QSGRectangleNode *node) +bool QSGSoftwareRenderListBuilder::visit(QSGInternalRectangleNode *node) { return addRenderableNode(node); } -void QSGSoftwareRenderListBuilder::endVisit(QSGRectangleNode *) +void QSGSoftwareRenderListBuilder::endVisit(QSGInternalRectangleNode *) { } @@ -131,15 +131,6 @@ void QSGSoftwareRenderListBuilder::endVisit(QSGGlyphNode *) { } -bool QSGSoftwareRenderListBuilder::visit(QSGNinePatchNode *node) -{ - return addRenderableNode(node); -} - -void QSGSoftwareRenderListBuilder::endVisit(QSGNinePatchNode *) -{ -} - bool QSGSoftwareRenderListBuilder::visit(QSGRootNode *) { return true; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h index 94b563564d..ce538f835f 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h @@ -70,16 +70,14 @@ public: void endVisit(QSGGeometryNode *) override; bool visit(QSGOpacityNode *) override; void endVisit(QSGOpacityNode *) override; - bool visit(QSGImageNode *) override; - void endVisit(QSGImageNode *) override; + bool visit(QSGInternalImageNode *) override; + void endVisit(QSGInternalImageNode *) override; bool visit(QSGPainterNode *) override; void endVisit(QSGPainterNode *) override; - bool visit(QSGRectangleNode *) override; - void endVisit(QSGRectangleNode *) override; + bool visit(QSGInternalRectangleNode *) override; + void endVisit(QSGInternalRectangleNode *) override; bool visit(QSGGlyphNode *) override; void endVisit(QSGGlyphNode *) override; - bool visit(QSGNinePatchNode *) override; - void endVisit(QSGNinePatchNode *) override; bool visit(QSGRootNode *) override; void endVisit(QSGRootNode *) override; diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri index b8cdbc4a25..a8ba77c147 100644 --- a/src/quick/scenegraph/adaptations/software/software.pri +++ b/src/quick/scenegraph/adaptations/software/software.pri @@ -6,10 +6,10 @@ SOURCES += \ $$PWD/qsgsoftwarecontext.cpp \ $$PWD/qsgabstractsoftwarerenderer.cpp \ $$PWD/qsgsoftwareglyphnode.cpp \ - $$PWD/qsgsoftwareimagenode.cpp \ - $$PWD/qsgsoftwareninepatchnode.cpp \ + $$PWD/qsgsoftwareinternalimagenode.cpp \ + $$PWD/qsgsoftwarepublicnodes.cpp \ $$PWD/qsgsoftwarepainternode.cpp \ - $$PWD/qsgsoftwarerectanglenode.cpp \ + $$PWD/qsgsoftwareinternalrectanglenode.cpp \ $$PWD/qsgsoftwarepixmaprenderer.cpp \ $$PWD/qsgsoftwarepixmaptexture.cpp \ $$PWD/qsgsoftwarerenderablenode.cpp \ @@ -24,12 +24,12 @@ HEADERS += \ $$PWD/qsgsoftwarecontext_p.h \ $$PWD/qsgabstractsoftwarerenderer_p.h \ $$PWD/qsgsoftwareglyphnode_p.h \ - $$PWD/qsgsoftwareimagenode_p.h \ - $$PWD/qsgsoftwareninepatchnode_p.h \ + $$PWD/qsgsoftwareinternalimagenode_p.h \ + $$PWD/qsgsoftwarepublicnodes_p.h \ $$PWD/qsgsoftwarepainternode_p.h \ $$PWD/qsgsoftwarepixmaprenderer_p.h \ $$PWD/qsgsoftwarepixmaptexture_p.h \ - $$PWD/qsgsoftwarerectanglenode_p.h \ + $$PWD/qsgsoftwareinternalrectanglenode_p.h \ $$PWD/qsgsoftwarerenderablenode_p.h \ $$PWD/qsgsoftwarerenderablenodeupdater_p.h \ $$PWD/qsgsoftwarerenderer_p.h \ diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp index 7ec72a6e10..239557d527 100644 --- a/src/quick/scenegraph/coreapi/qsggeometry.cpp +++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp @@ -726,6 +726,27 @@ void QSGGeometry::updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, v[3].ty = textureRect.bottom(); } +/*! + Updates the geometry \a g with the coordinates in \a rect. + + The function assumes the geometry object contains a single triangle strip + of QSGGeometry::ColoredPoint2D vertices + */ +void QSGGeometry::updateColoredRectGeometry(QSGGeometry *g, const QRectF &rect) +{ + ColoredPoint2D *v = g->vertexDataAsColoredPoint2D(); + v[0].x = rect.left(); + v[0].y = rect.top(); + + v[1].x = rect.left(); + v[1].y = rect.bottom(); + + v[2].x = rect.right(); + v[2].y = rect.top(); + + v[3].x = rect.right(); + v[3].y = rect.bottom(); +} /*! diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h index 1f54b7d81b..ae7b2f494c 100644 --- a/src/quick/scenegraph/coreapi/qsggeometry.h +++ b/src/quick/scenegraph/coreapi/qsggeometry.h @@ -182,6 +182,7 @@ public: static void updateRectGeometry(QSGGeometry *g, const QRectF &rect); static void updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &sourceRect); + static void updateColoredRectGeometry(QSGGeometry *g, const QRectF &rect); void setIndexDataPattern(DataPattern p); DataPattern indexDataPattern() const { return DataPattern(m_index_usage_pattern); } diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index 92f783d500..c0967bcb09 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -1466,8 +1466,6 @@ void QSGNodeVisitor::visitChildren(QSGNode *n) visitNode(c); } - - #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QSGGeometryNode *n) { diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 179ec3e3fa..bdb8ebb0f1 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -76,11 +76,10 @@ class TextureReference; class QSGDistanceFieldGlyphCacheManager; class QSGDistanceFieldGlyphNode; class QOpenGLContext; -class QSGImageNode; +class QSGInternalImageNode; class QSGPainterNode; -class QSGRectangleNode; +class QSGInternalRectangleNode; class QSGGlyphNode; -class QSGNinePatchNode; class QSGRootNode; class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx @@ -97,16 +96,14 @@ public: virtual void endVisit(QSGGeometryNode *) = 0; virtual bool visit(QSGOpacityNode *) = 0; virtual void endVisit(QSGOpacityNode *) = 0; - virtual bool visit(QSGImageNode *) = 0; - virtual void endVisit(QSGImageNode *) = 0; + virtual bool visit(QSGInternalImageNode *) = 0; + virtual void endVisit(QSGInternalImageNode *) = 0; virtual bool visit(QSGPainterNode *) = 0; virtual void endVisit(QSGPainterNode *) = 0; - virtual bool visit(QSGRectangleNode *) = 0; - virtual void endVisit(QSGRectangleNode *) = 0; + virtual bool visit(QSGInternalRectangleNode *) = 0; + virtual void endVisit(QSGInternalRectangleNode *) = 0; virtual bool visit(QSGGlyphNode *) = 0; virtual void endVisit(QSGGlyphNode *) = 0; - virtual bool visit(QSGNinePatchNode *) = 0; - virtual void endVisit(QSGNinePatchNode *) = 0; virtual bool visit(QSGRootNode *) = 0; virtual void endVisit(QSGRootNode *) = 0; @@ -122,7 +119,7 @@ public: virtual void accept(QSGNodeVisitorEx *) = 0; }; -class Q_QUICK_PRIVATE_EXPORT QSGRectangleNode : public QSGVisitableNode +class Q_QUICK_PRIVATE_EXPORT QSGInternalRectangleNode : public QSGVisitableNode { public: virtual void setRect(const QRectF &rect) = 0; @@ -140,7 +137,7 @@ public: }; -class Q_QUICK_PRIVATE_EXPORT QSGImageNode : public QSGVisitableNode +class Q_QUICK_PRIVATE_EXPORT QSGInternalImageNode : public QSGVisitableNode { public: virtual void setTargetRect(const QRectF &rect) = 0; @@ -186,19 +183,6 @@ public: virtual void accept(QSGNodeVisitorEx *visitor) { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); } }; -class Q_QUICK_PRIVATE_EXPORT QSGNinePatchNode : public QSGVisitableNode -{ -public: - virtual void setTexture(QSGTexture *texture) = 0; - virtual void setBounds(const QRectF &bounds) = 0; - virtual void setDevicePixelRatio(qreal ratio) = 0; - virtual void setPadding(qreal left, qreal top, qreal right, qreal bottom) = 0; - - virtual void update() = 0; - - virtual void accept(QSGNodeVisitorEx *visitor) { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); } -}; - class Q_QUICK_EXPORT QSGLayer : public QSGDynamicTexture { Q_OBJECT diff --git a/src/quick/scenegraph/qsgbasicimagenode.cpp b/src/quick/scenegraph/qsgbasicimagenode.cpp deleted file mode 100644 index 24077cc947..0000000000 --- a/src/quick/scenegraph/qsgbasicimagenode.cpp +++ /dev/null @@ -1,559 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qsgbasicimagenode_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -namespace -{ - struct SmoothVertex - { - float x, y, u, v; - float dx, dy, du, dv; - }; - - const QSGGeometry::AttributeSet &smoothAttributeSet() - { - static QSGGeometry::Attribute data[] = { - QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION), - QSGGeometry::Attribute::createWithSemantic(1, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD), - QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD1), - QSGGeometry::Attribute::createWithSemantic(3, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD2) - }; - static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data }; - return attrs; - } -} - -QSGBasicImageNode::QSGBasicImageNode() - : m_innerSourceRect(0, 0, 1, 1) - , m_subSourceRect(0, 0, 1, 1) - , m_antialiasing(false) - , m_mirror(false) - , m_dirtyGeometry(false) - , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) - , m_dynamicTexture(nullptr) -{ - setGeometry(&m_geometry); - -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("image")); -#endif -} - -void QSGBasicImageNode::setTargetRect(const QRectF &rect) -{ - if (rect == m_targetRect) - return; - m_targetRect = rect; - m_dirtyGeometry = true; -} - -void QSGBasicImageNode::setInnerTargetRect(const QRectF &rect) -{ - if (rect == m_innerTargetRect) - return; - m_innerTargetRect = rect; - m_dirtyGeometry = true; -} - -void QSGBasicImageNode::setInnerSourceRect(const QRectF &rect) -{ - if (rect == m_innerSourceRect) - return; - m_innerSourceRect = rect; - m_dirtyGeometry = true; -} - -void QSGBasicImageNode::setSubSourceRect(const QRectF &rect) -{ - if (rect == m_subSourceRect) - return; - m_subSourceRect = rect; - m_dirtyGeometry = true; -} - -void QSGBasicImageNode::setTexture(QSGTexture *texture) -{ - Q_ASSERT(texture); - - setMaterialTexture(texture); - updateMaterialBlending(); - - markDirty(DirtyMaterial); - - // Because the texture can be a different part of the atlas, we need to update it... - m_dirtyGeometry = true; -} - -void QSGBasicImageNode::setAntialiasing(bool antialiasing) -{ - if (antialiasing == m_antialiasing) - return; - m_antialiasing = antialiasing; - if (m_antialiasing) { - setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); - setFlag(OwnsGeometry, true); - } else { - setGeometry(&m_geometry); - setFlag(OwnsGeometry, false); - } - updateMaterialAntialiasing(); - m_dirtyGeometry = true; -} - -void QSGBasicImageNode::setMirror(bool mirror) -{ - if (mirror == m_mirror) - return; - m_mirror = mirror; - m_dirtyGeometry = true; -} - - -void QSGBasicImageNode::update() -{ - if (m_dirtyGeometry) - updateGeometry(); -} - -void QSGBasicImageNode::preprocess() -{ - bool doDirty = false; - QSGDynamicTexture *t = qobject_cast(materialTexture()); - if (t) { - doDirty = t->updateTexture(); - if (doDirty) { - // The geometry may need updating. This is expensive however, so do - // it only when something relevant has changed. - if (t != m_dynamicTexture - || t->textureSize() != m_dynamicTextureSize - || t->normalizedTextureSubRect() != m_dynamicTextureSubRect) { - updateGeometry(); - m_dynamicTextureSize = t->textureSize(); - m_dynamicTextureSubRect = t->normalizedTextureSubRect(); - } - } - } - m_dynamicTexture = t; - - if (updateMaterialBlending()) - doDirty = true; - - if (doDirty) - markDirty(DirtyMaterial); -} - -namespace { - struct X { float x, tx; }; - struct Y { float y, ty; }; -} - -static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight, - quint16 bottomLeft, quint16 bottomRight) -{ - *(*indices)++ = topLeft; - *(*indices)++ = bottomLeft; - *(*indices)++ = bottomRight; - *(*indices)++ = bottomRight; - *(*indices)++ = topRight; - *(*indices)++ = topLeft; -} - -QSGGeometry *QSGBasicImageNode::updateGeometry(const QRectF &targetRect, - const QRectF &innerTargetRect, - const QRectF &sourceRect, - const QRectF &innerSourceRect, - const QRectF &subSourceRect, - QSGGeometry *geometry, - bool mirror, - bool antialiasing) -{ - int floorLeft = qFloor(subSourceRect.left()); - int ceilRight = qCeil(subSourceRect.right()); - int floorTop = qFloor(subSourceRect.top()); - int ceilBottom = qCeil(subSourceRect.bottom()); - int hTiles = ceilRight - floorLeft; - int vTiles = ceilBottom - floorTop; - - int hCells = hTiles; - int vCells = vTiles; - if (innerTargetRect.width() == 0) - hCells = 0; - if (innerTargetRect.left() != targetRect.left()) - ++hCells; - if (innerTargetRect.right() != targetRect.right()) - ++hCells; - if (innerTargetRect.height() == 0) - vCells = 0; - if (innerTargetRect.top() != targetRect.top()) - ++vCells; - if (innerTargetRect.bottom() != targetRect.bottom()) - ++vCells; - QVarLengthArray xData(2 * hCells); - QVarLengthArray yData(2 * vCells); - X *xs = xData.data(); - Y *ys = yData.data(); - - if (innerTargetRect.left() != targetRect.left()) { - xs[0].x = targetRect.left(); - xs[0].tx = sourceRect.left(); - xs[1].x = innerTargetRect.left(); - xs[1].tx = innerSourceRect.left(); - xs += 2; - } - if (innerTargetRect.width() != 0) { - xs[0].x = innerTargetRect.left(); - xs[0].tx = innerSourceRect.x() + (subSourceRect.left() - floorLeft) * innerSourceRect.width(); - ++xs; - float b = innerTargetRect.width() / subSourceRect.width(); - float a = innerTargetRect.x() - subSourceRect.x() * b; - for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { - xs[0].x = xs[1].x = a + b * i; - xs[0].tx = innerSourceRect.right(); - xs[1].tx = innerSourceRect.left(); - xs += 2; - } - xs[0].x = innerTargetRect.right(); - xs[0].tx = innerSourceRect.x() + (subSourceRect.right() - ceilRight + 1) * innerSourceRect.width(); - ++xs; - } - if (innerTargetRect.right() != targetRect.right()) { - xs[0].x = innerTargetRect.right(); - xs[0].tx = innerSourceRect.right(); - xs[1].x = targetRect.right(); - xs[1].tx = sourceRect.right(); - xs += 2; - } - Q_ASSERT(xs == xData.data() + xData.size()); - if (mirror) { - float leftPlusRight = targetRect.left() + targetRect.right(); - int count = xData.size(); - xs = xData.data(); - for (int i = 0; i < count >> 1; ++i) - qSwap(xs[i], xs[count - 1 - i]); - for (int i = 0; i < count; ++i) - xs[i].x = leftPlusRight - xs[i].x; - } - - if (innerTargetRect.top() != targetRect.top()) { - ys[0].y = targetRect.top(); - ys[0].ty = sourceRect.top(); - ys[1].y = innerTargetRect.top(); - ys[1].ty = innerSourceRect.top(); - ys += 2; - } - if (innerTargetRect.height() != 0) { - ys[0].y = innerTargetRect.top(); - ys[0].ty = innerSourceRect.y() + (subSourceRect.top() - floorTop) * innerSourceRect.height(); - ++ys; - float b = innerTargetRect.height() / subSourceRect.height(); - float a = innerTargetRect.y() - subSourceRect.y() * b; - for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { - ys[0].y = ys[1].y = a + b * i; - ys[0].ty = innerSourceRect.bottom(); - ys[1].ty = innerSourceRect.top(); - ys += 2; - } - ys[0].y = innerTargetRect.bottom(); - ys[0].ty = innerSourceRect.y() + (subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height(); - ++ys; - } - if (innerTargetRect.bottom() != targetRect.bottom()) { - ys[0].y = innerTargetRect.bottom(); - ys[0].ty = innerSourceRect.bottom(); - ys[1].y = targetRect.bottom(); - ys[1].ty = sourceRect.bottom(); - ys += 2; - } - Q_ASSERT(ys == yData.data() + yData.size()); - - if (antialiasing) { - QSGGeometry *g = geometry; - Q_ASSERT(g); - - g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, - hCells * vCells * 6 + (hCells + vCells) * 12); - g->setDrawingMode(QSGGeometry::DrawTriangles); - SmoothVertex *vertices = reinterpret_cast(g->vertexData()); - memset(vertices, 0, g->vertexCount() * g->sizeOfVertex()); - quint16 *indices = g->indexDataAsUShort(); - - // The deltas are how much the fuzziness can reach into the image. - // Only the border vertices are moved by the vertex shader, so the fuzziness - // can't reach further into the image than the closest interior vertices. - float leftDx = xData.at(1).x - xData.at(0).x; - float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x; - float topDy = yData.at(1).y - yData.at(0).y; - float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y; - - float leftDu = xData.at(1).tx - xData.at(0).tx; - float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx; - float topDv = yData.at(1).ty - yData.at(0).ty; - float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty; - - if (hCells == 1) { - leftDx = rightDx *= 0.5f; - leftDu = rightDu *= 0.5f; - } - if (vCells == 1) { - topDy = bottomDy *= 0.5f; - topDv = bottomDv *= 0.5f; - } - - // This delta is how much the fuzziness can reach out from the image. - float delta = float(qAbs(targetRect.width()) < qAbs(targetRect.height()) - ? targetRect.width() : targetRect.height()) * 0.5f; - - quint16 index = 0; - ys = yData.data(); - for (int j = 0; j < vCells; ++j, ys += 2) { - xs = xData.data(); - bool isTop = j == 0; - bool isBottom = j == vCells - 1; - for (int i = 0; i < hCells; ++i, xs += 2) { - bool isLeft = i == 0; - bool isRight = i == hCells - 1; - - SmoothVertex *v = vertices + index; - - quint16 topLeft = index; - for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) { - v->x = xs[0].x; - v->u = xs[0].tx; - v->y = ys[0].y; - v->v = ys[0].ty; - } - - quint16 topRight = index; - for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) { - v->x = xs[1].x; - v->u = xs[1].tx; - v->y = ys[0].y; - v->v = ys[0].ty; - } - - quint16 bottomLeft = index; - for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) { - v->x = xs[0].x; - v->u = xs[0].tx; - v->y = ys[1].y; - v->v = ys[1].ty; - } - - quint16 bottomRight = index; - for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) { - v->x = xs[1].x; - v->u = xs[1].tx; - v->y = ys[1].y; - v->v = ys[1].ty; - } - - appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight); - - if (isTop) { - vertices[topLeft].dy = vertices[topRight].dy = topDy; - vertices[topLeft].dv = vertices[topRight].dv = topDv; - vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta; - appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight); - } - - if (isBottom) { - vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy; - vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv; - vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta; - appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1); - } - - if (isLeft) { - vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx; - vertices[topLeft].du = vertices[bottomLeft].du = leftDu; - vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta; - appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft); - } - - if (isRight) { - vertices[topRight].dx = vertices[bottomRight].dx = -rightDx; - vertices[topRight].du = vertices[bottomRight].du = -rightDu; - vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta; - appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1); - } - } - } - - Q_ASSERT(index == g->vertexCount()); - Q_ASSERT(indices - g->indexCount() == g->indexData()); - } else { - if (!geometry) { - geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), - hCells * vCells * 4, hCells * vCells * 6, - QSGGeometry::TypeUnsignedShort); - } else { - geometry->allocate(hCells * vCells * 4, hCells * vCells * 6); - } - geometry->setDrawingMode(QSGGeometry::DrawTriangles); - QSGGeometry::TexturedPoint2D *vertices = geometry->vertexDataAsTexturedPoint2D(); - ys = yData.data(); - for (int j = 0; j < vCells; ++j, ys += 2) { - xs = xData.data(); - for (int i = 0; i < hCells; ++i, xs += 2) { - vertices[0].x = vertices[2].x = xs[0].x; - vertices[0].tx = vertices[2].tx = xs[0].tx; - vertices[1].x = vertices[3].x = xs[1].x; - vertices[1].tx = vertices[3].tx = xs[1].tx; - - vertices[0].y = vertices[1].y = ys[0].y; - vertices[0].ty = vertices[1].ty = ys[0].ty; - vertices[2].y = vertices[3].y = ys[1].y; - vertices[2].ty = vertices[3].ty = ys[1].ty; - - vertices += 4; - } - } - - quint16 *indices = geometry->indexDataAsUShort(); - for (int i = 0; i < 4 * vCells * hCells; i += 4) - appendQuad(&indices, i, i + 1, i + 2, i + 3); - } - return geometry; -} - -void QSGBasicImageNode::updateGeometry() -{ - Q_ASSERT(!m_targetRect.isEmpty()); - const QSGTexture *t = materialTexture(); - if (!t) { - QSGGeometry *g = geometry(); - g->allocate(4); - g->setDrawingMode(QSGGeometry::DrawTriangleStrip); - memset(g->vertexData(), 0, g->sizeOfVertex() * 4); - } else { - QRectF sourceRect = t->normalizedTextureSubRect(); - - QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(), - sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(), - m_innerSourceRect.width() * sourceRect.width(), - m_innerSourceRect.height() * sourceRect.height()); - - bool hasMargins = m_targetRect != m_innerTargetRect; - - int floorLeft = qFloor(m_subSourceRect.left()); - int ceilRight = qCeil(m_subSourceRect.right()); - int floorTop = qFloor(m_subSourceRect.top()); - int ceilBottom = qCeil(m_subSourceRect.bottom()); - int hTiles = ceilRight - floorLeft; - int vTiles = ceilBottom - floorTop; - - bool hasTiles = hTiles != 1 || vTiles != 1; - bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1); - - // An image can be rendered as a single quad if: - // - There are no margins, and either: - // - the image isn't repeated - // - the source rectangle fills the entire texture so that texture wrapping can be used, - // and NPOT is supported - if (!hasMargins && (!hasTiles || (fullTexture && supportsWrap(t->textureSize())))) { - QRectF sr; - if (!fullTexture) { - sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(), - innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(), - m_subSourceRect.width() * innerSourceRect.width(), - m_subSourceRect.height() * innerSourceRect.height()); - } else { - sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop, - m_subSourceRect.width(), m_subSourceRect.height()); - } - if (m_mirror) { - qreal oldLeft = sr.left(); - sr.setLeft(sr.right()); - sr.setRight(oldLeft); - } - - if (m_antialiasing) { - QSGGeometry *g = geometry(); - Q_ASSERT(g != &m_geometry); - g->allocate(8, 14); - g->setDrawingMode(QSGGeometry::DrawTriangleStrip); - SmoothVertex *vertices = reinterpret_cast(g->vertexData()); - float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) - ? m_targetRect.width() : m_targetRect.height()) * 0.5f; - float sx = float(sr.width() / m_targetRect.width()); - float sy = float(sr.height() / m_targetRect.height()); - for (int d = -1; d <= 1; d += 2) { - for (int j = 0; j < 2; ++j) { - for (int i = 0; i < 2; ++i, ++vertices) { - vertices->x = m_targetRect.x() + i * m_targetRect.width(); - vertices->y = m_targetRect.y() + j * m_targetRect.height(); - vertices->u = sr.x() + i * sr.width(); - vertices->v = sr.y() + j * sr.height(); - vertices->dx = (i == 0 ? delta : -delta) * d; - vertices->dy = (j == 0 ? delta : -delta) * d; - vertices->du = (d < 0 ? 0 : vertices->dx * sx); - vertices->dv = (d < 0 ? 0 : vertices->dy * sy); - } - } - } - Q_ASSERT(vertices - g->vertexCount() == g->vertexData()); - static const quint16 indices[] = { - 0, 4, 1, 5, 3, 7, 2, 6, 0, 4, - 4, 6, 5, 7 - }; - Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices)); - memcpy(g->indexDataAsUShort(), indices, sizeof(indices)); - } else { - m_geometry.allocate(4); - m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip); - QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); - } - } else { - QSGGeometry *g = m_antialiasing ? geometry() : &m_geometry; - updateGeometry(m_targetRect, m_innerTargetRect, - sourceRect, innerSourceRect, m_subSourceRect, - g, m_mirror, m_antialiasing); - } - } - markDirty(DirtyGeometry); - m_dirtyGeometry = false; -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgbasicimagenode_p.h b/src/quick/scenegraph/qsgbasicimagenode_p.h deleted file mode 100644 index 4a96e1a9f6..0000000000 --- a/src/quick/scenegraph/qsgbasicimagenode_p.h +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#ifndef QSGBASICIMAGENODE_P_H -#define QSGBASICIMAGENODE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -QT_BEGIN_NAMESPACE - -class Q_QUICK_PRIVATE_EXPORT QSGBasicImageNode : public QSGImageNode -{ -public: - QSGBasicImageNode(); - - void setTargetRect(const QRectF &rect) override; - void setInnerTargetRect(const QRectF &rect) override; - void setInnerSourceRect(const QRectF &rect) override; - void setSubSourceRect(const QRectF &rect) override; - void setTexture(QSGTexture *texture) override; - void setAntialiasing(bool antialiasing) override; - void setMirror(bool mirror) override; - void update() override; - void preprocess() override; - - static QSGGeometry *updateGeometry(const QRectF &targetRect, - const QRectF &innerTargetRect, - const QRectF &sourceRect, - const QRectF &innerSourceRect, - const QRectF &subSourceRect, - QSGGeometry *geometry, - bool mirror = false, - bool antialiasing = false); - -protected: - virtual void updateMaterialAntialiasing() = 0; - virtual void setMaterialTexture(QSGTexture *texture) = 0; - virtual QSGTexture *materialTexture() const = 0; - virtual bool updateMaterialBlending() = 0; - virtual bool supportsWrap(const QSize &size) const = 0; - - void updateGeometry(); - - QRectF m_targetRect; - QRectF m_innerTargetRect; - QRectF m_innerSourceRect; - QRectF m_subSourceRect; - - uint m_antialiasing : 1; - uint m_mirror : 1; - uint m_dirtyGeometry : 1; - - QSGGeometry m_geometry; - - QSGDynamicTexture *m_dynamicTexture; - QSize m_dynamicTextureSize; - QRectF m_dynamicTextureSubRect; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp new file mode 100644 index 0000000000..685a51550d --- /dev/null +++ b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp @@ -0,0 +1,559 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgbasicinternalimagenode_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace +{ + struct SmoothVertex + { + float x, y, u, v; + float dx, dy, du, dv; + }; + + const QSGGeometry::AttributeSet &smoothAttributeSet() + { + static QSGGeometry::Attribute data[] = { + QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION), + QSGGeometry::Attribute::createWithSemantic(1, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD), + QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD1), + QSGGeometry::Attribute::createWithSemantic(3, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD2) + }; + static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data }; + return attrs; + } +} + +QSGBasicInternalImageNode::QSGBasicInternalImageNode() + : m_innerSourceRect(0, 0, 1, 1) + , m_subSourceRect(0, 0, 1, 1) + , m_antialiasing(false) + , m_mirror(false) + , m_dirtyGeometry(false) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) + , m_dynamicTexture(nullptr) +{ + setGeometry(&m_geometry); + +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("internalimage")); +#endif +} + +void QSGBasicInternalImageNode::setTargetRect(const QRectF &rect) +{ + if (rect == m_targetRect) + return; + m_targetRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setInnerTargetRect(const QRectF &rect) +{ + if (rect == m_innerTargetRect) + return; + m_innerTargetRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setInnerSourceRect(const QRectF &rect) +{ + if (rect == m_innerSourceRect) + return; + m_innerSourceRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setSubSourceRect(const QRectF &rect) +{ + if (rect == m_subSourceRect) + return; + m_subSourceRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setTexture(QSGTexture *texture) +{ + Q_ASSERT(texture); + + setMaterialTexture(texture); + updateMaterialBlending(); + + markDirty(DirtyMaterial); + + // Because the texture can be a different part of the atlas, we need to update it... + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setAntialiasing(bool antialiasing) +{ + if (antialiasing == m_antialiasing) + return; + m_antialiasing = antialiasing; + if (m_antialiasing) { + setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); + setFlag(OwnsGeometry, true); + } else { + setGeometry(&m_geometry); + setFlag(OwnsGeometry, false); + } + updateMaterialAntialiasing(); + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setMirror(bool mirror) +{ + if (mirror == m_mirror) + return; + m_mirror = mirror; + m_dirtyGeometry = true; +} + + +void QSGBasicInternalImageNode::update() +{ + if (m_dirtyGeometry) + updateGeometry(); +} + +void QSGBasicInternalImageNode::preprocess() +{ + bool doDirty = false; + QSGDynamicTexture *t = qobject_cast(materialTexture()); + if (t) { + doDirty = t->updateTexture(); + if (doDirty) { + // The geometry may need updating. This is expensive however, so do + // it only when something relevant has changed. + if (t != m_dynamicTexture + || t->textureSize() != m_dynamicTextureSize + || t->normalizedTextureSubRect() != m_dynamicTextureSubRect) { + updateGeometry(); + m_dynamicTextureSize = t->textureSize(); + m_dynamicTextureSubRect = t->normalizedTextureSubRect(); + } + } + } + m_dynamicTexture = t; + + if (updateMaterialBlending()) + doDirty = true; + + if (doDirty) + markDirty(DirtyMaterial); +} + +namespace { + struct X { float x, tx; }; + struct Y { float y, ty; }; +} + +static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight, + quint16 bottomLeft, quint16 bottomRight) +{ + *(*indices)++ = topLeft; + *(*indices)++ = bottomLeft; + *(*indices)++ = bottomRight; + *(*indices)++ = bottomRight; + *(*indices)++ = topRight; + *(*indices)++ = topLeft; +} + +QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, + const QRectF &innerTargetRect, + const QRectF &sourceRect, + const QRectF &innerSourceRect, + const QRectF &subSourceRect, + QSGGeometry *geometry, + bool mirror, + bool antialiasing) +{ + int floorLeft = qFloor(subSourceRect.left()); + int ceilRight = qCeil(subSourceRect.right()); + int floorTop = qFloor(subSourceRect.top()); + int ceilBottom = qCeil(subSourceRect.bottom()); + int hTiles = ceilRight - floorLeft; + int vTiles = ceilBottom - floorTop; + + int hCells = hTiles; + int vCells = vTiles; + if (innerTargetRect.width() == 0) + hCells = 0; + if (innerTargetRect.left() != targetRect.left()) + ++hCells; + if (innerTargetRect.right() != targetRect.right()) + ++hCells; + if (innerTargetRect.height() == 0) + vCells = 0; + if (innerTargetRect.top() != targetRect.top()) + ++vCells; + if (innerTargetRect.bottom() != targetRect.bottom()) + ++vCells; + QVarLengthArray xData(2 * hCells); + QVarLengthArray yData(2 * vCells); + X *xs = xData.data(); + Y *ys = yData.data(); + + if (innerTargetRect.left() != targetRect.left()) { + xs[0].x = targetRect.left(); + xs[0].tx = sourceRect.left(); + xs[1].x = innerTargetRect.left(); + xs[1].tx = innerSourceRect.left(); + xs += 2; + } + if (innerTargetRect.width() != 0) { + xs[0].x = innerTargetRect.left(); + xs[0].tx = innerSourceRect.x() + (subSourceRect.left() - floorLeft) * innerSourceRect.width(); + ++xs; + float b = innerTargetRect.width() / subSourceRect.width(); + float a = innerTargetRect.x() - subSourceRect.x() * b; + for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { + xs[0].x = xs[1].x = a + b * i; + xs[0].tx = innerSourceRect.right(); + xs[1].tx = innerSourceRect.left(); + xs += 2; + } + xs[0].x = innerTargetRect.right(); + xs[0].tx = innerSourceRect.x() + (subSourceRect.right() - ceilRight + 1) * innerSourceRect.width(); + ++xs; + } + if (innerTargetRect.right() != targetRect.right()) { + xs[0].x = innerTargetRect.right(); + xs[0].tx = innerSourceRect.right(); + xs[1].x = targetRect.right(); + xs[1].tx = sourceRect.right(); + xs += 2; + } + Q_ASSERT(xs == xData.data() + xData.size()); + if (mirror) { + float leftPlusRight = targetRect.left() + targetRect.right(); + int count = xData.size(); + xs = xData.data(); + for (int i = 0; i < count >> 1; ++i) + qSwap(xs[i], xs[count - 1 - i]); + for (int i = 0; i < count; ++i) + xs[i].x = leftPlusRight - xs[i].x; + } + + if (innerTargetRect.top() != targetRect.top()) { + ys[0].y = targetRect.top(); + ys[0].ty = sourceRect.top(); + ys[1].y = innerTargetRect.top(); + ys[1].ty = innerSourceRect.top(); + ys += 2; + } + if (innerTargetRect.height() != 0) { + ys[0].y = innerTargetRect.top(); + ys[0].ty = innerSourceRect.y() + (subSourceRect.top() - floorTop) * innerSourceRect.height(); + ++ys; + float b = innerTargetRect.height() / subSourceRect.height(); + float a = innerTargetRect.y() - subSourceRect.y() * b; + for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { + ys[0].y = ys[1].y = a + b * i; + ys[0].ty = innerSourceRect.bottom(); + ys[1].ty = innerSourceRect.top(); + ys += 2; + } + ys[0].y = innerTargetRect.bottom(); + ys[0].ty = innerSourceRect.y() + (subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height(); + ++ys; + } + if (innerTargetRect.bottom() != targetRect.bottom()) { + ys[0].y = innerTargetRect.bottom(); + ys[0].ty = innerSourceRect.bottom(); + ys[1].y = targetRect.bottom(); + ys[1].ty = sourceRect.bottom(); + ys += 2; + } + Q_ASSERT(ys == yData.data() + yData.size()); + + if (antialiasing) { + QSGGeometry *g = geometry; + Q_ASSERT(g); + + g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, + hCells * vCells * 6 + (hCells + vCells) * 12); + g->setDrawingMode(QSGGeometry::DrawTriangles); + SmoothVertex *vertices = reinterpret_cast(g->vertexData()); + memset(vertices, 0, g->vertexCount() * g->sizeOfVertex()); + quint16 *indices = g->indexDataAsUShort(); + + // The deltas are how much the fuzziness can reach into the image. + // Only the border vertices are moved by the vertex shader, so the fuzziness + // can't reach further into the image than the closest interior vertices. + float leftDx = xData.at(1).x - xData.at(0).x; + float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x; + float topDy = yData.at(1).y - yData.at(0).y; + float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y; + + float leftDu = xData.at(1).tx - xData.at(0).tx; + float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx; + float topDv = yData.at(1).ty - yData.at(0).ty; + float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty; + + if (hCells == 1) { + leftDx = rightDx *= 0.5f; + leftDu = rightDu *= 0.5f; + } + if (vCells == 1) { + topDy = bottomDy *= 0.5f; + topDv = bottomDv *= 0.5f; + } + + // This delta is how much the fuzziness can reach out from the image. + float delta = float(qAbs(targetRect.width()) < qAbs(targetRect.height()) + ? targetRect.width() : targetRect.height()) * 0.5f; + + quint16 index = 0; + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + bool isTop = j == 0; + bool isBottom = j == vCells - 1; + for (int i = 0; i < hCells; ++i, xs += 2) { + bool isLeft = i == 0; + bool isRight = i == hCells - 1; + + SmoothVertex *v = vertices + index; + + quint16 topLeft = index; + for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) { + v->x = xs[0].x; + v->u = xs[0].tx; + v->y = ys[0].y; + v->v = ys[0].ty; + } + + quint16 topRight = index; + for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) { + v->x = xs[1].x; + v->u = xs[1].tx; + v->y = ys[0].y; + v->v = ys[0].ty; + } + + quint16 bottomLeft = index; + for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) { + v->x = xs[0].x; + v->u = xs[0].tx; + v->y = ys[1].y; + v->v = ys[1].ty; + } + + quint16 bottomRight = index; + for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) { + v->x = xs[1].x; + v->u = xs[1].tx; + v->y = ys[1].y; + v->v = ys[1].ty; + } + + appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight); + + if (isTop) { + vertices[topLeft].dy = vertices[topRight].dy = topDy; + vertices[topLeft].dv = vertices[topRight].dv = topDv; + vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta; + appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight); + } + + if (isBottom) { + vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy; + vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv; + vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta; + appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1); + } + + if (isLeft) { + vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx; + vertices[topLeft].du = vertices[bottomLeft].du = leftDu; + vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta; + appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft); + } + + if (isRight) { + vertices[topRight].dx = vertices[bottomRight].dx = -rightDx; + vertices[topRight].du = vertices[bottomRight].du = -rightDu; + vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta; + appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1); + } + } + } + + Q_ASSERT(index == g->vertexCount()); + Q_ASSERT(indices - g->indexCount() == g->indexData()); + } else { + if (!geometry) { + geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), + hCells * vCells * 4, hCells * vCells * 6, + QSGGeometry::TypeUnsignedShort); + } else { + geometry->allocate(hCells * vCells * 4, hCells * vCells * 6); + } + geometry->setDrawingMode(QSGGeometry::DrawTriangles); + QSGGeometry::TexturedPoint2D *vertices = geometry->vertexDataAsTexturedPoint2D(); + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + for (int i = 0; i < hCells; ++i, xs += 2) { + vertices[0].x = vertices[2].x = xs[0].x; + vertices[0].tx = vertices[2].tx = xs[0].tx; + vertices[1].x = vertices[3].x = xs[1].x; + vertices[1].tx = vertices[3].tx = xs[1].tx; + + vertices[0].y = vertices[1].y = ys[0].y; + vertices[0].ty = vertices[1].ty = ys[0].ty; + vertices[2].y = vertices[3].y = ys[1].y; + vertices[2].ty = vertices[3].ty = ys[1].ty; + + vertices += 4; + } + } + + quint16 *indices = geometry->indexDataAsUShort(); + for (int i = 0; i < 4 * vCells * hCells; i += 4) + appendQuad(&indices, i, i + 1, i + 2, i + 3); + } + return geometry; +} + +void QSGBasicInternalImageNode::updateGeometry() +{ + Q_ASSERT(!m_targetRect.isEmpty()); + const QSGTexture *t = materialTexture(); + if (!t) { + QSGGeometry *g = geometry(); + g->allocate(4); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + memset(g->vertexData(), 0, g->sizeOfVertex() * 4); + } else { + QRectF sourceRect = t->normalizedTextureSubRect(); + + QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(), + sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(), + m_innerSourceRect.width() * sourceRect.width(), + m_innerSourceRect.height() * sourceRect.height()); + + bool hasMargins = m_targetRect != m_innerTargetRect; + + int floorLeft = qFloor(m_subSourceRect.left()); + int ceilRight = qCeil(m_subSourceRect.right()); + int floorTop = qFloor(m_subSourceRect.top()); + int ceilBottom = qCeil(m_subSourceRect.bottom()); + int hTiles = ceilRight - floorLeft; + int vTiles = ceilBottom - floorTop; + + bool hasTiles = hTiles != 1 || vTiles != 1; + bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1); + + // An image can be rendered as a single quad if: + // - There are no margins, and either: + // - the image isn't repeated + // - the source rectangle fills the entire texture so that texture wrapping can be used, + // and NPOT is supported + if (!hasMargins && (!hasTiles || (fullTexture && supportsWrap(t->textureSize())))) { + QRectF sr; + if (!fullTexture) { + sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(), + innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(), + m_subSourceRect.width() * innerSourceRect.width(), + m_subSourceRect.height() * innerSourceRect.height()); + } else { + sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop, + m_subSourceRect.width(), m_subSourceRect.height()); + } + if (m_mirror) { + qreal oldLeft = sr.left(); + sr.setLeft(sr.right()); + sr.setRight(oldLeft); + } + + if (m_antialiasing) { + QSGGeometry *g = geometry(); + Q_ASSERT(g != &m_geometry); + g->allocate(8, 14); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + SmoothVertex *vertices = reinterpret_cast(g->vertexData()); + float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) + ? m_targetRect.width() : m_targetRect.height()) * 0.5f; + float sx = float(sr.width() / m_targetRect.width()); + float sy = float(sr.height() / m_targetRect.height()); + for (int d = -1; d <= 1; d += 2) { + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 2; ++i, ++vertices) { + vertices->x = m_targetRect.x() + i * m_targetRect.width(); + vertices->y = m_targetRect.y() + j * m_targetRect.height(); + vertices->u = sr.x() + i * sr.width(); + vertices->v = sr.y() + j * sr.height(); + vertices->dx = (i == 0 ? delta : -delta) * d; + vertices->dy = (j == 0 ? delta : -delta) * d; + vertices->du = (d < 0 ? 0 : vertices->dx * sx); + vertices->dv = (d < 0 ? 0 : vertices->dy * sy); + } + } + } + Q_ASSERT(vertices - g->vertexCount() == g->vertexData()); + static const quint16 indices[] = { + 0, 4, 1, 5, 3, 7, 2, 6, 0, 4, + 4, 6, 5, 7 + }; + Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices)); + memcpy(g->indexDataAsUShort(), indices, sizeof(indices)); + } else { + m_geometry.allocate(4); + m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip); + QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); + } + } else { + QSGGeometry *g = m_antialiasing ? geometry() : &m_geometry; + updateGeometry(m_targetRect, m_innerTargetRect, + sourceRect, innerSourceRect, m_subSourceRect, + g, m_mirror, m_antialiasing); + } + } + markDirty(DirtyGeometry); + m_dirtyGeometry = false; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgbasicinternalimagenode_p.h b/src/quick/scenegraph/qsgbasicinternalimagenode_p.h new file mode 100644 index 0000000000..a5689b20aa --- /dev/null +++ b/src/quick/scenegraph/qsgbasicinternalimagenode_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGBASICINTERNALIMAGENODE_P_H +#define QSGBASICINTERNALIMAGENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGBasicInternalImageNode : public QSGInternalImageNode +{ +public: + QSGBasicInternalImageNode(); + + void setTargetRect(const QRectF &rect) override; + void setInnerTargetRect(const QRectF &rect) override; + void setInnerSourceRect(const QRectF &rect) override; + void setSubSourceRect(const QRectF &rect) override; + void setTexture(QSGTexture *texture) override; + void setAntialiasing(bool antialiasing) override; + void setMirror(bool mirror) override; + void update() override; + void preprocess() override; + + static QSGGeometry *updateGeometry(const QRectF &targetRect, + const QRectF &innerTargetRect, + const QRectF &sourceRect, + const QRectF &innerSourceRect, + const QRectF &subSourceRect, + QSGGeometry *geometry, + bool mirror = false, + bool antialiasing = false); + +protected: + virtual void updateMaterialAntialiasing() = 0; + virtual void setMaterialTexture(QSGTexture *texture) = 0; + virtual QSGTexture *materialTexture() const = 0; + virtual bool updateMaterialBlending() = 0; + virtual bool supportsWrap(const QSize &size) const = 0; + + void updateGeometry(); + + QRectF m_targetRect; + QRectF m_innerTargetRect; + QRectF m_innerSourceRect; + QRectF m_subSourceRect; + + uint m_antialiasing : 1; + uint m_mirror : 1; + uint m_dirtyGeometry : 1; + + QSGGeometry m_geometry; + + QSGDynamicTexture *m_dynamicTexture; + QSize m_dynamicTextureSize; + QRectF m_dynamicTextureSubRect; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp new file mode 100644 index 0000000000..8fc850b60c --- /dev/null +++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp @@ -0,0 +1,687 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgbasicinternalrectanglenode_p.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace +{ + struct Color4ub + { + unsigned char r, g, b, a; + }; + + Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; } + Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; } + + inline Color4ub colorToColor4ub(const QColor &c) + { + Color4ub color = { uchar(qRound(c.redF() * c.alphaF() * 255)), + uchar(qRound(c.greenF() * c.alphaF() * 255)), + uchar(qRound(c.blueF() * c.alphaF() * 255)), + uchar(qRound(c.alphaF() * 255)) + }; + return color; + } + + // Same layout as QSGGeometry::ColoredPoint2D, but uses Color4ub for convenience. + struct Vertex + { + float x, y; + Color4ub color; + void set(float nx, float ny, Color4ub ncolor) + { + x = nx; y = ny; color = ncolor; + } + }; + + struct SmoothVertex : public Vertex + { + float dx, dy; + void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy) + { + Vertex::set(nx, ny, ncolor); + dx = ndx; dy = ndy; + } + }; + + const QSGGeometry::AttributeSet &smoothAttributeSet() + { + static QSGGeometry::Attribute data[] = { + QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION), + QSGGeometry::Attribute::createWithSemantic(1, 4, QSGGeometry::TypeUnsignedByte, QSGGeometry::Attribute::COLOR), + QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD) + }; + static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data }; + return attrs; + } +} + +QSGBasicInternalRectangleNode::QSGBasicInternalRectangleNode() + : m_radius(0) + , m_pen_width(0) + , m_aligned(true) + , m_antialiasing(false) + , m_gradient_is_opaque(true) + , m_dirty_geometry(false) + , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0) +{ + setGeometry(&m_geometry); + +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("internalrectangle")); +#endif +} + +void QSGBasicInternalRectangleNode::setRect(const QRectF &rect) +{ + if (rect == m_rect) + return; + m_rect = rect; + m_dirty_geometry = true; +} + +void QSGBasicInternalRectangleNode::setColor(const QColor &color) +{ + if (color == m_color) + return; + m_color = color; + if (m_gradient_stops.isEmpty()) + m_dirty_geometry = true; +} + +void QSGBasicInternalRectangleNode::setPenColor(const QColor &color) +{ + if (color == m_border_color) + return; + m_border_color = color; + if (m_pen_width > 0) + m_dirty_geometry = true; +} + +void QSGBasicInternalRectangleNode::setPenWidth(qreal width) +{ + if (width == m_pen_width) + return; + m_pen_width = width; + m_dirty_geometry = true; +} + + +void QSGBasicInternalRectangleNode::setGradientStops(const QGradientStops &stops) +{ + if (stops.constData() == m_gradient_stops.constData()) + return; + + m_gradient_stops = stops; + + m_gradient_is_opaque = true; + for (int i = 0; i < stops.size(); ++i) + m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff; + m_dirty_geometry = true; +} + +void QSGBasicInternalRectangleNode::setRadius(qreal radius) +{ + if (radius == m_radius) + return; + m_radius = radius; + m_dirty_geometry = true; +} + +void QSGBasicInternalRectangleNode::setAntialiasing(bool antialiasing) +{ + if (!supportsAntialiasing()) + return; + + if (antialiasing == m_antialiasing) + return; + m_antialiasing = antialiasing; + if (m_antialiasing) { + setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); + setFlag(OwnsGeometry, true); + } else { + setGeometry(&m_geometry); + setFlag(OwnsGeometry, false); + } + updateMaterialAntialiasing(); + m_dirty_geometry = true; +} + +void QSGBasicInternalRectangleNode::setAligned(bool aligned) +{ + if (aligned == m_aligned) + return; + m_aligned = aligned; + m_dirty_geometry = true; +} + +void QSGBasicInternalRectangleNode::update() +{ + if (m_dirty_geometry) { + updateGeometry(); + m_dirty_geometry = false; + + QSGNode::DirtyState state = QSGNode::DirtyGeometry; + updateMaterialBlending(&state); + markDirty(state); + } +} + +void QSGBasicInternalRectangleNode::updateGeometry() +{ + float width = float(m_rect.width()); + float height = float(m_rect.height()); + float penWidth = qMin(qMin(width, height) * 0.5f, float(m_pen_width)); + + if (m_aligned) + penWidth = qRound(penWidth); + + QSGGeometry *g = geometry(); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + int vertexStride = g->sizeOfVertex(); + + union { + Vertex *vertices; + SmoothVertex *smoothVertices; + }; + + Color4ub fillColor = colorToColor4ub(m_color); + Color4ub borderColor = colorToColor4ub(m_border_color); + Color4ub transparent = { 0, 0, 0, 0 }; + const QGradientStops &stops = m_gradient_stops; + + int nextGradientStop = 0; + float gradientPos = penWidth / height; + while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) + ++nextGradientStop; + int lastGradientStop = stops.size() - 1; + float lastGradientPos = 1.0f - penWidth / height; + while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos) + --lastGradientStop; + int gradientIntersections = (lastGradientStop - nextGradientStop + 1); + + if (m_radius > 0) { + // Rounded corners. + + // Radius should never exceeds half of the width or half of the height + float radius = qMin(qMin(width, height) * 0.5f, float(m_radius)); + QRectF innerRect = m_rect; + innerRect.adjust(radius, radius, -radius, -radius); + + float innerRadius = radius - penWidth * 1.0f; + float outerRadius = radius; + float delta = qMin(width, height) * 0.5f; + + // Number of segments per corner, approximately one per 3 pixels. + int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18); + + /* + + --+--__ + --+--__--__ + | --__--__ + | seg --__--+ + --+-__ ment _+ \ + --+-__--__ - \ \ + --__--+ se \ \ + + \ g \ \ + \ \ m \ \ + -----------+--+ e \ \ <- gradient line + \ \ nt\ \ + fill +--+----+--+ + | | | | + border + inner AA outer AA (AA = antialiasing) + + */ + + int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2; + int outerVertexCount = (segments + 1) * 4; + int vertexCount = innerVertexCount; + if (m_antialiasing || penWidth) + vertexCount += innerVertexCount; + if (penWidth) + vertexCount += outerVertexCount; + if (m_antialiasing && penWidth) + vertexCount += outerVertexCount; + + int fillIndexCount = innerVertexCount; + int innerAAIndexCount = innerVertexCount * 2 + 2; + int borderIndexCount = innerVertexCount * 2 + 2; + int outerAAIndexCount = outerVertexCount * 2 + 2; + int indexCount = 0; + int fillHead = 0; + int innerAAHead = 0; + int innerAATail = 0; + int borderHead = 0; + int borderTail = 0; + int outerAAHead = 0; + int outerAATail = 0; + bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); + if (hasFill) + indexCount += fillIndexCount; + if (m_antialiasing) { + innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; + indexCount += innerAAIndexCount; + } + if (penWidth) { + borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; + indexCount += borderIndexCount; + } + if (m_antialiasing && penWidth) { + outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; + indexCount += outerAAIndexCount; + } + + g->allocate(vertexCount, indexCount); + vertices = reinterpret_cast(g->vertexData()); + memset(vertices, 0, vertexCount * vertexStride); + quint16 *indices = g->indexDataAsUShort(); + quint16 index = 0; + + float py = 0; // previous inner y-coordinate. + float plx = 0; // previous inner left x-coordinate. + float prx = 0; // previous inner right x-coordinate. + + float angle = 0.5f * float(M_PI) / segments; + float cosStep = qFastCos(angle); + float sinStep = qFastSin(angle); + + for (int part = 0; part < 2; ++part) { + float c = 1 - part; + float s = part; + for (int i = 0; i <= segments; ++i) { + float y, lx, rx; + if (innerRadius > 0) { + y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate. + lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate. + rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate. + gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height; + } else { + y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate. + lx = innerRect.left() - innerRadius; // current inner left x-coordinate. + rx = innerRect.right() + innerRadius; // current inner right x-coordinate. + gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height; + } + float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate. + float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate. + float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate. + + while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { + // Insert vertices at gradient stops. + float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height; + float t = (gy - py) / (y - py); + float glx = plx * (1 - t) + t * lx; + float grx = prx * (1 - t) + t * rx; + + fillColor = colorToColor4ub(stops.at(nextGradientStop).second); + + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; + } + + if (penWidth) { + --borderHead; + indices[borderHead] = indices[borderHead + 2]; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail] = indices[borderTail - 2]; + ++borderTail; + } + + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + bool lower = stops.at(nextGradientStop).first > 0.5f; + float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); + smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy); + smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy); + if (penWidth) { + smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); + smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); + } else { + dy = lower ? delta : -delta; + smoothVertices[index++].set(grx, gy, transparent, delta, dy); + smoothVertices[index++].set(glx, gy, transparent, -delta, dy); + } + } else { + vertices[index++].set(grx, gy, fillColor); + vertices[index++].set(glx, gy, fillColor); + if (penWidth) { + vertices[index++].set(grx, gy, borderColor); + vertices[index++].set(glx, gy, borderColor); + } + } + ++nextGradientStop; + } + + if (!stops.isEmpty()) { + if (nextGradientStop == 0) { + fillColor = colorToColor4ub(stops.at(0).second); + } else if (nextGradientStop == stops.size()) { + fillColor = colorToColor4ub(stops.last().second); + } else { + const QGradientStop &prev = stops.at(nextGradientStop - 1); + const QGradientStop &next = stops.at(nextGradientStop); + float t = (gradientPos - prev.first) / (next.first - prev.first); + fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t; + } + } + + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; + } + + if (penWidth) { + indices[--borderHead] = index + 4; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail++] = index + 5; + } + + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); + smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); + smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); + + dy = part ? delta : -delta; + if (penWidth) { + smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); + smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); + smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c); + smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c); + smoothVertices[index++].set(rX, Y, transparent, delta, dy); + smoothVertices[index++].set(lX, Y, transparent, -delta, dy); + + indices[--outerAAHead] = index - 2; + indices[--outerAAHead] = index - 4; + indices[outerAATail++] = index - 3; + indices[outerAATail++] = index - 1; + } else { + smoothVertices[index++].set(rx, y, transparent, delta, dy); + smoothVertices[index++].set(lx, y, transparent, -delta, dy); + } + } else { + vertices[index++].set(rx, y, fillColor); + vertices[index++].set(lx, y, fillColor); + if (penWidth) { + vertices[index++].set(rx, y, borderColor); + vertices[index++].set(lx, y, borderColor); + vertices[index++].set(rX, Y, borderColor); + vertices[index++].set(lX, Y, borderColor); + } + } + + py = y; + plx = lx; + prx = rx; + + // Rotate + qreal tmp = c; + c = c * cosStep - s * sinStep; + s = s * cosStep + tmp * sinStep; + } + } + Q_ASSERT(index == vertexCount); + + // Close the triangle strips. + if (m_antialiasing) { + indices[--innerAAHead] = indices[innerAATail - 1]; + indices[--innerAAHead] = indices[innerAATail - 2]; + Q_ASSERT(innerAATail <= indexCount); + } + if (penWidth) { + indices[--borderHead] = indices[borderTail - 1]; + indices[--borderHead] = indices[borderTail - 2]; + Q_ASSERT(borderTail <= indexCount); + } + if (m_antialiasing && penWidth) { + indices[--outerAAHead] = indices[outerAATail - 1]; + indices[--outerAAHead] = indices[outerAATail - 2]; + Q_ASSERT(outerAATail == indexCount); + } + } else { + // Straight corners. + QRectF innerRect = m_rect; + QRectF outerRect = m_rect; + + if (penWidth) + innerRect.adjust(1.0f * penWidth, 1.0f * penWidth, -1.0f * penWidth, -1.0f * penWidth); + + float delta = qMin(width, height) * 0.5f; + int innerVertexCount = 4 + gradientIntersections * 2; + int outerVertexCount = 4; + int vertexCount = innerVertexCount; + if (m_antialiasing || penWidth) + vertexCount += innerVertexCount; + if (penWidth) + vertexCount += outerVertexCount; + if (m_antialiasing && penWidth) + vertexCount += outerVertexCount; + + int fillIndexCount = innerVertexCount; + int innerAAIndexCount = innerVertexCount * 2 + 2; + int borderIndexCount = innerVertexCount * 2 + 2; + int outerAAIndexCount = outerVertexCount * 2 + 2; + int indexCount = 0; + int fillHead = 0; + int innerAAHead = 0; + int innerAATail = 0; + int borderHead = 0; + int borderTail = 0; + int outerAAHead = 0; + int outerAATail = 0; + bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); + if (hasFill) + indexCount += fillIndexCount; + if (m_antialiasing) { + innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; + indexCount += innerAAIndexCount; + } + if (penWidth) { + borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; + indexCount += borderIndexCount; + } + if (m_antialiasing && penWidth) { + outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; + indexCount += outerAAIndexCount; + } + + g->allocate(vertexCount, indexCount); + vertices = reinterpret_cast(g->vertexData()); + memset(vertices, 0, vertexCount * vertexStride); + quint16 *indices = g->indexDataAsUShort(); + quint16 index = 0; + + float lx = innerRect.left(); + float rx = innerRect.right(); + float lX = outerRect.left(); + float rX = outerRect.right(); + + for (int part = -1; part <= 1; part += 2) { + float y = (part == 1 ? innerRect.bottom() : innerRect.top()); + float Y = (part == 1 ? outerRect.bottom() : outerRect.top()); + gradientPos = (y - innerRect.top() + penWidth) / height; + + while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { + // Insert vertices at gradient stops. + float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height; + + fillColor = colorToColor4ub(stops.at(nextGradientStop).second); + + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; + } + + if (penWidth) { + --borderHead; + indices[borderHead] = indices[borderHead + 2]; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail] = indices[borderTail - 2]; + ++borderTail; + } + + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + bool lower = stops.at(nextGradientStop).first > 0.5f; + float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); + smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy); + smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy); + if (penWidth) { + smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); + smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); + } else { + smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta); + smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta); + } + } else { + vertices[index++].set(rx, gy, fillColor); + vertices[index++].set(lx, gy, fillColor); + if (penWidth) { + vertices[index++].set(rx, gy, borderColor); + vertices[index++].set(lx, gy, borderColor); + } + } + ++nextGradientStop; + } + + if (!stops.isEmpty()) { + if (nextGradientStop == 0) { + fillColor = colorToColor4ub(stops.at(0).second); + } else if (nextGradientStop == stops.size()) { + fillColor = colorToColor4ub(stops.last().second); + } else { + const QGradientStop &prev = stops.at(nextGradientStop - 1); + const QGradientStop &next = stops.at(nextGradientStop); + float t = (gradientPos - prev.first) / (next.first - prev.first); + fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t; + } + } + + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; + } + + if (penWidth) { + indices[--borderHead] = index + 4; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail++] = index + 5; + } + + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); + smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); + smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); + + if (penWidth) { + smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part); + smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part); + smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part); + smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part); + smoothVertices[index++].set(rX, Y, transparent, delta, delta * part); + smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part); + + indices[--outerAAHead] = index - 2; + indices[--outerAAHead] = index - 4; + indices[outerAATail++] = index - 3; + indices[outerAATail++] = index - 1; + } else { + smoothVertices[index++].set(rx, y, transparent, delta, delta * part); + smoothVertices[index++].set(lx, y, transparent, -delta, delta * part); + } + } else { + vertices[index++].set(rx, y, fillColor); + vertices[index++].set(lx, y, fillColor); + if (penWidth) { + vertices[index++].set(rx, y, borderColor); + vertices[index++].set(lx, y, borderColor); + vertices[index++].set(rX, Y, borderColor); + vertices[index++].set(lX, Y, borderColor); + } + } + } + Q_ASSERT(index == vertexCount); + + // Close the triangle strips. + if (m_antialiasing) { + indices[--innerAAHead] = indices[innerAATail - 1]; + indices[--innerAAHead] = indices[innerAATail - 2]; + Q_ASSERT(innerAATail <= indexCount); + } + if (penWidth) { + indices[--borderHead] = indices[borderTail - 1]; + indices[--borderHead] = indices[borderTail - 2]; + Q_ASSERT(borderTail <= indexCount); + } + if (m_antialiasing && penWidth) { + indices[--outerAAHead] = indices[outerAATail - 1]; + indices[--outerAAHead] = indices[outerAATail - 2]; + Q_ASSERT(outerAATail == indexCount); + } + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h new file mode 100644 index 0000000000..98e53669ce --- /dev/null +++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + + +#ifndef QSGBASICINTERNALRECTANGLENODE_P_H +#define QSGBASICINTERNALRECTANGLENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGBasicInternalRectangleNode : public QSGInternalRectangleNode +{ +public: + QSGBasicInternalRectangleNode(); + + void setRect(const QRectF &rect) override; + void setColor(const QColor &color) override; + void setPenColor(const QColor &color) override; + void setPenWidth(qreal width) override; + void setGradientStops(const QGradientStops &stops) override; + void setRadius(qreal radius) override; + void setAntialiasing(bool antialiasing) override; + void setAligned(bool aligned) override; + void update() override; + +protected: + virtual bool supportsAntialiasing() const { return true; } + virtual void updateMaterialAntialiasing() = 0; + virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0; + + void updateGeometry(); + void updateGradientTexture(); + + QRectF m_rect; + QGradientStops m_gradient_stops; + QColor m_color; + QColor m_border_color; + qreal m_radius; + qreal m_pen_width; + + uint m_aligned : 1; + uint m_antialiasing : 1; + uint m_gradient_is_opaque : 1; + uint m_dirty_geometry : 1; + + QSGGeometry m_geometry; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/qsgbasicrectanglenode.cpp b/src/quick/scenegraph/qsgbasicrectanglenode.cpp deleted file mode 100644 index 14d2dc9677..0000000000 --- a/src/quick/scenegraph/qsgbasicrectanglenode.cpp +++ /dev/null @@ -1,687 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qsgbasicrectanglenode_p.h" - -#include - -QT_BEGIN_NAMESPACE - -namespace -{ - struct Color4ub - { - unsigned char r, g, b, a; - }; - - Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; } - Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; } - - inline Color4ub colorToColor4ub(const QColor &c) - { - Color4ub color = { uchar(qRound(c.redF() * c.alphaF() * 255)), - uchar(qRound(c.greenF() * c.alphaF() * 255)), - uchar(qRound(c.blueF() * c.alphaF() * 255)), - uchar(qRound(c.alphaF() * 255)) - }; - return color; - } - - // Same layout as QSGGeometry::ColoredPoint2D, but uses Color4ub for convenience. - struct Vertex - { - float x, y; - Color4ub color; - void set(float nx, float ny, Color4ub ncolor) - { - x = nx; y = ny; color = ncolor; - } - }; - - struct SmoothVertex : public Vertex - { - float dx, dy; - void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy) - { - Vertex::set(nx, ny, ncolor); - dx = ndx; dy = ndy; - } - }; - - const QSGGeometry::AttributeSet &smoothAttributeSet() - { - static QSGGeometry::Attribute data[] = { - QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION), - QSGGeometry::Attribute::createWithSemantic(1, 4, QSGGeometry::TypeUnsignedByte, QSGGeometry::Attribute::COLOR), - QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD) - }; - static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data }; - return attrs; - } -} - -QSGBasicRectangleNode::QSGBasicRectangleNode() - : m_radius(0) - , m_pen_width(0) - , m_aligned(true) - , m_antialiasing(false) - , m_gradient_is_opaque(true) - , m_dirty_geometry(false) - , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0) -{ - setGeometry(&m_geometry); - -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("rectangle")); -#endif -} - -void QSGBasicRectangleNode::setRect(const QRectF &rect) -{ - if (rect == m_rect) - return; - m_rect = rect; - m_dirty_geometry = true; -} - -void QSGBasicRectangleNode::setColor(const QColor &color) -{ - if (color == m_color) - return; - m_color = color; - if (m_gradient_stops.isEmpty()) - m_dirty_geometry = true; -} - -void QSGBasicRectangleNode::setPenColor(const QColor &color) -{ - if (color == m_border_color) - return; - m_border_color = color; - if (m_pen_width > 0) - m_dirty_geometry = true; -} - -void QSGBasicRectangleNode::setPenWidth(qreal width) -{ - if (width == m_pen_width) - return; - m_pen_width = width; - m_dirty_geometry = true; -} - - -void QSGBasicRectangleNode::setGradientStops(const QGradientStops &stops) -{ - if (stops.constData() == m_gradient_stops.constData()) - return; - - m_gradient_stops = stops; - - m_gradient_is_opaque = true; - for (int i = 0; i < stops.size(); ++i) - m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff; - m_dirty_geometry = true; -} - -void QSGBasicRectangleNode::setRadius(qreal radius) -{ - if (radius == m_radius) - return; - m_radius = radius; - m_dirty_geometry = true; -} - -void QSGBasicRectangleNode::setAntialiasing(bool antialiasing) -{ - if (!supportsAntialiasing()) - return; - - if (antialiasing == m_antialiasing) - return; - m_antialiasing = antialiasing; - if (m_antialiasing) { - setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); - setFlag(OwnsGeometry, true); - } else { - setGeometry(&m_geometry); - setFlag(OwnsGeometry, false); - } - updateMaterialAntialiasing(); - m_dirty_geometry = true; -} - -void QSGBasicRectangleNode::setAligned(bool aligned) -{ - if (aligned == m_aligned) - return; - m_aligned = aligned; - m_dirty_geometry = true; -} - -void QSGBasicRectangleNode::update() -{ - if (m_dirty_geometry) { - updateGeometry(); - m_dirty_geometry = false; - - QSGNode::DirtyState state = QSGNode::DirtyGeometry; - updateMaterialBlending(&state); - markDirty(state); - } -} - -void QSGBasicRectangleNode::updateGeometry() -{ - float width = float(m_rect.width()); - float height = float(m_rect.height()); - float penWidth = qMin(qMin(width, height) * 0.5f, float(m_pen_width)); - - if (m_aligned) - penWidth = qRound(penWidth); - - QSGGeometry *g = geometry(); - g->setDrawingMode(QSGGeometry::DrawTriangleStrip); - int vertexStride = g->sizeOfVertex(); - - union { - Vertex *vertices; - SmoothVertex *smoothVertices; - }; - - Color4ub fillColor = colorToColor4ub(m_color); - Color4ub borderColor = colorToColor4ub(m_border_color); - Color4ub transparent = { 0, 0, 0, 0 }; - const QGradientStops &stops = m_gradient_stops; - - int nextGradientStop = 0; - float gradientPos = penWidth / height; - while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) - ++nextGradientStop; - int lastGradientStop = stops.size() - 1; - float lastGradientPos = 1.0f - penWidth / height; - while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos) - --lastGradientStop; - int gradientIntersections = (lastGradientStop - nextGradientStop + 1); - - if (m_radius > 0) { - // Rounded corners. - - // Radius should never exceeds half of the width or half of the height - float radius = qMin(qMin(width, height) * 0.5f, float(m_radius)); - QRectF innerRect = m_rect; - innerRect.adjust(radius, radius, -radius, -radius); - - float innerRadius = radius - penWidth * 1.0f; - float outerRadius = radius; - float delta = qMin(width, height) * 0.5f; - - // Number of segments per corner, approximately one per 3 pixels. - int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18); - - /* - - --+--__ - --+--__--__ - | --__--__ - | seg --__--+ - --+-__ ment _+ \ - --+-__--__ - \ \ - --__--+ se \ \ - + \ g \ \ - \ \ m \ \ - -----------+--+ e \ \ <- gradient line - \ \ nt\ \ - fill +--+----+--+ - | | | | - border - inner AA outer AA (AA = antialiasing) - - */ - - int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2; - int outerVertexCount = (segments + 1) * 4; - int vertexCount = innerVertexCount; - if (m_antialiasing || penWidth) - vertexCount += innerVertexCount; - if (penWidth) - vertexCount += outerVertexCount; - if (m_antialiasing && penWidth) - vertexCount += outerVertexCount; - - int fillIndexCount = innerVertexCount; - int innerAAIndexCount = innerVertexCount * 2 + 2; - int borderIndexCount = innerVertexCount * 2 + 2; - int outerAAIndexCount = outerVertexCount * 2 + 2; - int indexCount = 0; - int fillHead = 0; - int innerAAHead = 0; - int innerAATail = 0; - int borderHead = 0; - int borderTail = 0; - int outerAAHead = 0; - int outerAATail = 0; - bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); - if (hasFill) - indexCount += fillIndexCount; - if (m_antialiasing) { - innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; - indexCount += innerAAIndexCount; - } - if (penWidth) { - borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; - indexCount += borderIndexCount; - } - if (m_antialiasing && penWidth) { - outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; - indexCount += outerAAIndexCount; - } - - g->allocate(vertexCount, indexCount); - vertices = reinterpret_cast(g->vertexData()); - memset(vertices, 0, vertexCount * vertexStride); - quint16 *indices = g->indexDataAsUShort(); - quint16 index = 0; - - float py = 0; // previous inner y-coordinate. - float plx = 0; // previous inner left x-coordinate. - float prx = 0; // previous inner right x-coordinate. - - float angle = 0.5f * float(M_PI) / segments; - float cosStep = qFastCos(angle); - float sinStep = qFastSin(angle); - - for (int part = 0; part < 2; ++part) { - float c = 1 - part; - float s = part; - for (int i = 0; i <= segments; ++i) { - float y, lx, rx; - if (innerRadius > 0) { - y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate. - lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate. - rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate. - gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height; - } else { - y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate. - lx = innerRect.left() - innerRadius; // current inner left x-coordinate. - rx = innerRect.right() + innerRadius; // current inner right x-coordinate. - gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height; - } - float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate. - float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate. - float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate. - - while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { - // Insert vertices at gradient stops. - float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height; - float t = (gy - py) / (y - py); - float glx = plx * (1 - t) + t * lx; - float grx = prx * (1 - t) + t * rx; - - fillColor = colorToColor4ub(stops.at(nextGradientStop).second); - - if (hasFill) { - indices[fillHead++] = index; - indices[fillHead++] = index + 1; - } - - if (penWidth) { - --borderHead; - indices[borderHead] = indices[borderHead + 2]; - indices[--borderHead] = index + 2; - indices[borderTail++] = index + 3; - indices[borderTail] = indices[borderTail - 2]; - ++borderTail; - } - - if (m_antialiasing) { - indices[--innerAAHead] = index + 2; - indices[--innerAAHead] = index; - indices[innerAATail++] = index + 1; - indices[innerAATail++] = index + 3; - - bool lower = stops.at(nextGradientStop).first > 0.5f; - float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); - smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy); - smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy); - if (penWidth) { - smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); - smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); - } else { - dy = lower ? delta : -delta; - smoothVertices[index++].set(grx, gy, transparent, delta, dy); - smoothVertices[index++].set(glx, gy, transparent, -delta, dy); - } - } else { - vertices[index++].set(grx, gy, fillColor); - vertices[index++].set(glx, gy, fillColor); - if (penWidth) { - vertices[index++].set(grx, gy, borderColor); - vertices[index++].set(glx, gy, borderColor); - } - } - ++nextGradientStop; - } - - if (!stops.isEmpty()) { - if (nextGradientStop == 0) { - fillColor = colorToColor4ub(stops.at(0).second); - } else if (nextGradientStop == stops.size()) { - fillColor = colorToColor4ub(stops.last().second); - } else { - const QGradientStop &prev = stops.at(nextGradientStop - 1); - const QGradientStop &next = stops.at(nextGradientStop); - float t = (gradientPos - prev.first) / (next.first - prev.first); - fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t; - } - } - - if (hasFill) { - indices[fillHead++] = index; - indices[fillHead++] = index + 1; - } - - if (penWidth) { - indices[--borderHead] = index + 4; - indices[--borderHead] = index + 2; - indices[borderTail++] = index + 3; - indices[borderTail++] = index + 5; - } - - if (m_antialiasing) { - indices[--innerAAHead] = index + 2; - indices[--innerAAHead] = index; - indices[innerAATail++] = index + 1; - indices[innerAATail++] = index + 3; - - float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); - smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); - smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); - - dy = part ? delta : -delta; - if (penWidth) { - smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); - smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); - smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c); - smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c); - smoothVertices[index++].set(rX, Y, transparent, delta, dy); - smoothVertices[index++].set(lX, Y, transparent, -delta, dy); - - indices[--outerAAHead] = index - 2; - indices[--outerAAHead] = index - 4; - indices[outerAATail++] = index - 3; - indices[outerAATail++] = index - 1; - } else { - smoothVertices[index++].set(rx, y, transparent, delta, dy); - smoothVertices[index++].set(lx, y, transparent, -delta, dy); - } - } else { - vertices[index++].set(rx, y, fillColor); - vertices[index++].set(lx, y, fillColor); - if (penWidth) { - vertices[index++].set(rx, y, borderColor); - vertices[index++].set(lx, y, borderColor); - vertices[index++].set(rX, Y, borderColor); - vertices[index++].set(lX, Y, borderColor); - } - } - - py = y; - plx = lx; - prx = rx; - - // Rotate - qreal tmp = c; - c = c * cosStep - s * sinStep; - s = s * cosStep + tmp * sinStep; - } - } - Q_ASSERT(index == vertexCount); - - // Close the triangle strips. - if (m_antialiasing) { - indices[--innerAAHead] = indices[innerAATail - 1]; - indices[--innerAAHead] = indices[innerAATail - 2]; - Q_ASSERT(innerAATail <= indexCount); - } - if (penWidth) { - indices[--borderHead] = indices[borderTail - 1]; - indices[--borderHead] = indices[borderTail - 2]; - Q_ASSERT(borderTail <= indexCount); - } - if (m_antialiasing && penWidth) { - indices[--outerAAHead] = indices[outerAATail - 1]; - indices[--outerAAHead] = indices[outerAATail - 2]; - Q_ASSERT(outerAATail == indexCount); - } - } else { - // Straight corners. - QRectF innerRect = m_rect; - QRectF outerRect = m_rect; - - if (penWidth) - innerRect.adjust(1.0f * penWidth, 1.0f * penWidth, -1.0f * penWidth, -1.0f * penWidth); - - float delta = qMin(width, height) * 0.5f; - int innerVertexCount = 4 + gradientIntersections * 2; - int outerVertexCount = 4; - int vertexCount = innerVertexCount; - if (m_antialiasing || penWidth) - vertexCount += innerVertexCount; - if (penWidth) - vertexCount += outerVertexCount; - if (m_antialiasing && penWidth) - vertexCount += outerVertexCount; - - int fillIndexCount = innerVertexCount; - int innerAAIndexCount = innerVertexCount * 2 + 2; - int borderIndexCount = innerVertexCount * 2 + 2; - int outerAAIndexCount = outerVertexCount * 2 + 2; - int indexCount = 0; - int fillHead = 0; - int innerAAHead = 0; - int innerAATail = 0; - int borderHead = 0; - int borderTail = 0; - int outerAAHead = 0; - int outerAATail = 0; - bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); - if (hasFill) - indexCount += fillIndexCount; - if (m_antialiasing) { - innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; - indexCount += innerAAIndexCount; - } - if (penWidth) { - borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; - indexCount += borderIndexCount; - } - if (m_antialiasing && penWidth) { - outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; - indexCount += outerAAIndexCount; - } - - g->allocate(vertexCount, indexCount); - vertices = reinterpret_cast(g->vertexData()); - memset(vertices, 0, vertexCount * vertexStride); - quint16 *indices = g->indexDataAsUShort(); - quint16 index = 0; - - float lx = innerRect.left(); - float rx = innerRect.right(); - float lX = outerRect.left(); - float rX = outerRect.right(); - - for (int part = -1; part <= 1; part += 2) { - float y = (part == 1 ? innerRect.bottom() : innerRect.top()); - float Y = (part == 1 ? outerRect.bottom() : outerRect.top()); - gradientPos = (y - innerRect.top() + penWidth) / height; - - while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { - // Insert vertices at gradient stops. - float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height; - - fillColor = colorToColor4ub(stops.at(nextGradientStop).second); - - if (hasFill) { - indices[fillHead++] = index; - indices[fillHead++] = index + 1; - } - - if (penWidth) { - --borderHead; - indices[borderHead] = indices[borderHead + 2]; - indices[--borderHead] = index + 2; - indices[borderTail++] = index + 3; - indices[borderTail] = indices[borderTail - 2]; - ++borderTail; - } - - if (m_antialiasing) { - indices[--innerAAHead] = index + 2; - indices[--innerAAHead] = index; - indices[innerAATail++] = index + 1; - indices[innerAATail++] = index + 3; - - bool lower = stops.at(nextGradientStop).first > 0.5f; - float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); - smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy); - smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy); - if (penWidth) { - smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); - smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); - } else { - smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta); - smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta); - } - } else { - vertices[index++].set(rx, gy, fillColor); - vertices[index++].set(lx, gy, fillColor); - if (penWidth) { - vertices[index++].set(rx, gy, borderColor); - vertices[index++].set(lx, gy, borderColor); - } - } - ++nextGradientStop; - } - - if (!stops.isEmpty()) { - if (nextGradientStop == 0) { - fillColor = colorToColor4ub(stops.at(0).second); - } else if (nextGradientStop == stops.size()) { - fillColor = colorToColor4ub(stops.last().second); - } else { - const QGradientStop &prev = stops.at(nextGradientStop - 1); - const QGradientStop &next = stops.at(nextGradientStop); - float t = (gradientPos - prev.first) / (next.first - prev.first); - fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t; - } - } - - if (hasFill) { - indices[fillHead++] = index; - indices[fillHead++] = index + 1; - } - - if (penWidth) { - indices[--borderHead] = index + 4; - indices[--borderHead] = index + 2; - indices[borderTail++] = index + 3; - indices[borderTail++] = index + 5; - } - - if (m_antialiasing) { - indices[--innerAAHead] = index + 2; - indices[--innerAAHead] = index; - indices[innerAATail++] = index + 1; - indices[innerAATail++] = index + 3; - - float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); - smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); - smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); - - if (penWidth) { - smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part); - smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part); - smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part); - smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part); - smoothVertices[index++].set(rX, Y, transparent, delta, delta * part); - smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part); - - indices[--outerAAHead] = index - 2; - indices[--outerAAHead] = index - 4; - indices[outerAATail++] = index - 3; - indices[outerAATail++] = index - 1; - } else { - smoothVertices[index++].set(rx, y, transparent, delta, delta * part); - smoothVertices[index++].set(lx, y, transparent, -delta, delta * part); - } - } else { - vertices[index++].set(rx, y, fillColor); - vertices[index++].set(lx, y, fillColor); - if (penWidth) { - vertices[index++].set(rx, y, borderColor); - vertices[index++].set(lx, y, borderColor); - vertices[index++].set(rX, Y, borderColor); - vertices[index++].set(lX, Y, borderColor); - } - } - } - Q_ASSERT(index == vertexCount); - - // Close the triangle strips. - if (m_antialiasing) { - indices[--innerAAHead] = indices[innerAATail - 1]; - indices[--innerAAHead] = indices[innerAATail - 2]; - Q_ASSERT(innerAATail <= indexCount); - } - if (penWidth) { - indices[--borderHead] = indices[borderTail - 1]; - indices[--borderHead] = indices[borderTail - 2]; - Q_ASSERT(borderTail <= indexCount); - } - if (m_antialiasing && penWidth) { - indices[--outerAAHead] = indices[outerAATail - 1]; - indices[--outerAAHead] = indices[outerAATail - 2]; - Q_ASSERT(outerAATail == indexCount); - } - } -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgbasicrectanglenode_p.h b/src/quick/scenegraph/qsgbasicrectanglenode_p.h deleted file mode 100644 index b1d1457590..0000000000 --- a/src/quick/scenegraph/qsgbasicrectanglenode_p.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - - -#ifndef QSGBASICRECTANGLENODE_P_H -#define QSGBASICRECTANGLENODE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -QT_BEGIN_NAMESPACE - -class Q_QUICK_PRIVATE_EXPORT QSGBasicRectangleNode : public QSGRectangleNode -{ -public: - QSGBasicRectangleNode(); - - void setRect(const QRectF &rect) override; - void setColor(const QColor &color) override; - void setPenColor(const QColor &color) override; - void setPenWidth(qreal width) override; - void setGradientStops(const QGradientStops &stops) override; - void setRadius(qreal radius) override; - void setAntialiasing(bool antialiasing) override; - void setAligned(bool aligned) override; - void update() override; - -protected: - virtual bool supportsAntialiasing() const { return true; } - virtual void updateMaterialAntialiasing() = 0; - virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0; - - void updateGeometry(); - void updateGradientTexture(); - - QRectF m_rect; - QGradientStops m_gradient_stops; - QColor m_color; - QColor m_border_color; - qreal m_radius; - qreal m_pen_width; - - uint m_aligned : 1; - uint m_antialiasing : 1; - uint m_gradient_is_opaque : 1; - uint m_dirty_geometry : 1; - - QSGGeometry m_geometry; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 40d65d99aa..fa77c2d6cc 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -268,9 +268,9 @@ void QSGContext::renderContextInvalidated(QSGRenderContext *) /*! Convenience factory function for creating a colored rectangle with the given geometry. */ -QSGRectangleNode *QSGContext::createRectangleNode(const QRectF &rect, const QColor &c) +QSGInternalRectangleNode *QSGContext::createInternalRectangleNode(const QRectF &rect, const QColor &c) { - QSGRectangleNode *node = createRectangleNode(); + QSGInternalRectangleNode *node = createInternalRectangleNode(); node->setRect(rect); node->setColor(c); node->update(); diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 2527be0480..e34d2c3ebf 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -66,11 +66,10 @@ QT_BEGIN_NAMESPACE class QSGContextPrivate; -class QSGRectangleNode; -class QSGImageNode; +class QSGInternalRectangleNode; +class QSGInternalImageNode; class QSGPainterNode; class QSGGlyphNode; -class QSGNinePatchNode; class QSGRenderer; class QSGDistanceFieldGlyphCache; class QQuickWindow; @@ -85,6 +84,9 @@ class QQuickPaintedItem; class QSGRendererInterface; class QSGShaderEffectNode; class QSGGuiThreadShaderEffectManager; +class QSGRectangleNode; +class QSGImageNode; +class QSGNinePatchNode; Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP) Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION) @@ -163,12 +165,11 @@ public: virtual void renderContextInvalidated(QSGRenderContext *renderContext); virtual QSGRenderContext *createRenderContext() = 0; - QSGRectangleNode *createRectangleNode(const QRectF &rect, const QColor &c); - virtual QSGRectangleNode *createRectangleNode() = 0; - virtual QSGImageNode *createImageNode() = 0; + QSGInternalRectangleNode *createInternalRectangleNode(const QRectF &rect, const QColor &c); + virtual QSGInternalRectangleNode *createInternalRectangleNode() = 0; + virtual QSGInternalImageNode *createInternalImageNode() = 0; virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item) = 0; virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) = 0; - virtual QSGNinePatchNode *createNinePatchNode() = 0; virtual QSGLayer *createLayer(QSGRenderContext *renderContext) = 0; virtual QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager(); virtual QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext, @@ -180,6 +181,10 @@ public: virtual QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext); + virtual QSGRectangleNode *createRectangleNode() = 0; + virtual QSGImageNode *createImageNode() = 0; + virtual QSGNinePatchNode *createNinePatchNode() = 0; + static QSGContext *createDefaultContext(); static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image); static QSGRenderLoop *createWindowManager(); diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp index f9978e816c..a33f4b174f 100644 --- a/src/quick/scenegraph/qsgdefaultcontext.cpp +++ b/src/quick/scenegraph/qsgdefaultcontext.cpp @@ -40,8 +40,8 @@ #include "qsgdefaultcontext_p.h" #include -#include -#include +#include +#include #include #include #include @@ -49,6 +49,9 @@ #include #include #include +#include +#include +#include #include #include @@ -60,13 +63,13 @@ QT_BEGIN_NAMESPACE namespace QSGMultisampleAntialiasing { - class ImageNode : public QSGDefaultImageNode { + class ImageNode : public QSGDefaultInternalImageNode { public: void setAntialiasing(bool) { } }; - class RectangleNode : public QSGDefaultRectangleNode { + class RectangleNode : public QSGDefaultInternalRectangleNode { public: void setAntialiasing(bool) { } }; @@ -170,18 +173,18 @@ QSGRenderContext *QSGDefaultContext::createRenderContext() return new QSGDefaultRenderContext(this); } -QSGRectangleNode *QSGDefaultContext::createRectangleNode() +QSGInternalRectangleNode *QSGDefaultContext::createInternalRectangleNode() { return m_antialiasingMethod == MsaaAntialiasing ? new QSGMultisampleAntialiasing::RectangleNode - : new QSGDefaultRectangleNode; + : new QSGDefaultInternalRectangleNode; } -QSGImageNode *QSGDefaultContext::createImageNode() +QSGInternalImageNode *QSGDefaultContext::createInternalImageNode() { return m_antialiasingMethod == MsaaAntialiasing ? new QSGMultisampleAntialiasing::ImageNode - : new QSGDefaultImageNode; + : new QSGDefaultInternalImageNode; } QSGPainterNode *QSGDefaultContext::createPainterNode(QQuickPaintedItem *item) @@ -200,15 +203,6 @@ QSGGlyphNode *QSGDefaultContext::createGlyphNode(QSGRenderContext *rc, bool pref } } -/*! - * Factory function for scene graph backends of the QStyle stylable elements. Returns a - * null pointer if the backend doesn't provide its own node type. - */ -QSGNinePatchNode *QSGDefaultContext::createNinePatchNode() -{ - return nullptr; -} - QSGLayer *QSGDefaultContext::createLayer(QSGRenderContext *renderContext) { return new QSGDefaultLayer(renderContext); @@ -246,6 +240,21 @@ QSGRendererInterface *QSGDefaultContext::rendererInterface(QSGRenderContext *ren return this; } +QSGRectangleNode *QSGDefaultContext::createRectangleNode() +{ + return new QSGDefaultRectangleNode; +} + +QSGImageNode *QSGDefaultContext::createImageNode() +{ + return new QSGDefaultImageNode; +} + +QSGNinePatchNode *QSGDefaultContext::createNinePatchNode() +{ + return new QSGDefaultNinePatchNode; +} + QSGRendererInterface::GraphicsApi QSGDefaultContext::graphicsApi() const { return OpenGL; diff --git a/src/quick/scenegraph/qsgdefaultcontext_p.h b/src/quick/scenegraph/qsgdefaultcontext_p.h index 6686ab98a0..63c817cc7b 100644 --- a/src/quick/scenegraph/qsgdefaultcontext_p.h +++ b/src/quick/scenegraph/qsgdefaultcontext_p.h @@ -66,14 +66,16 @@ public: void renderContextInitialized(QSGRenderContext *renderContext) override; void renderContextInvalidated(QSGRenderContext *) override; QSGRenderContext *createRenderContext() override; - QSGRectangleNode *createRectangleNode() override; - QSGImageNode *createImageNode() override; + QSGInternalRectangleNode *createInternalRectangleNode() override; + QSGInternalImageNode *createInternalImageNode() override; QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override; QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override; - QSGNinePatchNode *createNinePatchNode() override; QSGLayer *createLayer(QSGRenderContext *renderContext) override; QSurfaceFormat defaultSurfaceFormat() const override; QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override; + QSGRectangleNode *createRectangleNode() override; + QSGImageNode *createImageNode() override; + QSGNinePatchNode *createNinePatchNode() override; void setDistanceFieldEnabled(bool enabled); bool isDistanceFieldEnabled() const; diff --git a/src/quick/scenegraph/qsgdefaultimagenode.cpp b/src/quick/scenegraph/qsgdefaultimagenode.cpp deleted file mode 100644 index 9fed70a7de..0000000000 --- a/src/quick/scenegraph/qsgdefaultimagenode.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qsgdefaultimagenode_p.h" -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class SmoothTextureMaterialShader : public QSGTextureMaterialShader -{ -public: - SmoothTextureMaterialShader(); - - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; - -protected: - virtual void initialize(); - - int m_pixelSizeLoc; -}; - - -QSGSmoothTextureMaterial::QSGSmoothTextureMaterial() -{ - setFlag(RequiresFullMatrixExceptTranslate, true); - setFlag(Blending, true); -} - -void QSGSmoothTextureMaterial::setTexture(QSGTexture *texture) -{ - m_texture = texture; -} - -QSGMaterialType *QSGSmoothTextureMaterial::type() const -{ - static QSGMaterialType type; - return &type; -} - -QSGMaterialShader *QSGSmoothTextureMaterial::createShader() const -{ - return new SmoothTextureMaterialShader; -} - -SmoothTextureMaterialShader::SmoothTextureMaterialShader() - : QSGTextureMaterialShader() -{ - setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.vert")); - setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.frag")); -} - -void SmoothTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) -{ - if (oldEffect == 0) { - // The viewport is constant, so set the pixel size uniform only once. - QRect r = state.viewportRect(); - program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height()); - } - QSGTextureMaterialShader::updateState(state, newEffect, oldEffect); -} - -char const *const *SmoothTextureMaterialShader::attributeNames() const -{ - static char const *const attributes[] = { - "vertex", - "multiTexCoord", - "vertexOffset", - "texCoordOffset", - 0 - }; - return attributes; -} - -void SmoothTextureMaterialShader::initialize() -{ - m_pixelSizeLoc = program()->uniformLocation("pixelSize"); - QSGTextureMaterialShader::initialize(); -} - -QSGDefaultImageNode::QSGDefaultImageNode() -{ - setMaterial(&m_materialO); - setOpaqueMaterial(&m_material); -} - -void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering) -{ - if (m_material.filtering() == filtering) - return; - - m_material.setFiltering(filtering); - m_materialO.setFiltering(filtering); - m_smoothMaterial.setFiltering(filtering); - markDirty(DirtyMaterial); -} - -void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) -{ - if (m_material.mipmapFiltering() == filtering) - return; - - m_material.setMipmapFiltering(filtering); - m_materialO.setMipmapFiltering(filtering); - m_smoothMaterial.setMipmapFiltering(filtering); - markDirty(DirtyMaterial); -} - -void QSGDefaultImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) -{ - if (m_material.verticalWrapMode() == wrapMode) - return; - - m_material.setVerticalWrapMode(wrapMode); - m_materialO.setVerticalWrapMode(wrapMode); - m_smoothMaterial.setVerticalWrapMode(wrapMode); - markDirty(DirtyMaterial); -} - -void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) -{ - if (m_material.horizontalWrapMode() == wrapMode) - return; - - m_material.setHorizontalWrapMode(wrapMode); - m_materialO.setHorizontalWrapMode(wrapMode); - m_smoothMaterial.setHorizontalWrapMode(wrapMode); - markDirty(DirtyMaterial); -} - -void QSGDefaultImageNode::updateMaterialAntialiasing() -{ - if (m_antialiasing) { - setMaterial(&m_smoothMaterial); - setOpaqueMaterial(0); - } else { - setMaterial(&m_materialO); - setOpaqueMaterial(&m_material); - } -} - -void QSGDefaultImageNode::setMaterialTexture(QSGTexture *texture) -{ - m_material.setTexture(texture); - m_materialO.setTexture(texture); - m_smoothMaterial.setTexture(texture); -} - -QSGTexture *QSGDefaultImageNode::materialTexture() const -{ - return m_material.texture(); -} - -bool QSGDefaultImageNode::updateMaterialBlending() -{ - const bool alpha = m_material.flags() & QSGMaterial::Blending; - if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) { - m_material.setFlag(QSGMaterial::Blending, !alpha); - return true; - } - return false; -} - -inline static bool isPowerOfTwo(int x) -{ - // Assumption: x >= 1 - return x == (x & -x); -} - -bool QSGDefaultImageNode::supportsWrap(const QSize &size) const -{ - bool wrapSupported = true; - - QOpenGLContext *ctx = QOpenGLContext::currentContext(); -#ifndef QT_OPENGL_ES_2 - if (ctx->isOpenGLES()) -#endif - { - bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat); - const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); - wrapSupported = npotSupported || !isNpot; - } - - return wrapSupported; -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h deleted file mode 100644 index 688c5a5039..0000000000 --- a/src/quick/scenegraph/qsgdefaultimagenode_p.h +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - - -#ifndef QSGDEFAULTIMAGENODE_P_H -#define QSGDEFAULTIMAGENODE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class Q_QUICK_PRIVATE_EXPORT QSGSmoothTextureMaterial : public QSGTextureMaterial -{ -public: - QSGSmoothTextureMaterial(); - - void setTexture(QSGTexture *texture); - -protected: - QSGMaterialType *type() const override; - QSGMaterialShader *createShader() const override; -}; - -class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGBasicImageNode -{ -public: - QSGDefaultImageNode(); - - void setMipmapFiltering(QSGTexture::Filtering filtering) override; - void setFiltering(QSGTexture::Filtering filtering) override; - void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override; - void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override; - - void updateMaterialAntialiasing() override; - void setMaterialTexture(QSGTexture *texture) override; - QSGTexture *materialTexture() const override; - bool updateMaterialBlending() override; - bool supportsWrap(const QSize &size) const override; - -private: - QSGOpaqueTextureMaterial m_material; - QSGTextureMaterial m_materialO; - QSGSmoothTextureMaterial m_smoothMaterial; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp new file mode 100644 index 0000000000..1d54628acd --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgdefaultinternalimagenode_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class SmoothTextureMaterialShader : public QSGTextureMaterialShader +{ +public: + SmoothTextureMaterialShader(); + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + +protected: + virtual void initialize(); + + int m_pixelSizeLoc; +}; + + +QSGSmoothTextureMaterial::QSGSmoothTextureMaterial() +{ + setFlag(RequiresFullMatrixExceptTranslate, true); + setFlag(Blending, true); +} + +void QSGSmoothTextureMaterial::setTexture(QSGTexture *texture) +{ + m_texture = texture; +} + +QSGMaterialType *QSGSmoothTextureMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *QSGSmoothTextureMaterial::createShader() const +{ + return new SmoothTextureMaterialShader; +} + +SmoothTextureMaterialShader::SmoothTextureMaterialShader() + : QSGTextureMaterialShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.frag")); +} + +void SmoothTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + if (oldEffect == 0) { + // The viewport is constant, so set the pixel size uniform only once. + QRect r = state.viewportRect(); + program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height()); + } + QSGTextureMaterialShader::updateState(state, newEffect, oldEffect); +} + +char const *const *SmoothTextureMaterialShader::attributeNames() const +{ + static char const *const attributes[] = { + "vertex", + "multiTexCoord", + "vertexOffset", + "texCoordOffset", + 0 + }; + return attributes; +} + +void SmoothTextureMaterialShader::initialize() +{ + m_pixelSizeLoc = program()->uniformLocation("pixelSize"); + QSGTextureMaterialShader::initialize(); +} + +QSGDefaultInternalImageNode::QSGDefaultInternalImageNode() +{ + setMaterial(&m_materialO); + setOpaqueMaterial(&m_material); +} + +void QSGDefaultInternalImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.filtering() == filtering) + return; + + m_material.setFiltering(filtering); + m_materialO.setFiltering(filtering); + m_smoothMaterial.setFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGDefaultInternalImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.mipmapFiltering() == filtering) + return; + + m_material.setMipmapFiltering(filtering); + m_materialO.setMipmapFiltering(filtering); + m_smoothMaterial.setMipmapFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGDefaultInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) +{ + if (m_material.verticalWrapMode() == wrapMode) + return; + + m_material.setVerticalWrapMode(wrapMode); + m_materialO.setVerticalWrapMode(wrapMode); + m_smoothMaterial.setVerticalWrapMode(wrapMode); + markDirty(DirtyMaterial); +} + +void QSGDefaultInternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) +{ + if (m_material.horizontalWrapMode() == wrapMode) + return; + + m_material.setHorizontalWrapMode(wrapMode); + m_materialO.setHorizontalWrapMode(wrapMode); + m_smoothMaterial.setHorizontalWrapMode(wrapMode); + markDirty(DirtyMaterial); +} + +void QSGDefaultInternalImageNode::updateMaterialAntialiasing() +{ + if (m_antialiasing) { + setMaterial(&m_smoothMaterial); + setOpaqueMaterial(0); + } else { + setMaterial(&m_materialO); + setOpaqueMaterial(&m_material); + } +} + +void QSGDefaultInternalImageNode::setMaterialTexture(QSGTexture *texture) +{ + m_material.setTexture(texture); + m_materialO.setTexture(texture); + m_smoothMaterial.setTexture(texture); +} + +QSGTexture *QSGDefaultInternalImageNode::materialTexture() const +{ + return m_material.texture(); +} + +bool QSGDefaultInternalImageNode::updateMaterialBlending() +{ + const bool alpha = m_material.flags() & QSGMaterial::Blending; + if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) { + m_material.setFlag(QSGMaterial::Blending, !alpha); + return true; + } + return false; +} + +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + +bool QSGDefaultInternalImageNode::supportsWrap(const QSize &size) const +{ + bool wrapSupported = true; + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); +#ifndef QT_OPENGL_ES_2 + if (ctx->isOpenGLES()) +#endif + { + bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat); + const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); + wrapSupported = npotSupported || !isNpot; + } + + return wrapSupported; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h new file mode 100644 index 0000000000..1fc7834bd1 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + + +#ifndef QSGDEFAULTINTERNALIMAGENODE_P_H +#define QSGDEFAULTINTERNALIMAGENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGSmoothTextureMaterial : public QSGTextureMaterial +{ +public: + QSGSmoothTextureMaterial(); + + void setTexture(QSGTexture *texture); + +protected: + QSGMaterialType *type() const override; + QSGMaterialShader *createShader() const override; +}; + +class Q_QUICK_PRIVATE_EXPORT QSGDefaultInternalImageNode : public QSGBasicInternalImageNode +{ +public: + QSGDefaultInternalImageNode(); + + void setMipmapFiltering(QSGTexture::Filtering filtering) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override; + void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override; + + void updateMaterialAntialiasing() override; + void setMaterialTexture(QSGTexture *texture) override; + QSGTexture *materialTexture() const override; + bool updateMaterialBlending() override; + bool supportsWrap(const QSize &size) const override; + +private: + QSGOpaqueTextureMaterial m_material; + QSGTextureMaterial m_materialO; + QSGSmoothTextureMaterial m_smoothMaterial; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp new file mode 100644 index 0000000000..94414444ba --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp @@ -0,0 +1,159 @@ + +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgdefaultinternalrectanglenode_p.h" + +#include +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class SmoothColorMaterialShader : public QSGMaterialShader +{ +public: + SmoothColorMaterialShader(); + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + +private: + virtual void initialize(); + + int m_matrixLoc; + int m_opacityLoc; + int m_pixelSizeLoc; +}; + +SmoothColorMaterialShader::SmoothColorMaterialShader() + : QSGMaterialShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.frag")); +} + +void SmoothColorMaterialShader::updateState(const RenderState &state, QSGMaterial *, QSGMaterial *oldEffect) +{ + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + if (oldEffect == 0) { + // The viewport is constant, so set the pixel size uniform only once. + QRect r = state.viewportRect(); + program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height()); + } +} + +char const *const *SmoothColorMaterialShader::attributeNames() const +{ + static char const *const attributes[] = { + "vertex", + "vertexColor", + "vertexOffset", + 0 + }; + return attributes; +} + +void SmoothColorMaterialShader::initialize() +{ + m_matrixLoc = program()->uniformLocation("matrix"); + m_opacityLoc = program()->uniformLocation("opacity"); + m_pixelSizeLoc = program()->uniformLocation("pixelSize"); +} + +QSGSmoothColorMaterial::QSGSmoothColorMaterial() +{ + setFlag(RequiresFullMatrixExceptTranslate, true); + setFlag(Blending, true); +} + +int QSGSmoothColorMaterial::compare(const QSGMaterial *) const +{ + return 0; +} + +QSGMaterialType *QSGSmoothColorMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *QSGSmoothColorMaterial::createShader() const +{ + return new SmoothColorMaterialShader; +} + +QSGDefaultInternalRectangleNode::QSGDefaultInternalRectangleNode() +{ + setMaterial(&m_material); +} + +void QSGDefaultInternalRectangleNode::updateMaterialAntialiasing() +{ + if (m_antialiasing) + setMaterial(&m_smoothMaterial); + else + setMaterial(&m_material); +} + +void QSGDefaultInternalRectangleNode::updateMaterialBlending(QSGNode::DirtyState *state) +{ + // smoothed material is always blended, so no change in material state + if (material() == &m_material) { + bool wasBlending = (m_material.flags() & QSGMaterial::Blending); + bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque) + || (m_color.alpha() < 255 && m_color.alpha() != 0) + || (m_pen_width > 0 && m_border_color.alpha() < 255); + if (wasBlending != isBlending) { + m_material.setFlag(QSGMaterial::Blending, isBlending); + *state |= QSGNode::DirtyMaterial; + } + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h new file mode 100644 index 0000000000..941d870c8b --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + + +#ifndef QSGDEFAULTINTERNALRECTANGLENODE_P_H +#define QSGDEFAULTINTERNALRECTANGLENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGContext; + +class Q_QUICK_PRIVATE_EXPORT QSGSmoothColorMaterial : public QSGMaterial +{ +public: + QSGSmoothColorMaterial(); + + int compare(const QSGMaterial *other) const; + +protected: + QSGMaterialType *type() const override; + QSGMaterialShader *createShader() const override; +}; + +class Q_QUICK_PRIVATE_EXPORT QSGDefaultInternalRectangleNode : public QSGBasicInternalRectangleNode +{ +public: + QSGDefaultInternalRectangleNode(); + +private: + void updateMaterialAntialiasing() override; + void updateMaterialBlending(QSGNode::DirtyState *state) override; + + QSGVertexColorMaterial m_material; + QSGSmoothColorMaterial m_smoothMaterial; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp deleted file mode 100644 index 117a9272e5..0000000000 --- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp +++ /dev/null @@ -1,159 +0,0 @@ - -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qsgdefaultrectanglenode_p.h" - -#include -#include - -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class SmoothColorMaterialShader : public QSGMaterialShader -{ -public: - SmoothColorMaterialShader(); - - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; - -private: - virtual void initialize(); - - int m_matrixLoc; - int m_opacityLoc; - int m_pixelSizeLoc; -}; - -SmoothColorMaterialShader::SmoothColorMaterialShader() - : QSGMaterialShader() -{ - setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.vert")); - setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.frag")); -} - -void SmoothColorMaterialShader::updateState(const RenderState &state, QSGMaterial *, QSGMaterial *oldEffect) -{ - if (state.isOpacityDirty()) - program()->setUniformValue(m_opacityLoc, state.opacity()); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); - - if (oldEffect == 0) { - // The viewport is constant, so set the pixel size uniform only once. - QRect r = state.viewportRect(); - program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height()); - } -} - -char const *const *SmoothColorMaterialShader::attributeNames() const -{ - static char const *const attributes[] = { - "vertex", - "vertexColor", - "vertexOffset", - 0 - }; - return attributes; -} - -void SmoothColorMaterialShader::initialize() -{ - m_matrixLoc = program()->uniformLocation("matrix"); - m_opacityLoc = program()->uniformLocation("opacity"); - m_pixelSizeLoc = program()->uniformLocation("pixelSize"); -} - -QSGSmoothColorMaterial::QSGSmoothColorMaterial() -{ - setFlag(RequiresFullMatrixExceptTranslate, true); - setFlag(Blending, true); -} - -int QSGSmoothColorMaterial::compare(const QSGMaterial *) const -{ - return 0; -} - -QSGMaterialType *QSGSmoothColorMaterial::type() const -{ - static QSGMaterialType type; - return &type; -} - -QSGMaterialShader *QSGSmoothColorMaterial::createShader() const -{ - return new SmoothColorMaterialShader; -} - -QSGDefaultRectangleNode::QSGDefaultRectangleNode() -{ - setMaterial(&m_material); -} - -void QSGDefaultRectangleNode::updateMaterialAntialiasing() -{ - if (m_antialiasing) - setMaterial(&m_smoothMaterial); - else - setMaterial(&m_material); -} - -void QSGDefaultRectangleNode::updateMaterialBlending(QSGNode::DirtyState *state) -{ - // smoothed material is always blended, so no change in material state - if (material() == &m_material) { - bool wasBlending = (m_material.flags() & QSGMaterial::Blending); - bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque) - || (m_color.alpha() < 255 && m_color.alpha() != 0) - || (m_pen_width > 0 && m_border_color.alpha() < 255); - if (wasBlending != isBlending) { - m_material.setFlag(QSGMaterial::Blending, isBlending); - *state |= QSGNode::DirtyMaterial; - } - } -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h deleted file mode 100644 index f30a3beed7..0000000000 --- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - - -#ifndef QSGDEFAULTRECTANGLENODE_P_H -#define QSGDEFAULTRECTANGLENODE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGContext; - -class Q_QUICK_PRIVATE_EXPORT QSGSmoothColorMaterial : public QSGMaterial -{ -public: - QSGSmoothColorMaterial(); - - int compare(const QSGMaterial *other) const; - -protected: - QSGMaterialType *type() const override; - QSGMaterialShader *createShader() const override; -}; - -class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGBasicRectangleNode -{ -public: - QSGDefaultRectangleNode(); - -private: - void updateMaterialAntialiasing() override; - void updateMaterialBlending(QSGNode::DirtyState *state) override; - - QSGVertexColorMaterial m_material; - QSGSmoothColorMaterial m_smoothMaterial; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index d8dfd01e7a..5b13b7449a 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -52,7 +52,10 @@ HEADERS += \ $$PWD/util/qsgsimplematerial.h \ $$PWD/util/qsgtexturematerial.h \ $$PWD/util/qsgtexturematerial_p.h \ - $$PWD/util/qsgvertexcolormaterial.h + $$PWD/util/qsgvertexcolormaterial.h \ + $$PWD/util/qsgrectanglenode.h \ + $$PWD/util/qsgimagenode.h \ + $$PWD/util/qsgninepatchnode.h SOURCES += \ $$PWD/util/qsgareaallocator.cpp \ @@ -65,7 +68,11 @@ SOURCES += \ $$PWD/util/qsgflatcolormaterial.cpp \ $$PWD/util/qsgsimplematerial.cpp \ $$PWD/util/qsgtexturematerial.cpp \ - $$PWD/util/qsgvertexcolormaterial.cpp + $$PWD/util/qsgvertexcolormaterial.cpp \ + $$PWD/util/qsgrectanglenode.cpp \ + $$PWD/util/qsgimagenode.cpp \ + $$PWD/util/qsgninepatchnode.cpp + contains(QT_CONFIG, opengl(es1|es2)?) { HEADERS += \ $$PWD/util/qsgdepthstencilbuffer_p.h \ @@ -83,8 +90,8 @@ HEADERS += \ $$PWD/qsgadaptationlayer_p.h \ $$PWD/qsgcontext_p.h \ $$PWD/qsgcontextplugin_p.h \ - $$PWD/qsgbasicrectanglenode_p.h \ - $$PWD/qsgbasicimagenode_p.h \ + $$PWD/qsgbasicinternalrectanglenode_p.h \ + $$PWD/qsgbasicinternalimagenode_p.h \ $$PWD/qsgbasicglyphnode_p.h \ $$PWD/qsgrenderloop_p.h @@ -92,8 +99,8 @@ SOURCES += \ $$PWD/qsgadaptationlayer.cpp \ $$PWD/qsgcontext.cpp \ $$PWD/qsgcontextplugin.cpp \ - $$PWD/qsgbasicrectanglenode.cpp \ - $$PWD/qsgbasicimagenode.cpp \ + $$PWD/qsgbasicinternalrectanglenode.cpp \ + $$PWD/qsgbasicinternalimagenode.cpp \ $$PWD/qsgbasicglyphnode.cpp \ $$PWD/qsgrenderloop.cpp @@ -104,11 +111,14 @@ contains(QT_CONFIG, opengl(es1|es2)?) { $$PWD/qsgdefaultdistancefieldglyphcache.cpp \ $$PWD/qsgdistancefieldglyphnode.cpp \ $$PWD/qsgdistancefieldglyphnode_p.cpp \ - $$PWD/qsgdefaultimagenode.cpp \ - $$PWD/qsgdefaultrectanglenode.cpp \ + $$PWD/qsgdefaultinternalimagenode.cpp \ + $$PWD/qsgdefaultinternalrectanglenode.cpp \ $$PWD/qsgdefaultrendercontext.cpp \ $$PWD/qsgdefaultcontext.cpp \ $$PWD/util/qsgdefaultpainternode.cpp \ + $$PWD/util/qsgdefaultrectanglenode.cpp \ + $$PWD/util/qsgdefaultimagenode.cpp \ + $$PWD/util/qsgdefaultninepatchnode.cpp \ $$PWD/qsgdefaultlayer.cpp \ $$PWD/qsgthreadedrenderloop.cpp \ $$PWD/qsgwindowsrenderloop.cpp @@ -118,11 +128,14 @@ contains(QT_CONFIG, opengl(es1|es2)?) { $$PWD/qsgdistancefieldglyphnode_p.h \ $$PWD/qsgdistancefieldglyphnode_p_p.h \ $$PWD/qsgdefaultglyphnode_p_p.h \ - $$PWD/qsgdefaultimagenode_p.h \ - $$PWD/qsgdefaultrectanglenode_p.h \ + $$PWD/qsgdefaultinternalimagenode_p.h \ + $$PWD/qsgdefaultinternalrectanglenode_p.h \ $$PWD/qsgdefaultrendercontext_p.h \ $$PWD/qsgdefaultcontext_p.h \ $$PWD/util/qsgdefaultpainternode_p.h \ + $$PWD/util/qsgdefaultrectanglenode_p.h \ + $$PWD/util/qsgdefaultimagenode_p.h \ + $$PWD/util/qsgdefaultninepatchnode_p.h \ $$PWD/qsgdefaultlayer_p.h \ $$PWD/qsgthreadedrenderloop_p.h \ $$PWD/qsgwindowsrenderloop_p.h diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode.cpp b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp new file mode 100644 index 0000000000..ed3d73adb4 --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgdefaultimagenode_p.h" +#include + +QT_BEGIN_NAMESPACE + +QSGDefaultImageNode::QSGDefaultImageNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) + , m_texCoordMode(QSGDefaultImageNode::NoTransform) + , m_isAtlasTexture(false) + , m_ownsTexture(false) +{ + setGeometry(&m_geometry); + setMaterial(&m_material); + setOpaqueMaterial(&m_opaque_material); + m_material.setMipmapFiltering(QSGTexture::None); + m_opaque_material.setMipmapFiltering(QSGTexture::None); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("image")); +#endif +} + +QSGDefaultImageNode::~QSGDefaultImageNode() +{ + if (m_ownsTexture) + delete m_material.texture(); +} + +void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.filtering() == filtering) + return; + + m_material.setFiltering(filtering); + m_opaque_material.setFiltering(filtering); + markDirty(DirtyMaterial); +} + +QSGTexture::Filtering QSGDefaultImageNode::filtering() const +{ + return m_material.filtering(); +} + +void QSGDefaultImageNode::setRect(const QRectF &r) +{ + if (m_rect == r) + return; + + m_rect = r; + rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyGeometry); +} + +QRectF QSGDefaultImageNode::rect() const +{ + return m_rect; +} + +void QSGDefaultImageNode::setSourceRect(const QRectF &r) +{ + if (m_sourceRect == r) + return; + + m_sourceRect = r; + rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyGeometry); +} + +QRectF QSGDefaultImageNode::sourceRect() const +{ + return m_sourceRect; +} + +void QSGDefaultImageNode::setTexture(QSGTexture *texture) +{ + Q_ASSERT(texture); + if (m_ownsTexture) + delete m_material.texture(); + m_material.setTexture(texture); + m_opaque_material.setTexture(texture); + rebuildGeometry(&m_geometry, texture, m_rect, m_sourceRect, m_texCoordMode); + + DirtyState dirty = DirtyMaterial; + // It would be tempting to skip the extra bit here and instead use + // m_material.texture to get the old state, but that texture could + // have been deleted in the mean time. + bool wasAtlas = m_isAtlasTexture; + m_isAtlasTexture = texture->isAtlasTexture(); + if (wasAtlas || m_isAtlasTexture) + dirty |= DirtyGeometry; + markDirty(dirty); +} + +QSGTexture *QSGDefaultImageNode::texture() const +{ + return m_material.texture(); +} + +void QSGDefaultImageNode::setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) +{ + if (m_texCoordMode == mode) + return; + m_texCoordMode = mode; + rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyMaterial); +} + +QSGDefaultImageNode::TextureCoordinatesTransformMode QSGDefaultImageNode::textureCoordinatesTransform() const +{ + return m_texCoordMode; +} + +void QSGDefaultImageNode::setOwnsTexture(bool owns) +{ + m_ownsTexture = owns; +} + +bool QSGDefaultImageNode::ownsTexture() const +{ + return m_ownsTexture; +} + +void QSGDefaultImageNode::rebuildGeometry(QSGGeometry *g, + QSGTexture *texture, + const QRectF &rect, + QRectF sourceRect, + TextureCoordinatesTransformMode texCoordMode) +{ + if (!texture) + return; + + if (!sourceRect.width() || !sourceRect.height()) { + QSize ts = texture->textureSize(); + sourceRect = QRectF(0, 0, ts.width(), ts.height()); + } + + // Maybe transform the texture coordinates + if (texCoordMode.testFlag(QSGImageNode::MirrorHorizontally)) { + float tmp = sourceRect.left(); + sourceRect.setLeft(sourceRect.right()); + sourceRect.setRight(tmp); + } + if (texCoordMode.testFlag(QSGImageNode::MirrorVertically)) { + float tmp = sourceRect.top(); + sourceRect.setTop(sourceRect.bottom()); + sourceRect.setBottom(tmp); + } + + QSGGeometry::updateTexturedRectGeometry(g, rect, texture->convertToNormalizedSourceRect(sourceRect)); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h new file mode 100644 index 0000000000..5316542cff --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTIMAGENODE_P_H +#define QSGDEFAULTIMAGENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGImageNode +{ +public: + QSGDefaultImageNode(); + ~QSGDefaultImageNode(); + + void setRect(const QRectF &rect) override; + QRectF rect() const override; + + void setSourceRect(const QRectF &r) override; + QRectF sourceRect() const override; + + void setTexture(QSGTexture *texture) override; + QSGTexture *texture() const override; + + void setFiltering(QSGTexture::Filtering filtering) override; + QSGTexture::Filtering filtering() const override; + + void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) override; + TextureCoordinatesTransformMode textureCoordinatesTransform() const override; + + void setOwnsTexture(bool owns) override; + bool ownsTexture() const override; + + static void rebuildGeometry(QSGGeometry *g, + QSGTexture *texture, + const QRectF &rect, + QRectF sourceRect, + TextureCoordinatesTransformMode texCoordMode); + +private: + QSGGeometry m_geometry; + QSGOpaqueTextureMaterial m_opaque_material; + QSGTextureMaterial m_material; + QRectF m_rect; + QRectF m_sourceRect; + TextureCoordinatesTransformMode m_texCoordMode; + uint m_isAtlasTexture : 1; + uint m_ownsTexture : 1; +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTIMAGENODE_P_H diff --git a/src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp b/src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp new file mode 100644 index 0000000000..e5a53a3617 --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgdefaultninepatchnode_p.h" + +QT_BEGIN_NAMESPACE + +QSGDefaultNinePatchNode::QSGDefaultNinePatchNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) +{ + m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip); + setGeometry(&m_geometry); + setMaterial(&m_material); +} + +QSGDefaultNinePatchNode::~QSGDefaultNinePatchNode() +{ + delete m_material.texture(); +} + +void QSGDefaultNinePatchNode::setTexture(QSGTexture *texture) +{ + delete m_material.texture(); + m_material.setTexture(texture); +} + +void QSGDefaultNinePatchNode::setBounds(const QRectF &bounds) +{ + m_bounds = bounds; +} + +void QSGDefaultNinePatchNode::setDevicePixelRatio(qreal ratio) +{ + m_devicePixelRatio = ratio; +} + +void QSGDefaultNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) +{ + m_padding = QVector4D(left, top, right, bottom); +} + +void QSGDefaultNinePatchNode::update() +{ + rebuildGeometry(m_material.texture(), &m_geometry, m_padding, m_bounds, m_devicePixelRatio); + markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial); +} + +void QSGDefaultNinePatchNode::rebuildGeometry(QSGTexture *texture, QSGGeometry *geometry, const QVector4D &padding, + const QRectF &bounds, qreal dpr) +{ + if (padding.x() <= 0 && padding.y() <= 0 && padding.z() <= 0 && padding.w() <= 0) { + geometry->allocate(4, 0); + QSGGeometry::updateTexturedRectGeometry(geometry, bounds, texture->normalizedTextureSubRect()); + return; + } + + QRectF tc = texture->normalizedTextureSubRect(); + QSize ts = texture->textureSize(); + ts.setHeight(ts.height() / dpr); + ts.setWidth(ts.width() / dpr); + + qreal invtw = tc.width() / ts.width(); + qreal invth = tc.height() / ts.height(); + + struct Coord { qreal p; qreal t; }; + Coord cx[4] = { { bounds.left(), tc.left() }, + { bounds.left() + padding.x(), tc.left() + padding.x() * invtw }, + { bounds.right() - padding.z(), tc.right() - padding.z() * invtw }, + { bounds.right(), tc.right() } + }; + Coord cy[4] = { { bounds.top(), tc.top() }, + { bounds.top() + padding.y(), tc.top() + padding.y() * invth }, + { bounds.bottom() - padding.w(), tc.bottom() - padding.w() * invth }, + { bounds.bottom(), tc.bottom() } + }; + + geometry->allocate(16, 28); + QSGGeometry::TexturedPoint2D *v = geometry->vertexDataAsTexturedPoint2D(); + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + v->set(cx[x].p, cy[y].p, cx[x].t, cy[y].t); + ++v; + } + } + + quint16 *i = geometry->indexDataAsUShort(); + for (int r = 0; r < 3; ++r) { + if (r > 0) + *i++ = 4 * r; + for (int c = 0; c < 4; ++c) { + i[0] = 4 * r + c; + i[1] = 4 * r + c + 4; + i += 2; + } + if (r < 2) + *i++ = 4 * r + 3 + 4; + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h b/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h new file mode 100644 index 0000000000..675cf48f47 --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTNINEPATCHNODE_P_H +#define QSGDEFAULTNINEPATCHNODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGDefaultNinePatchNode : public QSGNinePatchNode +{ +public: + QSGDefaultNinePatchNode(); + ~QSGDefaultNinePatchNode(); + + void setTexture(QSGTexture *texture) override; + void setBounds(const QRectF &bounds) override; + void setDevicePixelRatio(qreal ratio) override; + void setPadding(qreal left, qreal top, qreal right, qreal bottom) override; + void update() override; + + static void rebuildGeometry(QSGTexture *texture, QSGGeometry *geometry, const QVector4D &padding, + const QRectF &bounds, qreal dpr); + +private: + QRectF m_bounds; + qreal m_devicePixelRatio; + QVector4D m_padding; + QSGGeometry m_geometry; + QSGTextureMaterial m_material; +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTNINEPATCHNODE_P_H diff --git a/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp new file mode 100644 index 0000000000..41db4f1c03 --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgdefaultrectanglenode_p.h" +#include "qsgflatcolormaterial.h" + +QT_BEGIN_NAMESPACE + +// Unlike our predecessor, QSGSimpleRectNode, use QSGVertexColorMaterial +// instead of Flat in order to allow better batching in the renderer. + +QSGDefaultRectangleNode::QSGDefaultRectangleNode() + : m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 4) + , m_color(QColor(255, 255, 255)) +{ + QSGGeometry::updateColoredRectGeometry(&m_geometry, QRectF()); + setMaterial(&m_material); + setGeometry(&m_geometry); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("rectangle")); +#endif +} + +void QSGDefaultRectangleNode::setRect(const QRectF &rect) +{ + QSGGeometry::updateColoredRectGeometry(&m_geometry, rect); + markDirty(QSGNode::DirtyGeometry); +} + +QRectF QSGDefaultRectangleNode::rect() const +{ + const QSGGeometry::ColoredPoint2D *pts = m_geometry.vertexDataAsColoredPoint2D(); + return QRectF(pts[0].x, + pts[0].y, + pts[3].x - pts[0].x, + pts[3].y - pts[0].y); +} + +void QSGDefaultRectangleNode::setColor(const QColor &color) +{ + if (color != m_color) { + m_color = color; + QSGGeometry::ColoredPoint2D *pts = m_geometry.vertexDataAsColoredPoint2D(); + for (int i = 0; i < 4; ++i) { + pts[i].r = uchar(qRound(m_color.redF() * m_color.alphaF() * 255)); + pts[i].g = uchar(qRound(m_color.greenF() * m_color.alphaF() * 255)); + pts[i].b = uchar(qRound(m_color.blueF() * m_color.alphaF() * 255)); + pts[i].a = uchar(qRound(m_color.alphaF() * 255)); + } + markDirty(QSGNode::DirtyGeometry); + } +} + +QColor QSGDefaultRectangleNode::color() const +{ + return m_color; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/util/qsgdefaultrectanglenode_p.h new file mode 100644 index 0000000000..965aa8dabb --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultrectanglenode_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTRECTANGLENODE_P_H +#define QSGDEFAULTRECTANGLENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGDefaultRectangleNode : public QSGRectangleNode +{ +public: + QSGDefaultRectangleNode(); + + void setRect(const QRectF &rect) override; + QRectF rect() const override; + + void setColor(const QColor &color) override; + QColor color() const override; + +private: + QSGVertexColorMaterial m_material; + QSGGeometry m_geometry; + QColor m_color; +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTRECTANGLENODE_P_H diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp index f4d86bba04..988dcffbd8 100644 --- a/src/quick/scenegraph/util/qsgengine.cpp +++ b/src/quick/scenegraph/util/qsgengine.cpp @@ -221,4 +221,45 @@ QSGRendererInterface *QSGEngine::rendererInterface() const : nullptr; } +/*! + Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null. + + This is cross-backend alternative to constructing a QSGSimpleRectNode directly. + + \since 5.8 + \sa QSGRectangleNode + */ +QSGRectangleNode *QSGEngine::createRectangleNode() const +{ + Q_D(const QSGEngine); + return d->sgRenderContext->isValid() ? d->sgRenderContext->sceneGraphContext()->createRectangleNode() : nullptr; +} + +/*! + Creates a simple image node. When the scenegraph is not initialized, the return value is null. + + This is cross-backend alternative to constructing a QSGSimpleTextureNode directly. + + \since 5.8 + \sa QSGImageNode + */ + +QSGImageNode *QSGEngine::createImageNode() const +{ + Q_D(const QSGEngine); + return d->sgRenderContext->isValid() ? d->sgRenderContext->sceneGraphContext()->createImageNode() : nullptr; +} + +/*! + Creates a nine patch node. When the scenegraph is not initialized, the return value is null. + + \since 5.8 + */ + +QSGNinePatchNode *QSGEngine::createNinePatchNode() const +{ + Q_D(const QSGEngine); + return d->sgRenderContext->isValid() ? d->sgRenderContext->sceneGraphContext()->createNinePatchNode() : nullptr; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h index 9d27fb5525..3c8b61852e 100644 --- a/src/quick/scenegraph/util/qsgengine.h +++ b/src/quick/scenegraph/util/qsgengine.h @@ -50,6 +50,9 @@ class QSGAbstractRenderer; class QSGEnginePrivate; class QSGTexture; class QSGRendererInterface; +class QSGRectangleNode; +class QSGImageNode; +class QSGNinePatchNode; class Q_QUICK_EXPORT QSGEngine : public QObject { @@ -74,6 +77,9 @@ public: QSGTexture *createTextureFromImage(const QImage &image, CreateTextureOptions options = CreateTextureOption()) const; QSGTexture *createTextureFromId(uint id, const QSize &size, CreateTextureOptions options = CreateTextureOption()) const; QSGRendererInterface *rendererInterface() const; + QSGRectangleNode *createRectangleNode() const; + QSGImageNode *createImageNode() const; + QSGNinePatchNode *createNinePatchNode() const; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgimagenode.cpp b/src/quick/scenegraph/util/qsgimagenode.cpp new file mode 100644 index 0000000000..0387efc62b --- /dev/null +++ b/src/quick/scenegraph/util/qsgimagenode.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgimagenode.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSGImageNode + \brief The QSGImageNode class is provided for convenience to easily draw + textured content using the QML scene graph. + + \inmodule QtQuick + \since 5.8 + + \warning The image node class must have a texture before being + added to the scene graph to be rendered. + */ + +/*! + \fn void QSGImageNode::setRect(const QRectF &rect) + + Sets the target rect of this image node to \a r. + */ + +/*! + \fn void QSGImageNode::setRect(qreal x, qreal y, qreal w, qreal h) + \overload + + Sets the rectangle of this image node to begin at (\a x, \a y) and have + width \a w and height \a h. + */ + +/*! + \fn QRectF QSGImageNode::rect() const + + Returns the target rect of this image node. + */ + +/*! + \fn void QSGImageNode::setSourceRect(const QRectF &rect) + + Sets the source rect of this image node to \a r. + */ + +/*! + \fn void QSGImageNode::setSourceRect(qreal x, qreal y, qreal w, qreal h) + \overload + + Sets the rectangle of this image node to show its texture from (\a x, \a y) and + have width \a w and height \a h relatively to the QSGTexture::textureSize. + */ + +/*! + \fn QRectF QSGImageNode::sourceRect() const + + Returns the source rect of this image node. + */ + +/*! + \fn void QSGImageNode::setTexture(QSGTexture *texture) + + Sets the texture of this image node to \a texture. + + Use setOwnsTexture() to set whether the node should take + ownership of the texture. By default, the node does not + take ownership. + + \warning An image node must have a texture before being added to the + scenegraph to be rendered. + */ + +/*! + \fn QSGTexture *QSGImageNode::texture() const + + Returns the texture for this image node. + */ + +/*! + \fn void QSGImageNode::setFiltering(QSGTexture::Filtering filtering) + + Sets the filtering to be used for this image node to \a filtering. + + For smooth scaling, use QSGTexture::Linear. For normal scaling, use + QSGTexture::Nearest. + */ + +/*! + \fn QSGTexture::Filtering QSGImageNode::filtering() const + + Returns the filtering for this image node. + */ + +/*! + \enum QSGImageNode::TextureCoordinatesTransformFlag + + The TextureCoordinatesTransformFlag enum is used to specify the mode used + to generate texture coordinates for a textured quad. + + \value NoTransform Texture coordinates are oriented with window coordinates + i.e. with origin at top-left. + + \value MirrorHorizontally Texture coordinates are inverted in the horizontal axis with + respect to window coordinates + + \value MirrorVertically Texture coordinates are inverted in the vertical axis with + respect to window coordinates + */ + +/*! + \fn void QSGImageNode::setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) + + Sets the method used to generate texture coordinates to \a mode. This can + be used to obtain correct orientation of the texture. This is commonly + needed when using a third-party OpenGL library to render to texture as + OpenGL has an inverted y-axis relative to Qt Quick. + */ + +/*! + \fn QSGImageNode::TextureCoordinatesTransformMode textureCoordinatesTransform() const + + Returns the mode used to generate texture coordinates for this node. + */ + +/*! + \fn void QSGImageNode::setOwnsTexture(bool owns) + + Sets whether the node takes ownership of the texture to \a owns. + + By default, the node does not take ownership of the texture. + */ + +/*! + \fn bool QSGImageNode::ownsTexture() const + + \return \c true if the node takes ownership of the texture; otherwise \c false. + */ + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgimagenode.h b/src/quick/scenegraph/util/qsgimagenode.h new file mode 100644 index 0000000000..a094924ac3 --- /dev/null +++ b/src/quick/scenegraph/util/qsgimagenode.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGIMAGENODE_H +#define QSGIMAGENODE_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGImageNode : public QSGGeometryNode +{ +public: + virtual ~QSGImageNode() { } + + virtual void setRect(const QRectF &rect) = 0; + inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); } + virtual QRectF rect() const = 0; + + virtual void setSourceRect(const QRectF &r) = 0; + inline void setSourceRect(qreal x, qreal y, qreal w, qreal h) { setSourceRect(QRectF(x, y, w, h)); } + virtual QRectF sourceRect() const = 0; + + virtual void setTexture(QSGTexture *texture) = 0; + virtual QSGTexture *texture() const = 0; + + virtual void setFiltering(QSGTexture::Filtering filtering) = 0; + virtual QSGTexture::Filtering filtering() const = 0; + + enum TextureCoordinatesTransformFlag { + NoTransform = 0x00, + MirrorHorizontally = 0x01, + MirrorVertically = 0x02 + }; + Q_DECLARE_FLAGS(TextureCoordinatesTransformMode, TextureCoordinatesTransformFlag) + + virtual void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) = 0; + virtual TextureCoordinatesTransformMode textureCoordinatesTransform() const = 0; + + virtual void setOwnsTexture(bool owns) = 0; + virtual bool ownsTexture() const = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGImageNode::TextureCoordinatesTransformMode) + +QT_END_NAMESPACE + +#endif // QSGIMAGENODE_H diff --git a/src/quick/scenegraph/util/qsgninepatchnode.cpp b/src/quick/scenegraph/util/qsgninepatchnode.cpp new file mode 100644 index 0000000000..9c167ca76f --- /dev/null +++ b/src/quick/scenegraph/util/qsgninepatchnode.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgninepatchnode.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSGNinePatchNode + \inmodule QtQuick + \since 5.8 + \internal + */ + +/*! + \fn void QSGNinePatchNode::setTexture(QSGTexture *texture) + \internal + */ + +/*! + \fn void QSGNinePatchNode::setBounds(const QRectF &bounds) + \internal + */ + +/*! + \fn void QSGNinePatchNode::setDevicePixelRatio(qreal ratio) + \internal + */ + +/*! + \fn void QSGNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) + \internal + */ + + +/*! + \fn void QSGNinePatchNode::update() + \internal + */ + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgninepatchnode.h b/src/quick/scenegraph/util/qsgninepatchnode.h new file mode 100644 index 0000000000..8677a432ba --- /dev/null +++ b/src/quick/scenegraph/util/qsgninepatchnode.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGNINEPATCHNODE_H +#define QSGNINEPATCHNODE_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGNinePatchNode : public QSGGeometryNode +{ +public: + virtual ~QSGNinePatchNode() { } + + virtual void setTexture(QSGTexture *texture) = 0; + virtual void setBounds(const QRectF &bounds) = 0; + virtual void setDevicePixelRatio(qreal ratio) = 0; + virtual void setPadding(qreal left, qreal top, qreal right, qreal bottom) = 0; + virtual void update() = 0; +}; + +QT_END_NAMESPACE + +#endif // QSGNINEPATCHNODE_H diff --git a/src/quick/scenegraph/util/qsgrectanglenode.cpp b/src/quick/scenegraph/util/qsgrectanglenode.cpp new file mode 100644 index 0000000000..38c1f16a63 --- /dev/null +++ b/src/quick/scenegraph/util/qsgrectanglenode.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgrectanglenode.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSGRectangleNode + + \brief The QSGRectangleNode class is a convenience class for drawing + solid filled rectangles using scenegraph. + \inmodule QtQuick + \since 5.8 + */ + +/*! + \fn void QSGRectangleNode::setRect(const QRectF &rect) + + Sets the rectangle of this rect node to \a rect. + */ + +/*! + \fn void QSGRectangleNode::setRect(qreal x, qreal y, qreal w, qreal h) + \overload + + Sets the rectangle of this rect node to begin at (\a x, \a y) and have + width \a w and height \a h. + */ + +/*! + \fn QRectF QSGRectangleNode::rect() const + + Returns the rectangle that this rect node covers. + */ + +/*! + \fn void QSGRectangleNode::setColor(const QColor &color) + + Sets the color of this rectangle to \a color. The default color will be + white. + */ + +/*! + \fn QColor QSGRectangleNode::color() const + + Returns the color of this rectangle. + */ + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgrectanglenode.h b/src/quick/scenegraph/util/qsgrectanglenode.h new file mode 100644 index 0000000000..8e0da1d9c7 --- /dev/null +++ b/src/quick/scenegraph/util/qsgrectanglenode.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGRECTANGLENODE_H +#define QSGRECTANGLENODE_H + +#include + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGRectangleNode : public QSGGeometryNode +{ +public: + virtual ~QSGRectangleNode() { } + + virtual void setRect(const QRectF &rect) = 0; + inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); } + virtual QRectF rect() const = 0; + + virtual void setColor(const QColor &color) = 0; + virtual QColor color() const = 0; +}; + +QT_END_NAMESPACE + +#endif // QSGRECTANGLENODE_H diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.cpp b/src/quick/scenegraph/util/qsgsimplerectnode.cpp index 3f6b8b0eec..e20c051f9a 100644 --- a/src/quick/scenegraph/util/qsgsimplerectnode.cpp +++ b/src/quick/scenegraph/util/qsgsimplerectnode.cpp @@ -49,6 +49,10 @@ QT_BEGIN_NAMESPACE solid filled rectangles using scenegraph. \inmodule QtQuick + \warning This utility class is only functional when running with the OpenGL + or software backends of the Qt Quick scenegraph. For a proper cross-platform + alternative prefer using QSGSimpleRectangleNode via + QQuickWindow::createSimpleRectangleNode() or QSGEngine::createSimpleRectangleNode(). */ diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp index 1208a6bc72..4ed0445ce1 100644 --- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp @@ -97,6 +97,11 @@ static void qsgsimpletexturenode_update(QSGGeometry *g, \warning The simple texture node class must have a texture before being added to the scene graph to be rendered. + + \warning This utility class is only functional when running with the OpenGL + or software backends of the Qt Quick scenegraph. For a proper cross-platform + alternative prefer using QSGSimpleImageNode via + QQuickWindow::createSimpleImageNode() or QSGEngine::createSimpleImageNode(). */ /*! diff --git a/tests/auto/quick/nokeywords/tst_nokeywords.cpp b/tests/auto/quick/nokeywords/tst_nokeywords.cpp index e25cd9535b..6c94b484ae 100644 --- a/tests/auto/quick/nokeywords/tst_nokeywords.cpp +++ b/tests/auto/quick/nokeywords/tst_nokeywords.cpp @@ -51,8 +51,8 @@ #ifndef QT_NO_OPENGL #include #include -#include -#include +#include +#include #include #include #include -- cgit v1.2.3 From 8188c652c192c958217546e4dc155a7c33c17a9f Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Wed, 22 Jun 2016 17:50:23 +0300 Subject: Fix a missing override declaration. Change-Id: Iea2fec595e588571ed3d9a5c843404d52cd24c92 Reviewed-by: Simon Hausmann --- src/qml/jit/qv4assembler_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index f0063aae06..d53e0b0454 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -83,7 +83,7 @@ struct CompilationUnit : public QV4::CompiledData::CompilationUnit void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; - bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); + bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; // Coderef + execution engine -- cgit v1.2.3 From b90f810ffa5a7f98b0ac58e5812bcdcd66d028bc Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 21 Jun 2016 14:36:55 -0700 Subject: Use QDateTime::currentMSecsSinceEpoch() instead of currentDateTime More efficient if all you want is the number of seconds. Change-Id: Ib57b52598e2f452985e9fffd145a36f2b3c703ea Reviewed-by: Erik Verbruggen --- src/qml/doc/src/cppintegration/definetypes.qdoc | 2 +- src/quick/items/context2d/qquickcanvasitem.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index 593feb49e7..e06451b2bc 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -603,7 +603,7 @@ public: RandomNumberGenerator(QObject *parent) : QObject(parent), m_maxValue(100) { - qsrand(QDateTime::currentDateTime().toTime_t()); + qsrand(QDateTime::currentMSecsSinceEpoch() / 1000); QObject::connect(&m_timer, SIGNAL(timeout()), SLOT(updateProperty())); m_timer.start(500); } diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index b3b5144eb3..89a68e4f33 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -711,7 +711,7 @@ void QQuickCanvasItem::updatePolish() for (auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) { QV4::ScopedFunctionObject f(scope, it.value().value()); - callData->args[0] = QV4::Primitive::fromUInt32(QDateTime::currentDateTimeUtc().toTime_t()); + callData->args[0] = QV4::Primitive::fromUInt32(QDateTime::currentMSecsSinceEpoch() / 1000); f->call(callData); } } -- cgit v1.2.3 From fb15206453faa8db26feb197dac3d16a8d6dfa1b Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Tue, 21 Jun 2016 13:40:15 +0200 Subject: ungrab touch points if the MultiPointTouchArea is hidden or disabled This caused MPTA to not emit onCanceled and caused the touch points 'pressed' property to not become 'false' after the MPTA was hidden or disabled. We now ungrab the touch points where we already ungrabbed the mouse. Change-Id: I90a5d4fa4b3fa470b8b60881c80418e79061f001 Task-number: QTBUG-42928 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 4 ++ .../qquickmultipointtoucharea/data/cancel.qml | 41 ++++++++++++++++ .../tst_qquickmultipointtoucharea.cpp | 55 ++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 tests/auto/quick/qquickmultipointtoucharea/data/cancel.qml diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index d0f5f162fc..1c7a556540 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -5715,6 +5715,8 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); if (windowPriv->mouseGrabberItem == q) q->ungrabMouse(); + if (!effectiveVisible) + q->ungrabTouchPoints(); } bool childVisibilityChanged = false; @@ -5763,6 +5765,8 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); if (windowPriv->mouseGrabberItem == q) q->ungrabMouse(); + if (!effectiveEnable) + q->ungrabTouchPoints(); if (scope && !effectiveEnable && activeFocus) { windowPriv->clearFocusInScope( scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem); diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/cancel.qml b/tests/auto/quick/qquickmultipointtoucharea/data/cancel.qml new file mode 100644 index 0000000000..e108003bca --- /dev/null +++ b/tests/auto/quick/qquickmultipointtoucharea/data/cancel.qml @@ -0,0 +1,41 @@ +import QtQuick 2.0 + +MultiPointTouchArea { + width: 240 + height: 320 + + minimumTouchPoints: 1 + maximumTouchPoints: 5 + touchPoints: [ + TouchPoint { objectName: "point1" }, + TouchPoint { objectName: "point2" }, + TouchPoint { objectName: "point3" }, + TouchPoint { objectName: "point4" }, + TouchPoint { objectName: "point5" } + ] + + function clearCounts() { + touchPointPressCount = 0; + touchPointUpdateCount = 0; + touchPointReleaseCount = 0; + touchPointCancelCount = 0; + touchCount = 0; + touchUpdatedHandled = false; + } + + property int touchPointPressCount: 0 + property int touchPointUpdateCount: 0 + property int touchPointReleaseCount: 0 + property int touchPointCancelCount: 0 + property int touchCount: 0 + property bool touchUpdatedHandled: false + + onPressed: { touchPointPressCount = touchPoints.length } + onUpdated: { touchPointUpdateCount = touchPoints.length } + onReleased: { touchPointReleaseCount = touchPoints.length } + onCanceled: { touchPointCancelCount = touchPoints.length } + onTouchUpdated: { + touchCount = touchPoints.length + touchUpdatedHandled = true + } +} diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 4da4767d7b..2f432e57bc 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -74,6 +74,7 @@ private slots: void transformedTouchArea(); void mouseInteraction(); void mouseInteraction_data(); + void cancel(); private: QQuickView *createAndShowView(const QString &file); @@ -1200,6 +1201,60 @@ void tst_QQuickMultiPointTouchArea::mouseInteraction() QCOMPARE(area->property("touchCount").toInt(), 0); } +void tst_QQuickMultiPointTouchArea::cancel() +{ + QScopedPointer window(createAndShowView("cancel.qml")); + QVERIFY(window->rootObject() != 0); + + QQuickMultiPointTouchArea *area = qobject_cast(window->rootObject()); + QTest::QTouchEventSequence sequence = QTest::touchEvent(window.data(), device); + QQuickTouchPoint *point1 = area->findChild("point1"); + + QPoint p1(20,100); + sequence.press(0, p1).commit(); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(point1->pressed(), true); + QCOMPARE(area->property("touchPointPressCount").toInt(), 1); + QCOMPARE(area->property("touchPointUpdateCount").toInt(), 0); + QCOMPARE(area->property("touchPointReleaseCount").toInt(), 0); + QCOMPARE(area->property("touchPointCancelCount").toInt(), 0); + QCOMPARE(area->property("touchCount").toInt(), 1); + QMetaObject::invokeMethod(area, "clearCounts"); + + area->setVisible(false); + // we should get a onCancel signal + QCOMPARE(point1->pressed(), false); + QCOMPARE(area->property("touchPointPressCount").toInt(), 0); + QCOMPARE(area->property("touchPointUpdateCount").toInt(), 0); + QCOMPARE(area->property("touchPointReleaseCount").toInt(), 0); + QCOMPARE(area->property("touchPointCancelCount").toInt(), 1); + QCOMPARE(area->property("touchCount").toInt(), 0); + QMetaObject::invokeMethod(area, "clearCounts"); + area->setVisible(true); + + + sequence.press(0, p1).commit(); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(point1->pressed(), true); + QCOMPARE(area->property("touchPointPressCount").toInt(), 1); + QCOMPARE(area->property("touchPointUpdateCount").toInt(), 0); + QCOMPARE(area->property("touchPointReleaseCount").toInt(), 0); + QCOMPARE(area->property("touchPointCancelCount").toInt(), 0); + QCOMPARE(area->property("touchCount").toInt(), 1); + QMetaObject::invokeMethod(area, "clearCounts"); + + area->setEnabled(false); + // we should get a onCancel signal + QCOMPARE(point1->pressed(), false); + QCOMPARE(area->property("touchPointPressCount").toInt(), 0); + QCOMPARE(area->property("touchPointUpdateCount").toInt(), 0); + QCOMPARE(area->property("touchPointReleaseCount").toInt(), 0); + QCOMPARE(area->property("touchPointCancelCount").toInt(), 1); + QCOMPARE(area->property("touchCount").toInt(), 0); + QMetaObject::invokeMethod(area, "clearCounts"); + +} + QTEST_MAIN(tst_QQuickMultiPointTouchArea) -- cgit v1.2.3 From 6c16a50a51c1bafad71ab48c19ce98d57c005d25 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Tue, 21 Jun 2016 09:45:06 +0200 Subject: Rename qt.scenegraph.info to qt.scenegraph.general If configured with logging rules (QT_LOGGING_RULES=), qt.scenegraph.info=true will be interpreted as enabling QtInfoMsg for the qt.scenegraph category and subcategories. [ChangeLog][QtQuick] qt.scenegraph.info logging category got renamed to qt.scenegraph.general. Task-number: QTBUG-54238 Change-Id: I7601e522697c3b4b00b6e9866b803d91f72e76fc Reviewed-by: Gunnar Sletta Reviewed-by: Shawn Rutledge --- src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc | 4 ++-- src/quick/scenegraph/qsgcontext.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc index 516630d034..caedd72dc5 100644 --- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc @@ -180,7 +180,7 @@ dedicated thread. Qt attempts to choose a suitable loop based on the platform and possibly the graphics drivers in use. When this is not satisfactory, or for testing purposes, the environment variable \c QSG_RENDER_LOOP can be used to force the usage of a given loop. To -verify which render loop is in use, enable the \c qt.scenegraph.info +verify which render loop is in use, enable the \c qt.scenegraph.general \l {QLoggingCategory}{logging category}. \note The \c threaded and \c windows render loops rely on the OpenGL @@ -368,7 +368,7 @@ addition to being helpful to Qt contributors. \li \c {qt.scenegraph.time.glyph} - logs the time spent preparing distance field glyphs -\li \c {qt.scenegraph.info} - logs general information about various parts of the scene graph and the graphics stack +\li \c {qt.scenegraph.general} - logs general information about various parts of the scene graph and the graphics stack \li \c {qt.scenegraph.renderloop} - creates a detailed log of the various stages involved in rendering. This log mode is primarily useful for developers working on Qt. diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index dd6977e42e..be228e87c7 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -86,7 +86,7 @@ QT_BEGIN_NAMESPACE // Used for very high-level info about the renderering and gl context // Includes GL_VERSION, type of render loop, atlas size, etc. -Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.info") +Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.general") // Used to debug the renderloop logic. Primarily useful for platform integrators // and when investigating the render loop logic. -- cgit v1.2.3 From a3935184c4dbeda4f46e8140c9f75af1e58189f1 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Fri, 10 Jun 2016 15:13:31 +0200 Subject: QQuickWidget: Fix crash when there's an offscreen window, but no real window context Fix crash when creating a QQuickWidget where the offscreenWindow context is valid, but the window shareContext is not. This happens e.g. when QWebEngineWidgets is loaded, that creates a globally shared context. Task-number: QTBUG-54020 Change-Id: Ieeba0c20d12cce220b22cdd76adaf87d1ab2649e Reviewed-by: Joerg Bornemann Reviewed-by: Laszlo Agocs --- src/quickwidgets/qquickwidget.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 6709d88fa3..7f869c6ba5 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -778,9 +778,10 @@ void QQuickWidget::createFramebufferObject() return; } - if (context->shareContext() != QWidgetPrivate::get(window())->shareContext()) { - context->setShareContext(QWidgetPrivate::get(window())->shareContext()); - context->setScreen(context->shareContext()->screen()); + QOpenGLContext *shareWindowContext = QWidgetPrivate::get(window())->shareContext(); + if (shareWindowContext && context->shareContext() != shareWindowContext) { + context->setShareContext(shareWindowContext); + context->setScreen(shareWindowContext->screen()); if (!context->create()) qWarning("QQuickWidget: Failed to recreate context"); // The screen may be different so we must recreate the offscreen surface too. -- cgit v1.2.3 From 69e04aa5e1a7627c5cbe4f5613fc6b2cc9f7bcea Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Wed, 1 Jun 2016 12:12:15 +0200 Subject: Doc: QML Qt.callLater(): Fix documentation warnings qqmlbuiltinfunctions.cpp:2014: warning: Cannot find file to quote from: 'doc/src/snippets/qml/qtLater.qml' qqmlbuiltinfunctions.cpp:2001: warning: Can't link to 'QML:Qt::callLater()' Change-Id: I003494dba9aa6401825f8ec1b9adb6b99d7c45ff Reviewed-by: Robin Burchell --- src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index a0dff482b1..76a900c846 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -2003,16 +2003,16 @@ ReturnedValue GlobalExtensions::method_string_arg(CallContext *ctx) \since 5.8 Use this function to eliminate redundant calls to a function or signal. -The function passed as the first argument to \l{QML:Qt::callLater()}{Qt.callLater()} +The function passed as the first argument to Qt.callLater() will be called later, once the QML engine returns to the event loop. When this function is called multiple times in quick succession with the same function as its first argument, that function will be called only once. For example: -\snippet doc/src/snippets/qml/qtLater.qml 0 +\snippet qml/qtLater.qml 0 -Any additional arguments passed to \l{QML:Qt::callLater()}{Qt.callLater()} will +Any additional arguments passed to Qt.callLater() will be passed on to the function invoked. Note that if redundant calls are eliminated, then only the last set of arguments will be passed to the function. -- cgit v1.2.3 From 8d35bf976ea3283ef458dc40d6477013b9064583 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 22 Jun 2016 15:28:20 +0200 Subject: Remove misleading out of date comments in QQuickWindowPrivate Task-number: QTBUG-54133 Change-Id: I68e41c3d6c066745058db0c15984d680d6c05ee9 Reviewed-by: Andy Nichols --- src/quick/items/qquickwindow_p.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 787958ef86..33aa021d98 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -234,8 +234,6 @@ public: uint clearBeforeRendering : 1; - // Currently unused in the default implementation, as we're not stopping - // rendering when obscured as we should... uint persistentGLContext : 1; uint persistentSceneGraph : 1; -- cgit v1.2.3 From 3da510a2610e2c361ddacd6d877869164d1e5e58 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 23 Jun 2016 14:08:13 +0200 Subject: Privately export QQuickListView and QQuickPathView The Tumbler in Qt Quick Controls 2 needs this. Change-Id: If42d6a7f56e0b0944379192e8074480b27c05ba9 Reviewed-by: Robin Burchell --- src/quick/items/qquicklistview_p.h | 2 +- src/quick/items/qquickpathview_p.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h index b8ab38bbba..7e86a47433 100644 --- a/src/quick/items/qquicklistview_p.h +++ b/src/quick/items/qquicklistview_p.h @@ -103,7 +103,7 @@ private: class QQmlInstanceModel; class QQuickListViewAttached; -class Q_AUTOTEST_EXPORT QQuickListView : public QQuickItemView +class Q_QUICK_PRIVATE_EXPORT QQuickListView : public QQuickItemView { Q_OBJECT Q_DECLARE_PRIVATE(QQuickListView) diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h index daec965f02..bdd56b6311 100644 --- a/src/quick/items/qquickpathview_p.h +++ b/src/quick/items/qquickpathview_p.h @@ -53,6 +53,7 @@ #include "qquickitem.h" +#include #include QT_BEGIN_NAMESPACE @@ -61,7 +62,7 @@ class QQmlChangeSet; class QQuickPathViewPrivate; class QQuickPathViewAttached; -class Q_AUTOTEST_EXPORT QQuickPathView : public QQuickItem +class Q_QUICK_PRIVATE_EXPORT QQuickPathView : public QQuickItem { Q_OBJECT -- cgit v1.2.3 From 199ac27505468f222105008fe2729cf559600e2b Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Thu, 23 Jun 2016 13:48:45 +0200 Subject: Doc: Fix documentation warnings for QSGImageNode qsgimagenode.cpp:56: warning: Undocumented parameter 'rect' in QSGImageNode::setRect() qsgimagenode.cpp:76: warning: Undocumented parameter 'rect' in QSGImageNode::setSourceRect() Change-Id: I8ea167199e3a4ab014ac83513e43c4716747603c Reviewed-by: Nico Vertriest --- src/quick/scenegraph/util/qsgimagenode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/scenegraph/util/qsgimagenode.cpp b/src/quick/scenegraph/util/qsgimagenode.cpp index 0387efc62b..fbc4314bd8 100644 --- a/src/quick/scenegraph/util/qsgimagenode.cpp +++ b/src/quick/scenegraph/util/qsgimagenode.cpp @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE /*! \fn void QSGImageNode::setRect(const QRectF &rect) - Sets the target rect of this image node to \a r. + Sets the target rect of this image node to \a rect. */ /*! @@ -76,7 +76,7 @@ QT_BEGIN_NAMESPACE /*! \fn void QSGImageNode::setSourceRect(const QRectF &rect) - Sets the source rect of this image node to \a r. + Sets the source rect of this image node to \a rect. */ /*! -- cgit v1.2.3 From 9ab8b9e4bdf313bb4304be206dd479d4d0848211 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 5 Apr 2016 13:43:03 +0200 Subject: QML: Do not register static QML dependencies on every call. QML objects can be re-parented on the fly, resulting in different dependencies for expressions like 'parent.width'. So, because of this, dependencies are cleared and re-calculated after every binding evaluation. However, dependencies on properties of the scope and context objects cannot change, because these objects do not get changed for the life-time of a binding. So we can permanently register them. This is only done for bindings, not for functions, because those might be conditionally executed. According to valgrind, this is a reduction of ~186 instructions on x86 for every evaluation of: Item { height: width } Change-Id: Ib095497323d4f08caf712d480007e2627a176369 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 4 ++- src/qml/compiler/qv4codegen.cpp | 1 + src/qml/compiler/qv4jsir.cpp | 1 + src/qml/compiler/qv4jsir_p.h | 3 ++- src/qml/jsruntime/qv4functionobject.cpp | 2 +- src/qml/qml/qqmljavascriptexpression.cpp | 43 ++++++++++++++++++++++++-------- src/qml/qml/qqmljavascriptexpression_p.h | 13 +++++++--- 7 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 4fa63e3c84..c31a7bce4f 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1890,7 +1890,9 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int // Look for IDs first. foreach (const IdMapping &mapping, _idObjects) if (name == mapping.name) { - _function->idObjectDependencies.insert(mapping.idIndex); + if (_function->isQmlBinding) + _function->idObjectDependencies.insert(mapping.idIndex); + QV4::IR::Expr *s = _block->MEMBER(_block->TEMP(_qmlContextTemp), _function->newString(name), 0, QV4::IR::Member::MemberOfIdObjectsArray, mapping.idIndex); QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); _block->MOVE(result, s); diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 8711bc049a..7a444e8c57 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1961,6 +1961,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); function->isStrict = _env->isStrict; function->isNamedExpression = _env->isNamedFunctionExpression; + function->isQmlBinding = _env->compilationMode == QmlBinding; AST::SourceLocation loc = ast->firstSourceLocation(); function->line = loc.startLine; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index b994d2f707..74c4f91f2b 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -368,6 +368,7 @@ Function::Function(Module *module, Function *outer, const QString &name) , isNamedExpression(false) , hasTry(false) , hasWith(false) + , isQmlBinding(false) , unused(0) , line(-1) , column(-1) diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 0254ae224d..c196e8127b 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -1258,7 +1258,8 @@ struct Function { uint isNamedExpression : 1; uint hasTry: 1; uint hasWith: 1; - uint unused : 25; + uint isQmlBinding: 1; + uint unused : 24; // Location of declaration in source code (-1 if not specified) int line; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 041044930b..fce80c46eb 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -435,7 +435,7 @@ void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *call Scoped f(scope, static_cast(that)); - InternalClass *ic = scope.engine->emptyClass; + InternalClass *ic = v4->emptyClass; ScopedObject proto(scope, f->protoForConstructor()); ScopedObject obj(scope, v4->newObject(ic, proto)); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index eff1a6e039..7fb66a49bf 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -106,7 +106,8 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() m_nextExpression->m_prevExpression = m_prevExpression; } - clearGuards(); + clearActiveGuards(); + clearPermanentGuards(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = 0; } @@ -114,12 +115,12 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) { activeGuards.setFlagValue(v); - if (!v) clearGuards(); + if (!v) clearActiveGuards(); } void QQmlJavaScriptExpression::resetNotifyOnValueChanged() { - clearGuards(); + clearActiveGuards(); } void QQmlJavaScriptExpression::setContext(QQmlContextData *context) @@ -214,7 +215,7 @@ void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefin ep->propertyCapture = lastPropertyCapture; } -void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) +void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration) { if (watcher->wasDeleted()) return; @@ -234,14 +235,17 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) g->connect(n); } - expression->activeGuards.prepend(g); + if (duration == OnlyOnce) + expression->permanentGuards.prepend(g); + else + expression->activeGuards.prepend(g); } /*! \internal \a n is in the signal index range (see QObjectPrivate::signalIndex()). */ -void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n) +void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration) { if (watcher->wasDeleted()) return; @@ -280,7 +284,10 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n) g->connect(o, n, engine); } - expression->activeGuards.prepend(g); + if (duration == Permanently) + expression->permanentGuards.prepend(g); + else + expression->activeGuards.prepend(g); } } @@ -297,6 +304,11 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct if (!capture) return; + if (capture->expression->m_permanentDependenciesRegistered) + return; + + capture->expression->m_permanentDependenciesRegistered = true; + QV4::Scoped context(scope, engine->qmlContext()); QQmlContextData *qmlContext = context->qmlContext(); @@ -304,7 +316,8 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) { Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount); - capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings); + capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings, + QQmlPropertyCapture::Permanently); } Q_ASSERT(qmlContext->contextObject); @@ -313,7 +326,8 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct for (int i = 0; i < contextPropertyDependencyCount; ++i) { const int propertyIndex = *contextPropertyDependency++; const int notifyIndex = *contextPropertyDependency++; - capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex); + capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex, + QQmlPropertyCapture::Permanently); } QObject *scopeObject = context->qmlScope(); @@ -322,7 +336,8 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct for (int i = 0; i < scopePropertyDependencyCount; ++i) { const int propertyIndex = *scopePropertyDependency++; const int notifyIndex = *scopePropertyDependency++; - capture->captureProperty(scopeObject, propertyIndex, notifyIndex); + capture->captureProperty(scopeObject, propertyIndex, notifyIndex, + QQmlPropertyCapture::Permanently); } } @@ -406,12 +421,18 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject * } -void QQmlJavaScriptExpression::clearGuards() +void QQmlJavaScriptExpression::clearActiveGuards() { while (QQmlJavaScriptExpressionGuard *g = activeGuards.takeFirst()) g->Delete(); } +void QQmlJavaScriptExpression::clearPermanentGuards() +{ + while (QQmlJavaScriptExpressionGuard *g = permanentGuards.takeFirst()) + g->Delete(); +} + void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **) { QQmlJavaScriptExpression *expression = diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 4472a643ff..a5c7a80153 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -136,7 +136,8 @@ public: inline bool hasDelayedError() const; QQmlError error(QQmlEngine *) const; void clearError(); - void clearGuards(); + void clearActiveGuards(); + void clearPermanentGuards(); QQmlDelayedError *delayedError(); static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope, @@ -157,10 +158,12 @@ private: // activeGuards:flag2 - useSharedContext QBiPointer m_scopeObject; QForwardFieldList activeGuards; + QForwardFieldList permanentGuards; QQmlContextData *m_context; QQmlJavaScriptExpression **m_prevExpression; QQmlJavaScriptExpression *m_nextExpression; + bool m_permanentDependenciesRegistered = false; protected: QV4::PersistentValue m_function; @@ -177,10 +180,14 @@ public: Q_ASSERT(errorString == 0); } - void captureProperty(QQmlNotifier *); - void captureProperty(QObject *, int, int); + enum Duration { + OnlyOnce, + Permanently + }; static void registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope); + void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce); + void captureProperty(QObject *, int, int, Duration duration = OnlyOnce); QQmlEngine *engine; QQmlJavaScriptExpression *expression; -- cgit v1.2.3 From f291ceaea3d9fffe6794415d8e5ecf44225097fb Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 15 Apr 2016 11:24:26 +0200 Subject: QML: Store idObjectDependencies in a QVarLengthArray. The number of id objects referenced from a function or binding is often very small. So using a QSet that allocates hash nodes takes more memory, and is actually slower than using an array. Change-Id: Id9fa60a860314790284f66318593b3ef938e6eef Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compiler.cpp | 6 ++++-- src/qml/compiler/qv4jsir_p.h | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index b801009f4e..55d2501024 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -382,8 +382,10 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *ir // write QML dependencies quint32 *writtenDeps = (quint32 *)(f + function->dependingIdObjectsOffset); - foreach (int id, irFunction->idObjectDependencies) - *writtenDeps++ = id; + for (int id : irFunction->idObjectDependencies) { + Q_ASSERT(id >= 0); + *writtenDeps++ = static_cast(id); + } writtenDeps = (quint32 *)(f + function->dependingContextPropertiesOffset); for (auto property : irFunction->contextObjectPropertyDependencies) { diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index c196e8127b..a2bd6ad044 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -1193,6 +1193,20 @@ private: unsigned _isRemoved : 1; }; +template +class SmallSet: public QVarLengthArray +{ +public: + void insert(int value) + { + for (auto it : *this) { + if (it == value) + return; + } + this->append(value); + } +}; + // Map from meta property index (existence implies dependency) to notify signal index struct KeyValuePair { @@ -1266,7 +1280,7 @@ struct Function { int column; // Qml extension: - QSet idObjectDependencies; + SmallSet idObjectDependencies; PropertyDependencyMap contextObjectPropertyDependencies; PropertyDependencyMap scopeObjectPropertyDependencies; -- cgit v1.2.3 From e7e34c727298f310698dfe476dda14461606a7e5 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Thu, 23 Jun 2016 15:18:42 +0200 Subject: QSGEngine: Fix build with QT_NO_OPENGL The wrong type of #ifdef check was being used for no opengl. Change-Id: I4c80e149ec03c5dacfcf079de91cdb662d06007e Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/util/qsgengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp index 988dcffbd8..ad1fcfa470 100644 --- a/src/quick/scenegraph/util/qsgengine.cpp +++ b/src/quick/scenegraph/util/qsgengine.cpp @@ -116,7 +116,7 @@ QSGEngine::~QSGEngine() void QSGEngine::initialize(QOpenGLContext *context) { Q_D(QSGEngine); -#ifdef QT_NO_OPENGL +#ifndef QT_NO_OPENGL if (context && QOpenGLContext::currentContext() != context) { qWarning("WARNING: The context must be current before calling QSGEngine::initialize."); return; -- cgit v1.2.3 From c9d4c8ed97694defea78184a80874764ebedfda1 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 24 Jun 2016 10:22:47 +0200 Subject: Create QmlEngine lazy in case one is not needed QQuickWidget may be used with just a root item, and won't need a QmlEngine in that case. So if one isn't given to the constructor, only create one when one is needed for evaluating source. Change-Id: I96cfe5e2473d5d53fc2d52d4646d36c43f4ccb8a Reviewed-by: Laszlo Agocs --- src/quickwidgets/qquickwidget.cpp | 28 ++++++++++++---------- src/quickwidgets/qquickwidget_p.h | 1 + .../quickwidgets/qquickwidget/tst_qquickwidget.cpp | 9 +++++++ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 2f43582529..05097b3ce2 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -96,10 +96,7 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) engine = e; - if (engine.isNull()) - engine = new QQmlEngine(q); - - if (!engine.data()->incubationController()) + if (!engine.isNull() && !engine.data()->incubationController()) engine.data()->setIncubationController(offscreenWindow->incubationController()); #ifndef QT_NO_DRAGANDDROP @@ -112,6 +109,16 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate())); } +void QQuickWidgetPrivate::ensureEngine() +{ + Q_Q(QQuickWidget); + if (!engine.isNull()) + return; + + engine = new QQmlEngine(q); + engine.data()->setIncubationController(offscreenWindow->incubationController()); +} + void QQuickWidgetPrivate::invalidateRenderControl() { if (!context) // this is not an error, could be called before creating the context, or multiple times @@ -167,10 +174,7 @@ QQuickWidgetPrivate::~QQuickWidgetPrivate() void QQuickWidgetPrivate::execute() { Q_Q(QQuickWidget); - if (!engine) { - qWarning() << "QQuickWidget: invalid qml engine."; - return; - } + ensureEngine(); if (root) { delete root; @@ -404,7 +408,6 @@ QQuickWidget::QQuickWidget(QQmlEngine* engine, QWidget *parent) { setMouseTracking(true); setFocusPolicy(Qt::StrongFocus); - Q_ASSERT(engine); d_func()->init(engine); } @@ -548,7 +551,7 @@ QQmlContext* QQuickWidget::rootContext() const QQuickWidget::Status QQuickWidget::status() const { Q_D(const QQuickWidget); - if (!d->engine) + if (!d->engine && !d->source.isEmpty()) return QQuickWidget::Error; if (!d->component) @@ -574,11 +577,12 @@ QList QQuickWidget::errors() const if (d->component) errs = d->component->errors(); - if (!d->engine) { + if (!d->engine && !d->source.isEmpty()) { QQmlError error; error.setDescription(QLatin1String("QQuickWidget: invalid qml engine.")); errs << error; - } else if (d->component && d->component->status() == QQmlComponent::Ready && !d->root) { + } + if (d->component && d->component->status() == QQmlComponent::Ready && !d->root) { QQmlError error; error.setDescription(QLatin1String("QQuickWidget: invalid root object.")); errs << error; diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h index fd3ef8fbbf..f0e1f848e3 100644 --- a/src/quickwidgets/qquickwidget_p.h +++ b/src/quickwidgets/qquickwidget_p.h @@ -105,6 +105,7 @@ public: QImage grabFramebuffer() Q_DECL_OVERRIDE; void init(QQmlEngine* e = 0); + void ensureEngine(); void handleWindowChange(); void invalidateRenderControl(); diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index 09359060f6..9b4d0dd7d1 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -55,6 +55,7 @@ private slots: void readback(); void renderingSignals(); void grabBeforeShow(); + void nullEngine(); }; @@ -301,6 +302,14 @@ void tst_qquickwidget::grabBeforeShow() QVERIFY(!widget.grab().isNull()); } +void tst_qquickwidget::nullEngine() +{ + QQuickWidget widget; + QVERIFY(widget.engine() == Q_NULLPTR); + QVERIFY(widget.errors().isEmpty()); + QCOMPARE(widget.status(), QQuickWidget::Null); +} + QTEST_MAIN(tst_qquickwidget) #include "tst_qquickwidget.moc" -- cgit v1.2.3 From a88fb838854daabe175440c010fcc99a094939c3 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Thu, 23 Jun 2016 15:38:12 +0200 Subject: Fix rendering clipped texture and image nodes The entire texture node was always used causing scaling for any node with a source clip. Change-Id: I7c6f946cee6d59ad3ae64302f3a28782eba8f5da Reviewed-by: Andy Nichols --- src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp | 4 ++-- .../scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp index 53974a77bf..c8291edd10 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp @@ -73,10 +73,10 @@ void QSGSoftwareImageNode::paint(QPainter *painter) { if (QSGSoftwarePixmapTexture *pt = dynamic_cast(m_texture)) { const QPixmap &pm = pt->pixmap(); - painter->drawPixmap(m_rect, pm, QRectF(0, 0, pm.width(), pm.height())); + painter->drawPixmap(m_rect, pm, m_sourceRect); } else if (QSGPlainTexture *pt = dynamic_cast(m_texture)) { const QImage &im = pt->image(); - painter->drawImage(m_rect, im, QRectF(0, 0, im.width(), im.height())); + painter->drawImage(m_rect, im, m_sourceRect); } } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 571f242b67..7cfbc2dfda 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -223,10 +223,10 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu QSGTexture *texture = m_handle.simpleTextureNode->texture(); if (QSGSoftwarePixmapTexture *pt = dynamic_cast(texture)) { const QPixmap &pm = pt->pixmap(); - painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, QRectF(0, 0, pm.width(), pm.height())); + painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, m_handle.simpleTextureNode->sourceRect()); } else if (QSGPlainTexture *pt = dynamic_cast(texture)) { const QImage &im = pt->image(); - painter->drawImage(m_handle.simpleTextureNode->rect(), im, QRectF(0, 0, im.width(), im.height())); + painter->drawImage(m_handle.simpleTextureNode->rect(), im, m_handle.simpleTextureNode->sourceRect()); } } break; -- cgit v1.2.3 From 4412d22c26ba4a187837b0565c66e5b51de85b3c Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 24 Jun 2016 14:44:11 +0200 Subject: Enable QQuickWidget for no-OpenGL builds Enables and fixes QQuickWidget so it compiles in no-opengl builds. Change-Id: Ib50dec1d1429ec1140257dd6dcd9784126e61852 Reviewed-by: Andy Nichols --- src/quickwidgets/qquickwidget.cpp | 36 +++++++++++++++++++++++++++++++++--- src/quickwidgets/qquickwidget_p.h | 7 +++++++ src/src.pro | 3 ++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index d6de552017..775f6f5698 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -59,9 +59,11 @@ #include #include +#ifndef QT_NO_OPENGL #include #include #include +#endif #include #include @@ -74,7 +76,9 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_OPENGL extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); +#endif class QQuickWidgetRenderControl : public QQuickRenderControl { @@ -104,9 +108,11 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) useSoftwareRenderer = true; if (!useSoftwareRenderer) { +#ifndef QT_NO_OPENGL if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface)) setRenderToTexture(); else +#endif qWarning("QQuickWidget is not supported on this platform."); } @@ -130,6 +136,7 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) void QQuickWidgetPrivate::invalidateRenderControl() { +#ifndef QT_NO_OPENGL if (!useSoftwareRenderer) { if (!context) // this is not an error, could be called before creating the context, or multiple times return; @@ -140,6 +147,7 @@ void QQuickWidgetPrivate::invalidateRenderControl() return; } } +#endif renderControl->invalidate(); } @@ -157,9 +165,11 @@ QQuickWidgetPrivate::QQuickWidgetPrivate() , offscreenWindow(0) , offscreenSurface(0) , renderControl(0) +#ifndef QT_NO_OPENGL , fbo(0) , resolvedFbo(0) , context(0) +#endif , resizeMode(QQuickWidget::SizeViewToRootObject) , initialSize(0,0) , eventPending(false) @@ -178,6 +188,7 @@ QQuickWidgetPrivate::~QQuickWidgetPrivate() delete renderControl; delete offscreenWindow; } else { +#ifndef QT_NO_OPENGL // context and offscreenSurface are current at this stage, if the context was created. Q_ASSERT(!context || (QOpenGLContext::currentContext() == context && context->surface() == offscreenSurface)); delete renderControl; // always delete the rendercontrol first @@ -186,6 +197,7 @@ QQuickWidgetPrivate::~QQuickWidgetPrivate() delete fbo; destroyContext(); +#endif } } @@ -230,6 +242,7 @@ void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRec void QQuickWidgetPrivate::render(bool needsSync) { if (!useSoftwareRenderer) { +#ifndef QT_NO_OPENGL // createFramebufferObject() bails out when the size is empty. In this case // we cannot render either. if (!fbo) @@ -259,6 +272,7 @@ void QQuickWidgetPrivate::render(bool needsSync) static_cast(context->functions())->flushShared(); QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0; +#endif } else { //Software Renderer if (needsSync) { @@ -313,10 +327,12 @@ void QQuickWidgetPrivate::renderSceneGraph() QImage QQuickWidgetPrivate::grabFramebuffer() { if (!useSoftwareRenderer) { +#ifndef QT_NO_OPENGL if (!context) return QImage(); context->makeCurrent(offscreenSurface); +#endif } return renderControl->grab(); } @@ -759,6 +775,7 @@ void QQuickWidgetPrivate::handleContextCreationFailure(const QSurfaceFormat &for // Never called by Software Rendering backend void QQuickWidgetPrivate::createContext() { +#ifndef QT_NO_OPENGL Q_Q(QQuickWidget); // On hide-show we invalidate() but our context is kept. // We nonetheless need to initialize() again. @@ -798,6 +815,7 @@ void QQuickWidgetPrivate::createContext() if (context->makeCurrent(offscreenSurface)) renderControl->initialize(context); else +#endif qWarning("QQuickWidget: Failed to make context current"); } @@ -806,8 +824,10 @@ void QQuickWidgetPrivate::destroyContext() { delete offscreenSurface; offscreenSurface = 0; +#ifndef QT_NO_OPENGL delete context; context = 0; +#endif } void QQuickWidget::createFramebufferObject() @@ -826,6 +846,7 @@ void QQuickWidget::createFramebufferObject() return; } +#ifndef QT_NO_OPENGL QOpenGLContext *context = d->offscreenWindow->openglContext(); if (!context) { @@ -898,6 +919,7 @@ void QQuickWidget::createFramebufferObject() // Having one would mean create() was called and platforms that only support // a single native window were in trouble. Q_ASSERT(!d->offscreenWindow->handle()); +#endif } void QQuickWidget::destroyFramebufferObject() @@ -909,10 +931,12 @@ void QQuickWidget::destroyFramebufferObject() return; } +#ifndef QT_NO_OPENGL delete d->fbo; d->fbo = 0; delete d->resolvedFbo; d->resolvedFbo = 0; +#endif } QQuickWidget::ResizeMode QQuickWidget::resizeMode() const @@ -990,6 +1014,7 @@ void QQuickWidgetPrivate::setRootObject(QObject *obj) } } +#ifndef QT_NO_OPENGL GLuint QQuickWidgetPrivate::textureId() const { Q_Q(const QQuickWidget); @@ -1001,6 +1026,7 @@ GLuint QQuickWidgetPrivate::textureId() const return resolvedFbo ? resolvedFbo->texture() : (fbo ? fbo->texture() : 0); } +#endif /*! \internal @@ -1090,7 +1116,7 @@ void QQuickWidget::resizeEvent(QResizeEvent *e) createFramebufferObject(); } } else { - +#ifndef QT_NO_OPENGL if (d->context) { // Bail out when receiving a resize after scenegraph invalidation. This can happen // during hide - resize - show sequences and also during application exit. @@ -1112,7 +1138,7 @@ void QQuickWidget::resizeEvent(QResizeEvent *e) qWarning("QQuickWidget::resizeEvent() no OpenGL context"); return; } - +#endif } d->render(needsSync); @@ -1305,7 +1331,11 @@ bool QQuickWidget::event(QEvent *e) break; case QEvent::ScreenChangeInternal: - if (d->fbo || d->useSoftwareRenderer) { + if (d->useSoftwareRenderer +#ifndef QT_NO_OPENGL + || d->fbo +#endif + ) { // This will check the size taking the devicePixelRatio into account // and recreate if needed. createFramebufferObject(); diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h index 5c35093c58..9e66087295 100644 --- a/src/quickwidgets/qquickwidget_p.h +++ b/src/quickwidgets/qquickwidget_p.h @@ -101,8 +101,12 @@ public: QObject *focusObject() Q_DECL_OVERRIDE; +#ifndef QT_NO_OPENGL GLuint textureId() const Q_DECL_OVERRIDE; QImage grabFramebuffer() Q_DECL_OVERRIDE; +#else + QImage grabFramebuffer(); +#endif void init(QQmlEngine* e = 0); void handleWindowChange(); @@ -120,9 +124,12 @@ public: QQuickWindow *offscreenWindow; QOffscreenSurface *offscreenSurface; QQuickRenderControl *renderControl; + +#ifndef QT_NO_OPENGL QOpenGLFramebufferObject *fbo; QOpenGLFramebufferObject *resolvedFbo; QOpenGLContext *context; +#endif QQuickWidget::ResizeMode resizeMode; QSize initialSize; diff --git a/src/src.pro b/src/src.pro index 04fa8663bb..fbc4feaec4 100644 --- a/src/src.pro +++ b/src/src.pro @@ -7,9 +7,10 @@ SUBDIRS += \ qtHaveModule(gui):contains(QT_CONFIG, opengl(es1|es2)?) { SUBDIRS += particles - qtHaveModule(widgets): SUBDIRS += quickwidgets } +qtHaveModule(gui): qtHaveModule(widgets): SUBDIRS += quickwidgets + SUBDIRS += \ plugins \ imports \ -- cgit v1.2.3 From 886480688dbebf428eaa387a9f52ceaf7b0ae9d9 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 9 Jun 2016 16:26:05 +0200 Subject: D3D12: Reintroduce the single threaded render loop ...and use it by default. Use QSG_RENDER_LOOP=threaded to request the threaded one (but prepare for potential DXGI deadlocks, usually when multiple windows are involved). Change-Id: I1372b4a8f2388e08515899792e2a9c347b31faf8 Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/d3d12.pro | 2 + .../scenegraph/d3d12/qsgd3d12adaptation.cpp | 11 + .../scenegraph/d3d12/qsgd3d12renderloop.cpp | 1143 +++---------------- .../scenegraph/d3d12/qsgd3d12renderloop_p.h | 35 +- .../d3d12/qsgd3d12threadedrenderloop.cpp | 1187 ++++++++++++++++++++ .../d3d12/qsgd3d12threadedrenderloop_p.h | 129 +++ 6 files changed, 1516 insertions(+), 991 deletions(-) create mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp create mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h diff --git a/src/plugins/scenegraph/d3d12/d3d12.pro b/src/plugins/scenegraph/d3d12/d3d12.pro index c814c11de0..5d1b7a4946 100644 --- a/src/plugins/scenegraph/d3d12/d3d12.pro +++ b/src/plugins/scenegraph/d3d12/d3d12.pro @@ -12,6 +12,7 @@ QMAKE_TARGET_DESCRIPTION = "Quick D3D12 Renderer for Qt." SOURCES += \ $$PWD/qsgd3d12adaptation.cpp \ $$PWD/qsgd3d12renderloop.cpp \ + $$PWD/qsgd3d12threadedrenderloop.cpp \ $$PWD/qsgd3d12renderer.cpp \ $$PWD/qsgd3d12context.cpp \ $$PWD/qsgd3d12rendercontext.cpp \ @@ -33,6 +34,7 @@ NO_PCH_SOURCES += \ HEADERS += \ $$PWD/qsgd3d12adaptation_p.h \ $$PWD/qsgd3d12renderloop_p.h \ + $$PWD/qsgd3d12threadedrenderloop_p.h \ $$PWD/qsgd3d12renderer_p.h \ $$PWD/qsgd3d12context_p.h \ $$PWD/qsgd3d12rendercontext_p.h \ diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp index 2762177e5d..b93da0ae01 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp @@ -39,6 +39,7 @@ #include "qsgd3d12adaptation_p.h" #include "qsgd3d12renderloop_p.h" +#include "qsgd3d12threadedrenderloop_p.h" #include "qsgd3d12context_p.h" QT_BEGIN_NAMESPACE @@ -68,6 +69,16 @@ QSGContextFactoryInterface::Flags QSGD3D12Adaptation::flags(const QString &) con QSGRenderLoop *QSGD3D12Adaptation::createWindowManager() { + static bool threaded = false; + static bool envChecked = false; + if (!envChecked) { + envChecked = true; + threaded = qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("threaded"); + } + + if (threaded) + return new QSGD3D12ThreadedRenderLoop; + return new QSGD3D12RenderLoop; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 067b0d35f6..288a7ce4ad 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -42,16 +42,9 @@ #include "qsgd3d12context_p.h" #include "qsgd3d12rendercontext_p.h" #include "qsgd3d12shadereffectnode_p.h" -#include #include #include -#include -#include -#include -#include #include -#include -#include QT_BEGIN_NAMESPACE @@ -64,641 +57,16 @@ QT_BEGIN_NAMESPACE DECLARE_DEBUG_VAR(loop) DECLARE_DEBUG_VAR(time) -/* - The D3D render loop mostly mirrors the threaded OpenGL render loop. - - There are two classes here. QSGD3D12RenderLoop and QSGD3D12RenderThread. All - communication between the two is based on event passing and we have a number - of custom events. - - Render loop is per process, render thread is per window. The - QSGD3D12RenderContext and QSGD3D12Engine are per window as well. The former - is created (but not owned) by QQuickWindow. The D3D device is per process. - - In this implementation, the render thread is never blocked and the GUI - thread will initiate a polishAndSync which will block and wait for the - render thread to pick it up and release the block only after the render - thread is done syncing. The reason for this is: - - 1. Clear blocking paradigm. We only have one real "block" point - (polishAndSync()) and all blocking is initiated by GUI and picked up by - Render at specific times based on events. This makes the execution - deterministic. - - 2. Render does not have to interact with GUI. This is done so that the - render thread can run its own animation system which stays alive even when - the GUI thread is blocked doing I/O, object instantiation, QPainter-painting - or any other non-trivial task. - - The render thread has affinity to the GUI thread until a window is shown. - From that moment and until the window is destroyed, it will have affinity to - the render thread. (moved back at the end of run for cleanup). - */ - -// Passed from the RL to the RT when a window is removed obscured and should be -// removed from the render loop. -const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1); - -// Passed from the RL to RT when GUI has been locked, waiting for sync. -const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2); - -// Passed by the RT to itself to trigger another render pass. This is typically -// a result of QQuickWindow::update(). -const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3); - -// Passed by the RL to the RT to maybe release resource if no windows are -// rendering. -const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4); - -// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called. -const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5); - -// Passed by the window when there is a render job to run. -const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6); - -class QSGD3D12WindowEvent : public QEvent -{ -public: - QSGD3D12WindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { } - QQuickWindow *window; -}; - -class QSGD3D12TryReleaseEvent : public QSGD3D12WindowEvent -{ -public: - QSGD3D12TryReleaseEvent(QQuickWindow *win, bool destroy) - : QSGD3D12WindowEvent(win, WM_TryRelease), destroying(destroy) { } - bool destroying; -}; - -class QSGD3D12SyncEvent : public QSGD3D12WindowEvent -{ -public: - QSGD3D12SyncEvent(QQuickWindow *c, bool inExpose, bool force) - : QSGD3D12WindowEvent(c, WM_RequestSync) - , size(c->size()) - , dpr(c->effectiveDevicePixelRatio()) - , syncInExpose(inExpose) - , forceRenderPass(force) { } - QSize size; - float dpr; - bool syncInExpose; - bool forceRenderPass; -}; - -class QSGD3D12GrabEvent : public QSGD3D12WindowEvent -{ -public: - QSGD3D12GrabEvent(QQuickWindow *c, QImage *result) - : QSGD3D12WindowEvent(c, WM_Grab), image(result) { } - QImage *image; -}; - -class QSGD3D12JobEvent : public QSGD3D12WindowEvent -{ -public: - QSGD3D12JobEvent(QQuickWindow *c, QRunnable *postedJob) - : QSGD3D12WindowEvent(c, WM_PostJob), job(postedJob) { } - ~QSGD3D12JobEvent() { delete job; } - QRunnable *job; -}; - -class QSGD3D12EventQueue : public QQueue -{ -public: - void addEvent(QEvent *e) { - mutex.lock(); - enqueue(e); - if (waiting) - condition.wakeOne(); - mutex.unlock(); - } - - QEvent *takeEvent(bool wait) { - mutex.lock(); - if (isEmpty() && wait) { - waiting = true; - condition.wait(&mutex); - waiting = false; - } - QEvent *e = dequeue(); - mutex.unlock(); - return e; - } - - bool hasMoreEvents() { - mutex.lock(); - bool has = !isEmpty(); - mutex.unlock(); - return has; - } - -private: - QMutex mutex; - QWaitCondition condition; - bool waiting = false; -}; - -static inline int qsgrl_animation_interval() -{ - const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0; - return refreshRate < 1 ? 16 : int(1000 / refreshRate); -} - -class QSGD3D12RenderThread : public QThread -{ - Q_OBJECT - -public: - QSGD3D12RenderThread(QSGD3D12RenderLoop *rl, QSGRenderContext *renderContext) - : renderLoop(rl) - { - rc = static_cast(renderContext); - vsyncDelta = qsgrl_animation_interval(); - } - - ~QSGD3D12RenderThread() - { - delete rc; - } - - bool event(QEvent *e); - void run(); - - void syncAndRender(); - void sync(bool inExpose); - - void requestRepaint() - { - if (sleeping) - stopEventProcessing = true; - if (exposedWindow) - pendingUpdate |= RepaintRequest; - } - - void processEventsAndWaitForMore(); - void processEvents(); - void postEvent(QEvent *e); - - enum UpdateRequest { - SyncRequest = 0x01, - RepaintRequest = 0x02, - ExposeRequest = 0x04 | RepaintRequest | SyncRequest - }; - - QSGD3D12Engine *engine = nullptr; - QSGD3D12RenderLoop *renderLoop; - QSGD3D12RenderContext *rc; - QAnimationDriver *rtAnim = nullptr; - volatile bool active = false; - uint pendingUpdate = 0; - bool sleeping = false; - bool syncResultedInChanges = false; - float vsyncDelta; - QMutex mutex; - QWaitCondition waitCondition; - QQuickWindow *exposedWindow = nullptr; - bool stopEventProcessing = false; - QSGD3D12EventQueue eventQueue; - QElapsedTimer threadTimer; - qint64 syncTime; - qint64 renderTime; - qint64 sinceLastTime; - -public slots: - void onSceneGraphChanged() { - syncResultedInChanges = true; - } -}; - -bool QSGD3D12RenderThread::event(QEvent *e) -{ - switch (e->type()) { - - case WM_Obscure: - Q_ASSERT(!exposedWindow || exposedWindow == static_cast(e)->window); - if (Q_UNLIKELY(debug_loop())) - qDebug() << "RT - WM_Obscure" << exposedWindow; - mutex.lock(); - if (exposedWindow) { - QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop(); - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_Obscure - window removed"); - exposedWindow = nullptr; - } - waitCondition.wakeOne(); - mutex.unlock(); - return true; - - case WM_RequestSync: { - QSGD3D12SyncEvent *wme = static_cast(e); - if (sleeping) - stopEventProcessing = true; - // One thread+engine for each window. However, the native window may - // change in some (quite artificial) cases, e.g. due to a hide - - // destroy - show on the QWindow. - bool needsWindow = !engine->window(); - if (engine->window() && engine->window() != wme->window->winId()) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_RequestSync - native window handle changes for active engine"); - engine->waitGPU(); - QQuickWindowPrivate::get(wme->window)->cleanupNodesOnShutdown(); - QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); - rc->invalidate(); - engine->releaseResources(); - needsWindow = true; - // Be nice and emit the rendercontext's initialized() later on at - // some point so that QQuickWindow::sceneGraphInitialized() behaves - // in a manner similar to GL. - rc->setInitializedPending(); - } - if (needsWindow) { - // Must only ever get here when there is no window or releaseResources() has been called. - const int samples = wme->window->format().samples(); - if (Q_UNLIKELY(debug_loop())) - qDebug() << "RT - WM_RequestSync - initializing D3D12 engine" << wme->window - << wme->size << wme->dpr << samples; - engine->attachToWindow(wme->window->winId(), wme->size, wme->dpr, samples); - } - exposedWindow = wme->window; - engine->setWindowSize(wme->size, wme->dpr); - if (Q_UNLIKELY(debug_loop())) - qDebug() << "RT - WM_RequestSync" << exposedWindow; - pendingUpdate |= SyncRequest; - if (wme->syncInExpose) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_RequestSync - triggered from expose"); - pendingUpdate |= ExposeRequest; - } - if (wme->forceRenderPass) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_RequestSync - repaint regardless"); - pendingUpdate |= RepaintRequest; - } - return true; - } - - case WM_TryRelease: { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_TryRelease"); - mutex.lock(); - renderLoop->lockedForSync = true; - QSGD3D12TryReleaseEvent *wme = static_cast(e); - // Only when no windows are exposed anymore or we are shutting down. - if (!exposedWindow || wme->destroying) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_TryRelease - invalidating rc"); - if (wme->window) { - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); - if (wme->destroying) { - // QSGNode destruction may release graphics resources in use so wait first. - engine->waitGPU(); - // Bye bye nodes... - wd->cleanupNodesOnShutdown(); - QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); - } - rc->invalidate(); - QCoreApplication::processEvents(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - if (wme->destroying) - delete wd->animationController; - } - if (wme->destroying) - active = false; - if (sleeping) - stopEventProcessing = true; - } else { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_TryRelease - not releasing because window is still active"); - } - waitCondition.wakeOne(); - renderLoop->lockedForSync = false; - mutex.unlock(); - return true; - } - - case WM_Grab: { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_Grab"); - QSGD3D12GrabEvent *wme = static_cast(e); - Q_ASSERT(wme->window); - Q_ASSERT(wme->window == exposedWindow || !exposedWindow); - mutex.lock(); - if (wme->window) { - // Grabbing is generally done by rendering a frame and reading the - // color buffer contents back, without presenting, and then - // creating a QImage from the returned data. It is terribly - // inefficient since it involves a full blocking wait for the GPU. - // However, our hands are tied by the existing, synchronous APIs of - // QQuickWindow and such. - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); - rc->ensureInitializedEmitted(); - wd->syncSceneGraph(); - wd->renderSceneGraph(wme->window->size()); - *wme->image = engine->executeAndWaitReadbackRenderTarget(); - } - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_Grab - waking gui to handle result"); - waitCondition.wakeOne(); - mutex.unlock(); - return true; - } - - case WM_PostJob: { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_PostJob"); - QSGD3D12JobEvent *wme = static_cast(e); - Q_ASSERT(wme->window == exposedWindow); - if (exposedWindow) { - wme->job->run(); - delete wme->job; - wme->job = nullptr; - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_PostJob - job done"); - } - return true; - } - - case WM_RequestRepaint: - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_RequestPaint"); - // When GUI posts this event, it is followed by a polishAndSync, so we - // must not exit the event loop yet. - pendingUpdate |= RepaintRequest; - break; - - default: - break; - } - - return QThread::event(e); -} - -void QSGD3D12RenderThread::postEvent(QEvent *e) -{ - eventQueue.addEvent(e); -} - -void QSGD3D12RenderThread::processEvents() -{ - while (eventQueue.hasMoreEvents()) { - QEvent *e = eventQueue.takeEvent(false); - event(e); - delete e; - } -} - -void QSGD3D12RenderThread::processEventsAndWaitForMore() -{ - stopEventProcessing = false; - while (!stopEventProcessing) { - QEvent *e = eventQueue.takeEvent(true); - event(e); - delete e; - } -} - -void QSGD3D12RenderThread::run() -{ - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - run()"); - - engine = new QSGD3D12Engine; - rc->setEngine(engine); - - rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr); - rtAnim->install(); - - if (QQmlDebugConnector::service()) - QQuickProfiler::registerAnimationCallback(); - - while (active) { - if (exposedWindow) - syncAndRender(); - - processEvents(); - QCoreApplication::processEvents(); - - if (pendingUpdate == 0 || !exposedWindow) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - done drawing, sleep"); - sleeping = true; - processEventsAndWaitForMore(); - sleeping = false; - } - } - - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - run() exiting"); - - delete rtAnim; - rtAnim = nullptr; - - rc->moveToThread(renderLoop->thread()); - moveToThread(renderLoop->thread()); - - rc->setEngine(nullptr); - delete engine; - engine = nullptr; -} - -void QSGD3D12RenderThread::sync(bool inExpose) -{ - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - sync"); - - mutex.lock(); - Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked"); - - // Recover from device loss. - if (!engine->hasResources()) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - sync - device was lost, resetting scenegraph"); - QQuickWindowPrivate::get(exposedWindow)->cleanupNodesOnShutdown(); - QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); - rc->invalidate(); - } - - if (engine->window()) { - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); - bool hadRenderer = wd->renderer != nullptr; - // If the scene graph was touched since the last sync() make sure it sends the - // changed signal. - if (wd->renderer) - wd->renderer->clearChangedFlag(); - - rc->ensureInitializedEmitted(); - wd->syncSceneGraph(); - - if (!hadRenderer && wd->renderer) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - created renderer"); - syncResultedInChanges = true; - connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this, - &QSGD3D12RenderThread::onSceneGraphChanged, Qt::DirectConnection); - } - - // Process deferred deletes now, directly after the sync as deleteLater - // on the GUI must now also have resulted in SG changes and the delete - // is a safe operation. - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - } - - if (!inExpose) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - sync complete, waking gui"); - waitCondition.wakeOne(); - mutex.unlock(); - } -} - -void QSGD3D12RenderThread::syncAndRender() -{ - if (Q_UNLIKELY(debug_time())) { - sinceLastTime = threadTimer.nsecsElapsed(); - threadTimer.start(); - } - Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame); - - QElapsedTimer waitTimer; - waitTimer.start(); - - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - syncAndRender()"); - - syncResultedInChanges = false; - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); - - const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage; - const bool syncRequested = pendingUpdate & SyncRequest; - const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest; - pendingUpdate = 0; - - if (syncRequested) - sync(exposeRequested); - -#ifndef QSG_NO_RENDER_TIMING - if (Q_UNLIKELY(debug_time())) - syncTime = threadTimer.nsecsElapsed(); -#endif - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); - - if (!syncResultedInChanges && !repaintRequested) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - no changes, render aborted"); - int waitTime = vsyncDelta - (int) waitTimer.elapsed(); - if (waitTime > 0) - msleep(waitTime); - return; - } - - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - rendering started"); - - if (rtAnim->isRunning()) { - wd->animationController->lock(); - rtAnim->advance(); - wd->animationController->unlock(); - } - - bool canRender = wd->renderer != nullptr; - // Recover from device loss. - if (!engine->hasResources()) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - syncAndRender - device was lost, posting FullUpdateRequest"); - // Cannot do anything here because gui is not locked. Request a new - // sync+render round on the gui thread and let the sync handle it. - QCoreApplication::postEvent(exposedWindow, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))); - canRender = false; - } - - if (canRender) { - wd->renderSceneGraph(engine->windowSize()); - if (Q_UNLIKELY(debug_time())) - renderTime = threadTimer.nsecsElapsed(); - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); - - // The engine is able to have multiple frames in flight. This in effect is - // similar to BufferQueueingOpenGL. Provide an env var to force the - // traditional blocking swap behavior, just in case. - static bool blockOnEachFrame = qEnvironmentVariableIntValue("QT_D3D_BLOCKING_PRESENT") != 0; - - if (!wd->customRenderStage || !wd->customRenderStage->swap()) - engine->present(); - - if (blockOnEachFrame) - engine->waitGPU(); - - // The concept of "frame swaps" is quite misleading by default, when - // blockOnEachFrame is not used, but emit it for compatibility. - wd->fireFrameSwapped(); - } else { - Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1); - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - window not ready, skipping render"); - } - - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - rendering done"); - - if (exposeRequested) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - wake gui after initial expose"); - waitCondition.wakeOne(); - mutex.unlock(); - } - - if (Q_UNLIKELY(debug_time())) - qDebug("Frame rendered with 'd3d12' renderloop in %dms, sync=%d, render=%d, swap=%d - (on render thread)", - int(threadTimer.elapsed()), - int((syncTime/1000000)), - int((renderTime - syncTime) / 1000000), - int(threadTimer.elapsed() - renderTime / 1000000)); - - Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame); - - static int devLossTest = qEnvironmentVariableIntValue("QT_D3D_TEST_DEVICE_LOSS"); - if (devLossTest > 0) { - static QElapsedTimer kt; - static bool timerRunning = false; - if (!timerRunning) { - kt.start(); - timerRunning = true; - } else if (kt.elapsed() > 5000) { - --devLossTest; - kt.restart(); - engine->simulateDeviceLoss(); - } - } -} - -template T *windowFor(const QVector &list, QQuickWindow *window) -{ - for (const T &t : list) { - if (t.window == window) - return const_cast(&t); - } - return nullptr; -} - QSGD3D12RenderLoop::QSGD3D12RenderLoop() { if (Q_UNLIKELY(debug_loop())) - qDebug("new d3d12 render loop ctor"); + qDebug("new d3d12 render loop"); sg = new QSGD3D12Context; - - anim = sg->createAnimationDriver(this); - connect(anim, &QAnimationDriver::started, this, &QSGD3D12RenderLoop::onAnimationStarted); - connect(anim, &QAnimationDriver::stopped, this, &QSGD3D12RenderLoop::onAnimationStopped); - anim->install(); } QSGD3D12RenderLoop::~QSGD3D12RenderLoop() { - if (Q_UNLIKELY(debug_loop())) - qDebug("new d3d12 render loop dtor"); - delete sg; } @@ -712,11 +80,6 @@ void QSGD3D12RenderLoop::hide(QQuickWindow *window) { if (Q_UNLIKELY(debug_loop())) qDebug() << "hide" << window; - - if (window->isExposed()) - handleObscurity(windowFor(windows, window)); - - releaseResources(window); } void QSGD3D12RenderLoop::resize(QQuickWindow *window) @@ -725,7 +88,11 @@ void QSGD3D12RenderLoop::resize(QQuickWindow *window) return; if (Q_UNLIKELY(debug_loop())) - qDebug() << "resize" << window << window->size(); + qDebug() << "resize" << window; + + WindowData &data(m_windows[window]); + if (data.engine) + data.engine->setWindowSize(window->size(), window->effectiveDevicePixelRatio()); } void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) @@ -733,26 +100,32 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) if (Q_UNLIKELY(debug_loop())) qDebug() << "window destroyed" << window; - WindowData *w = windowFor(windows, window); - if (!w) + if (!m_windows.contains(window)) return; - handleObscurity(w); - handleResourceRelease(w, true); + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->fireAboutToStop(); - QSGD3D12RenderThread *thread = w->thread; - while (thread->isRunning()) - QThread::yieldCurrentThread(); + WindowData &data(m_windows[window]); + QSGD3D12Engine *engine = data.engine; + QSGD3D12RenderContext *rc = data.rc; + m_windows.remove(window); - Q_ASSERT(thread->thread() == QThread::currentThread()); - delete thread; + // QSGNode destruction may release graphics resources in use so wait first. + engine->waitGPU(); - for (int i = 0; i < windows.size(); ++i) { - if (windows.at(i).window == window) { - windows.removeAt(i); - break; - } - } + // Bye bye nodes... + wd->cleanupNodesOnShutdown(); + + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + + rc->invalidate(); + + if (m_windows.isEmpty()) + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + + delete rc; + delete engine; } void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window) @@ -761,76 +134,55 @@ void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window) qDebug() << "exposure changed" << window; if (window->isExposed()) { - handleExposure(window); - } else { - WindowData *w = windowFor(windows, window); - if (w) - handleObscurity(w); + if (!m_windows.contains(window)) { + WindowData data; + data.engine = new QSGD3D12Engine; + data.rc = static_cast(QQuickWindowPrivate::get(window)->context); + data.rc->setEngine(data.engine); + m_windows[window] = data; + + const int samples = window->format().samples(); + const qreal dpr = window->effectiveDevicePixelRatio(); + + if (debug_loop()) + qDebug() << "initializing D3D12 engine" << window << window->size() << dpr << samples; + + data.engine->attachToWindow(window->winId(), window->size(), dpr, samples); + } + m_windows[window].updatePending = true; + renderWindow(window); + } else if (m_windows.contains(window)) { + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->fireAboutToStop(); } } QImage QSGD3D12RenderLoop::grab(QQuickWindow *window) { - if (Q_UNLIKELY(debug_loop())) - qDebug() << "grab" << window; - - WindowData *w = windowFor(windows, window); - // Have to support invisible (but created()'ed) windows as well. - // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible. - const bool tempExpose = !w; - if (tempExpose) { - handleExposure(window); - w = windowFor(windows, window); - Q_ASSERT(w); - } - - if (!w->thread->isRunning()) + if (!m_windows.contains(window)) return QImage(); - if (!window->handle()) - window->create(); - - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); - wd->polishItems(); - - QImage result; - w->thread->mutex.lock(); - lockedForSync = true; - w->thread->postEvent(new QSGD3D12GrabEvent(window, &result)); - w->thread->waitCondition.wait(&w->thread->mutex); - lockedForSync = false; - w->thread->mutex.unlock(); + m_windows[window].grabOnly = true; - result.setDevicePixelRatio(window->effectiveDevicePixelRatio()); + renderWindow(window); - if (tempExpose) - handleObscurity(w); - - return result; + QImage grabbed = m_grabContent; + m_grabContent = QImage(); + return grabbed; } void QSGD3D12RenderLoop::update(QQuickWindow *window) { - WindowData *w = windowFor(windows, window); - if (!w) - return; - - if (w->thread == QThread::currentThread()) { - w->thread->requestRepaint(); + if (!m_windows.contains(window)) return; - } - // We set forceRenderPass because we want to make sure the QQuickWindow - // actually does a full render pass after the next sync. - w->forceRenderPass = true; - scheduleUpdate(w); + m_windows[window].updatePending = true; + window->requestUpdate(); } void QSGD3D12RenderLoop::maybeUpdate(QQuickWindow *window) { - WindowData *w = windowFor(windows, window); - if (w) - scheduleUpdate(w); + update(window); } // called in response to window->requestUpdate() @@ -839,14 +191,12 @@ void QSGD3D12RenderLoop::handleUpdateRequest(QQuickWindow *window) if (Q_UNLIKELY(debug_loop())) qDebug() << "handleUpdateRequest" << window; - WindowData *w = windowFor(windows, window); - if (w) - polishAndSync(w, false); + renderWindow(window); } QAnimationDriver *QSGD3D12RenderLoop::animationDriver() const { - return anim; + return nullptr; } QSGContext *QSGD3D12RenderLoop::sceneGraphContext() const @@ -863,19 +213,14 @@ void QSGD3D12RenderLoop::releaseResources(QQuickWindow *window) { if (Q_UNLIKELY(debug_loop())) qDebug() << "releaseResources" << window; - - WindowData *w = windowFor(windows, window); - if (w) - handleResourceRelease(w, false); } void QSGD3D12RenderLoop::postJob(QQuickWindow *window, QRunnable *job) { - WindowData *w = windowFor(windows, window); - if (w && w->thread && w->thread->exposedWindow) - w->thread->postEvent(new QSGD3D12JobEvent(window, job)); - else - delete job; + Q_ASSERT(job); + Q_ASSERT(window); + job->run(); + delete job; } QSurface::SurfaceType QSGD3D12RenderLoop::windowSurfaceType() const @@ -883,285 +228,153 @@ QSurface::SurfaceType QSGD3D12RenderLoop::windowSurfaceType() const return QSurface::OpenGLSurface; } -bool QSGD3D12RenderLoop::interleaveIncubation() const -{ - bool somethingVisible = false; - for (const WindowData &w : windows) { - if (w.window->isVisible() && w.window->isExposed()) { - somethingVisible = true; - break; - } - } - return somethingVisible && anim->isRunning(); -} - -int QSGD3D12RenderLoop::flags() const +void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) { - return SupportsGrabWithoutExpose; -} + if (Q_UNLIKELY(debug_loop())) + qDebug() << "renderWindow" << window; -bool QSGD3D12RenderLoop::event(QEvent *e) -{ - if (e->type() == QEvent::Timer) { - QTimerEvent *te = static_cast(e); - if (te->timerId() == animationTimer) { - anim->advance(); - emit timeToIncubate(); - return true; - } - } + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + if (!wd->isRenderable() || !m_windows.contains(window)) + return; - return QObject::event(e); -} + WindowData &data(m_windows[window]); -void QSGD3D12RenderLoop::onAnimationStarted() -{ - startOrStopAnimationTimer(); + const bool needsSwap = data.updatePending; + data.updatePending = false; - for (const WindowData &w : qAsConst(windows)) - w.window->requestUpdate(); -} + if (!data.grabOnly) { + wd->flushFrameSynchronousEvents(); + if (!m_windows.contains(window)) + return; + } -void QSGD3D12RenderLoop::onAnimationStopped() -{ - startOrStopAnimationTimer(); -} + QElapsedTimer renderTimer; + qint64 renderTime = 0, syncTime = 0, polishTime = 0; + const bool profileFrames = debug_time(); + if (profileFrames) + renderTimer.start(); + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame); -void QSGD3D12RenderLoop::startOrStopAnimationTimer() -{ - int exposedWindowCount = 0; - const WindowData *exposed = nullptr; - - for (int i = 0; i < windows.size(); ++i) { - const WindowData &w(windows[i]); - if (w.window->isVisible() && w.window->isExposed()) { - ++exposedWindowCount; - exposed = &w; - } - } + wd->polishItems(); - if (animationTimer && (exposedWindowCount == 1 || !anim->isRunning())) { - killTimer(animationTimer); - animationTimer = 0; - // If animations are running, make sure we keep on animating - if (anim->isRunning()) - exposed->window->requestUpdate(); - } else if (!animationTimer && exposedWindowCount != 1 && anim->isRunning()) { - animationTimer = startTimer(qsgrl_animation_interval()); - } -} + if (profileFrames) + polishTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame, + QQuickProfiler::SceneGraphRenderLoopFrame); -void QSGD3D12RenderLoop::handleExposure(QQuickWindow *window) -{ - if (Q_UNLIKELY(debug_loop())) - qDebug() << "handleExposure" << window; + emit window->afterAnimating(); - WindowData *w = windowFor(windows, window); - if (!w) { + // The native window may change in some (quite artificial) cases, e.g. due + // to a hide - destroy - show on the QWindow. + bool needsWindow = !data.engine->window(); + if (data.engine->window() && data.engine->window() != window->winId()) { if (Q_UNLIKELY(debug_loop())) - qDebug("adding window to list"); - WindowData win; - win.window = window; - QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership - win.thread = new QSGD3D12RenderThread(this, rc); - win.updateDuringSync = false; - win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt - windows.append(win); - w = &windows.last(); - } - - // set this early as we'll be rendering shortly anyway and this avoids - // special casing exposure in polishAndSync. - w->thread->exposedWindow = window; - - if (w->window->size().isEmpty() - || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) { -#ifndef QT_NO_DEBUG - qWarning().noquote().nospace() << "QSGD3D12RenderLoop: expose event received for window " - << w->window << " with invalid geometry: " << w->window->geometry() - << " on " << w->window->screen(); -#endif + qDebug("sync - native window handle changes for active engine"); + data.engine->waitGPU(); + wd->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + data.rc->invalidate(); + data.engine->releaseResources(); + needsWindow = true; + // Be nice and emit the rendercontext's initialized() later on so that + // QQuickWindow::sceneGraphInitialized() behaves in a manner similar to GL. + data.rc->setInitializedPending(); + } + if (needsWindow) { + // Must only ever get here when there is no window or releaseResources() has been called. + const int samples = window->format().samples(); + const qreal dpr = window->effectiveDevicePixelRatio(); + if (Q_UNLIKELY(debug_loop())) + qDebug() << "sync - reinitializing D3D12 engine" << window << window->size() << dpr << samples; + data.engine->attachToWindow(window->winId(), window->size(), dpr, samples); } - if (!w->window->handle()) - w->window->create(); - - // Start render thread if it is not running - if (!w->thread->isRunning()) { + // Recover from device loss. + if (!data.engine->hasResources()) { if (Q_UNLIKELY(debug_loop())) - qDebug("starting render thread"); - // Push a few things to the render thread. - QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController; - if (controller->thread() != w->thread) - controller->moveToThread(w->thread); - if (w->thread->thread() == QThread::currentThread()) { - w->thread->rc->moveToThread(w->thread); - w->thread->moveToThread(w->thread); - } - - w->thread->active = true; - w->thread->start(); - - if (!w->thread->isRunning()) - qFatal("Render thread failed to start, aborting application."); + qDebug("sync - device was lost, resetting scenegraph"); + wd->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + data.rc->invalidate(); + data.rc->setInitializedPending(); } - polishAndSync(w, true); - - startOrStopAnimationTimer(); -} - -void QSGD3D12RenderLoop::handleObscurity(WindowData *w) -{ - if (Q_UNLIKELY(debug_loop())) - qDebug() << "handleObscurity" << w->window; + data.rc->ensureInitializedEmitted(); - if (w->thread->isRunning()) { - w->thread->mutex.lock(); - w->thread->postEvent(new QSGD3D12WindowEvent(w->window, WM_Obscure)); - w->thread->waitCondition.wait(&w->thread->mutex); - w->thread->mutex.unlock(); - } - - startOrStopAnimationTimer(); -} + wd->syncSceneGraph(); -void QSGD3D12RenderLoop::scheduleUpdate(WindowData *w) -{ - if (!QCoreApplication::instance()) - return; + if (profileFrames) + syncTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); - if (!w || !w->thread->isRunning()) - return; + wd->renderSceneGraph(window->size()); - QThread *current = QThread::currentThread(); - if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) { - qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()"; - return; - } + if (profileFrames) + renderTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); - if (current == w->thread) { - w->updateDuringSync = true; - return; + if (data.grabOnly) { + m_grabContent = data.engine->executeAndWaitReadbackRenderTarget(); + data.grabOnly = false; } - w->window->requestUpdate(); -} + // The engine is able to have multiple frames in flight. This in effect is + // similar to BufferQueueingOpenGL. Provide an env var to force the + // traditional blocking swap behavior, just in case. + static bool blockOnEachFrame = qEnvironmentVariableIntValue("QT_D3D_BLOCKING_PRESENT") != 0; -void QSGD3D12RenderLoop::handleResourceRelease(WindowData *w, bool destroying) -{ - if (Q_UNLIKELY(debug_loop())) - qDebug() << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window; - - w->thread->mutex.lock(); - if (w->thread->isRunning() && w->thread->active) { - QQuickWindow *window = w->window; - - // Note that window->handle() is typically null by this time because - // the platform window is already destroyed. This should not be a - // problem for the D3D cleanup. - - w->thread->postEvent(new QSGD3D12TryReleaseEvent(window, destroying)); - w->thread->waitCondition.wait(&w->thread->mutex); - - // Avoid a shutdown race condition. - // If SG is invalidated and 'active' becomes false, the thread's run() - // method will exit. handleExposure() relies on QThread::isRunning() (because it - // potentially needs to start the thread again) and our mutex cannot be used to - // track the thread stopping, so we wait a few nanoseconds extra so the thread - // can exit properly. - if (!w->thread->active) - w->thread->wait(); + if (needsSwap && window->isVisible()) { + data.engine->present(); + if (blockOnEachFrame) + data.engine->waitGPU(); + // The concept of "frame swaps" is quite misleading by default, when + // blockOnEachFrame is not used, but emit it for compatibility. + wd->fireFrameSwapped(); + } else { + if (blockOnEachFrame) + data.engine->waitGPU(); } - w->thread->mutex.unlock(); -} -void QSGD3D12RenderLoop::polishAndSync(WindowData *w, bool inExpose) -{ - if (Q_UNLIKELY(debug_loop())) - qDebug() << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window; - - QQuickWindow *window = w->window; - if (!w->thread || !w->thread->exposedWindow) { - if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - not exposed, abort"); - return; - } + qint64 swapTime = 0; + if (profileFrames) + swapTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame); - // Flush pending touch events. - QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); - // The delivery of the event might have caused the window to stop rendering - w = windowFor(windows, window); - if (!w || !w->thread || !w->thread->exposedWindow) { - if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - removed after touch event flushing, abort"); - return; + if (Q_UNLIKELY(debug_time())) { + static QTime lastFrameTime = QTime::currentTime(); + qDebug("Frame rendered with 'd3d12' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d", + int(swapTime / 1000000), + int(polishTime / 1000000), + int((syncTime - polishTime) / 1000000), + int((renderTime - syncTime) / 1000000), + int((swapTime - renderTime) / 10000000), + int(lastFrameTime.msecsTo(QTime::currentTime()))); + lastFrameTime = QTime::currentTime(); } - QElapsedTimer timer; - qint64 polishTime = 0; - qint64 waitTime = 0; - qint64 syncTime = 0; - if (Q_UNLIKELY(debug_time())) - timer.start(); - Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync); - - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); - wd->polishItems(); - - if (Q_UNLIKELY(debug_time())) - polishTime = timer.nsecsElapsed(); - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); - - w->updateDuringSync = false; - - emit window->afterAnimating(); - - if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - lock for sync"); - w->thread->mutex.lock(); - lockedForSync = true; - w->thread->postEvent(new QSGD3D12SyncEvent(window, inExpose, w->forceRenderPass)); - w->forceRenderPass = false; - - if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - wait for sync"); - if (Q_UNLIKELY(debug_time())) - waitTime = timer.nsecsElapsed(); - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); - w->thread->waitCondition.wait(&w->thread->mutex); - lockedForSync = false; - w->thread->mutex.unlock(); - if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - unlock after sync"); - - if (Q_UNLIKELY(debug_time())) - syncTime = timer.nsecsElapsed(); - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + // Might have been set during syncSceneGraph() + if (data.updatePending) + maybeUpdate(window); - if (!animationTimer && anim->isRunning()) { - if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - advancing animations"); - anim->advance(); - // We need to trigger another sync to keep animations running... - w->window->requestUpdate(); - emit timeToIncubate(); - } else if (w->updateDuringSync) { - w->window->requestUpdate(); + // Simulate device loss if requested. + static int devLossTest = qEnvironmentVariableIntValue("QT_D3D_TEST_DEVICE_LOSS"); + if (devLossTest > 0) { + static QElapsedTimer kt; + static bool timerRunning = false; + if (!timerRunning) { + kt.start(); + timerRunning = true; + } else if (kt.elapsed() > 5000) { + --devLossTest; + kt.restart(); + data.engine->simulateDeviceLoss(); + } } - - if (Q_UNLIKELY(debug_time())) - qDebug().nospace() - << "Frame prepared with 'd3d12' renderloop" - << ", polish=" << (polishTime / 1000000) - << ", lock=" << (waitTime - polishTime) / 1000000 - << ", blockedForSync=" << (syncTime - waitTime) / 1000000 - << ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000 - << " - (on gui thread) " << window; - - Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync); } -#include "qsgd3d12renderloop.moc" +int QSGD3D12RenderLoop::flags() const +{ + return SupportsGrabWithoutExpose; +} QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h index 732f8dd5d2..fbd9d66d4a 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h @@ -58,12 +58,9 @@ QT_BEGIN_NAMESPACE class QSGD3D12Engine; class QSGD3D12Context; class QSGD3D12RenderContext; -class QSGD3D12RenderThread; class QSGD3D12RenderLoop : public QSGRenderLoop { - Q_OBJECT - public: QSGD3D12RenderLoop(); ~QSGD3D12RenderLoop(); @@ -91,37 +88,23 @@ public: void postJob(QQuickWindow *window, QRunnable *job) override; QSurface::SurfaceType windowSurfaceType() const override; - bool interleaveIncubation() const override; int flags() const override; - bool event(QEvent *e) override; +private: + void renderWindow(QQuickWindow *window); -public Q_SLOTS: - void onAnimationStarted(); - void onAnimationStopped(); + QSGD3D12Context *sg; -private: struct WindowData { - QQuickWindow *window; - QSGD3D12RenderThread *thread; - uint updateDuringSync : 1; - uint forceRenderPass : 1; + QSGD3D12RenderContext *rc = nullptr; + QSGD3D12Engine *engine = nullptr; + bool updatePending = false; + bool grabOnly = false; }; - void startOrStopAnimationTimer(); - void handleExposure(QQuickWindow *window); - void handleObscurity(WindowData *w); - void scheduleUpdate(WindowData *w); - void handleResourceRelease(WindowData *w, bool destroying); - void polishAndSync(WindowData *w, bool inExpose); - - QSGD3D12Context *sg; - QAnimationDriver *anim; - int animationTimer = 0; - bool lockedForSync = false; - QVector windows; + QHash m_windows; - friend class QSGD3D12RenderThread; + QImage m_grabContent; }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp new file mode 100644 index 0000000000..9f32a6d8bf --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp @@ -0,0 +1,1187 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgd3d12threadedrenderloop_p.h" +#include "qsgd3d12engine_p.h" +#include "qsgd3d12context_p.h" +#include "qsgd3d12rendercontext_p.h" +#include "qsgd3d12shadereffectnode_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// NOTE: Avoid categorized logging. It is slow. + +#define DECLARE_DEBUG_VAR(variable) \ + static bool debug_ ## variable() \ + { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; } + +DECLARE_DEBUG_VAR(loop) +DECLARE_DEBUG_VAR(time) + + +// NOTE: The threaded renderloop is not currently safe to use in practice as it +// is prone to deadlocks, in particular when multiple windows are active. This +// is because DXGI's limitation of relying on the gui message pump in certain +// cases. See +// https://msdn.microsoft.com/en-us/library/windows/desktop/ee417025(v=vs.85).aspx#multithreading_and_dxgi +// +// This means that if swap chain functions like create, release, and +// potentially even Present, are called outside the gui thread, then the +// application must ensure the gui thread does not ever block and wait for the +// render thread - since on the render thread a DXGI call may be in turn +// waiting for the gui thread to deliver a window message... +// +// Ensuring this is impossible with the current design where the gui thread +// must block at certain points, waiting for the render thread. Qt moves out +// rendering from the main thread, in order to make application's life easier, +// whereas the typical DXGI-compatible model would require moving work, but not +// windowing and presenting, out to additional threads. + + +/* + The D3D render loop mostly mirrors the threaded OpenGL render loop. + + There are two classes here. QSGD3D12ThreadedRenderLoop and + QSGD3D12RenderThread. All communication between the two is based on event + passing and we have a number of custom events. + + Render loop is per process, render thread is per window. The + QSGD3D12RenderContext and QSGD3D12Engine are per window as well. The former + is created (but not owned) by QQuickWindow. The D3D device is per process. + + In this implementation, the render thread is never blocked and the GUI + thread will initiate a polishAndSync which will block and wait for the + render thread to pick it up and release the block only after the render + thread is done syncing. The reason for this is: + + 1. Clear blocking paradigm. We only have one real "block" point + (polishAndSync()) and all blocking is initiated by GUI and picked up by + Render at specific times based on events. This makes the execution + deterministic. + + 2. Render does not have to interact with GUI. This is done so that the + render thread can run its own animation system which stays alive even when + the GUI thread is blocked doing I/O, object instantiation, QPainter-painting + or any other non-trivial task. + + The render thread has affinity to the GUI thread until a window is shown. + From that moment and until the window is destroyed, it will have affinity to + the render thread. (moved back at the end of run for cleanup). + */ + +// Passed from the RL to the RT when a window is removed obscured and should be +// removed from the render loop. +const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1); + +// Passed from the RL to RT when GUI has been locked, waiting for sync. +const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2); + +// Passed by the RT to itself to trigger another render pass. This is typically +// a result of QQuickWindow::update(). +const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3); + +// Passed by the RL to the RT to maybe release resource if no windows are +// rendering. +const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4); + +// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called. +const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5); + +// Passed by the window when there is a render job to run. +const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6); + +class QSGD3D12WindowEvent : public QEvent +{ +public: + QSGD3D12WindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { } + QQuickWindow *window; +}; + +class QSGD3D12TryReleaseEvent : public QSGD3D12WindowEvent +{ +public: + QSGD3D12TryReleaseEvent(QQuickWindow *win, bool destroy) + : QSGD3D12WindowEvent(win, WM_TryRelease), destroying(destroy) { } + bool destroying; +}; + +class QSGD3D12SyncEvent : public QSGD3D12WindowEvent +{ +public: + QSGD3D12SyncEvent(QQuickWindow *c, bool inExpose, bool force) + : QSGD3D12WindowEvent(c, WM_RequestSync) + , size(c->size()) + , dpr(c->effectiveDevicePixelRatio()) + , syncInExpose(inExpose) + , forceRenderPass(force) { } + QSize size; + float dpr; + bool syncInExpose; + bool forceRenderPass; +}; + +class QSGD3D12GrabEvent : public QSGD3D12WindowEvent +{ +public: + QSGD3D12GrabEvent(QQuickWindow *c, QImage *result) + : QSGD3D12WindowEvent(c, WM_Grab), image(result) { } + QImage *image; +}; + +class QSGD3D12JobEvent : public QSGD3D12WindowEvent +{ +public: + QSGD3D12JobEvent(QQuickWindow *c, QRunnable *postedJob) + : QSGD3D12WindowEvent(c, WM_PostJob), job(postedJob) { } + ~QSGD3D12JobEvent() { delete job; } + QRunnable *job; +}; + +class QSGD3D12EventQueue : public QQueue +{ +public: + void addEvent(QEvent *e) { + mutex.lock(); + enqueue(e); + if (waiting) + condition.wakeOne(); + mutex.unlock(); + } + + QEvent *takeEvent(bool wait) { + mutex.lock(); + if (isEmpty() && wait) { + waiting = true; + condition.wait(&mutex); + waiting = false; + } + QEvent *e = dequeue(); + mutex.unlock(); + return e; + } + + bool hasMoreEvents() { + mutex.lock(); + bool has = !isEmpty(); + mutex.unlock(); + return has; + } + +private: + QMutex mutex; + QWaitCondition condition; + bool waiting = false; +}; + +static inline int qsgrl_animation_interval() +{ + const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0; + return refreshRate < 1 ? 16 : int(1000 / refreshRate); +} + +class QSGD3D12RenderThread : public QThread +{ + Q_OBJECT + +public: + QSGD3D12RenderThread(QSGD3D12ThreadedRenderLoop *rl, QSGRenderContext *renderContext) + : renderLoop(rl) + { + rc = static_cast(renderContext); + vsyncDelta = qsgrl_animation_interval(); + } + + ~QSGD3D12RenderThread() + { + delete rc; + } + + bool event(QEvent *e); + void run(); + + void syncAndRender(); + void sync(bool inExpose); + + void requestRepaint() + { + if (sleeping) + stopEventProcessing = true; + if (exposedWindow) + pendingUpdate |= RepaintRequest; + } + + void processEventsAndWaitForMore(); + void processEvents(); + void postEvent(QEvent *e); + + enum UpdateRequest { + SyncRequest = 0x01, + RepaintRequest = 0x02, + ExposeRequest = 0x04 | RepaintRequest | SyncRequest + }; + + QSGD3D12Engine *engine = nullptr; + QSGD3D12ThreadedRenderLoop *renderLoop; + QSGD3D12RenderContext *rc; + QAnimationDriver *rtAnim = nullptr; + volatile bool active = false; + uint pendingUpdate = 0; + bool sleeping = false; + bool syncResultedInChanges = false; + float vsyncDelta; + QMutex mutex; + QWaitCondition waitCondition; + QQuickWindow *exposedWindow = nullptr; + bool stopEventProcessing = false; + QSGD3D12EventQueue eventQueue; + QElapsedTimer threadTimer; + qint64 syncTime; + qint64 renderTime; + qint64 sinceLastTime; + +public slots: + void onSceneGraphChanged() { + syncResultedInChanges = true; + } +}; + +bool QSGD3D12RenderThread::event(QEvent *e) +{ + switch (e->type()) { + + case WM_Obscure: + Q_ASSERT(!exposedWindow || exposedWindow == static_cast(e)->window); + if (Q_UNLIKELY(debug_loop())) + qDebug() << "RT - WM_Obscure" << exposedWindow; + mutex.lock(); + if (exposedWindow) { + QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop(); + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_Obscure - window removed"); + exposedWindow = nullptr; + } + waitCondition.wakeOne(); + mutex.unlock(); + return true; + + case WM_RequestSync: { + QSGD3D12SyncEvent *wme = static_cast(e); + if (sleeping) + stopEventProcessing = true; + // One thread+engine for each window. However, the native window may + // change in some (quite artificial) cases, e.g. due to a hide - + // destroy - show on the QWindow. + bool needsWindow = !engine->window(); + if (engine->window() && engine->window() != wme->window->winId()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_RequestSync - native window handle changes for active engine"); + engine->waitGPU(); + QQuickWindowPrivate::get(wme->window)->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + rc->invalidate(); + engine->releaseResources(); + needsWindow = true; + // Be nice and emit the rendercontext's initialized() later on at + // some point so that QQuickWindow::sceneGraphInitialized() behaves + // in a manner similar to GL. + rc->setInitializedPending(); + } + if (needsWindow) { + // Must only ever get here when there is no window or releaseResources() has been called. + const int samples = wme->window->format().samples(); + if (Q_UNLIKELY(debug_loop())) + qDebug() << "RT - WM_RequestSync - initializing D3D12 engine" << wme->window + << wme->size << wme->dpr << samples; + engine->attachToWindow(wme->window->winId(), wme->size, wme->dpr, samples); + } + exposedWindow = wme->window; + engine->setWindowSize(wme->size, wme->dpr); + if (Q_UNLIKELY(debug_loop())) + qDebug() << "RT - WM_RequestSync" << exposedWindow; + pendingUpdate |= SyncRequest; + if (wme->syncInExpose) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_RequestSync - triggered from expose"); + pendingUpdate |= ExposeRequest; + } + if (wme->forceRenderPass) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_RequestSync - repaint regardless"); + pendingUpdate |= RepaintRequest; + } + return true; + } + + case WM_TryRelease: { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_TryRelease"); + mutex.lock(); + renderLoop->lockedForSync = true; + QSGD3D12TryReleaseEvent *wme = static_cast(e); + // Only when no windows are exposed anymore or we are shutting down. + if (!exposedWindow || wme->destroying) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_TryRelease - invalidating rc"); + if (wme->window) { + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); + if (wme->destroying) { + // QSGNode destruction may release graphics resources in use so wait first. + engine->waitGPU(); + // Bye bye nodes... + wd->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + } + rc->invalidate(); + QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + if (wme->destroying) + delete wd->animationController; + } + if (wme->destroying) + active = false; + if (sleeping) + stopEventProcessing = true; + } else { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_TryRelease - not releasing because window is still active"); + } + waitCondition.wakeOne(); + renderLoop->lockedForSync = false; + mutex.unlock(); + return true; + } + + case WM_Grab: { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_Grab"); + QSGD3D12GrabEvent *wme = static_cast(e); + Q_ASSERT(wme->window); + Q_ASSERT(wme->window == exposedWindow || !exposedWindow); + mutex.lock(); + if (wme->window) { + // Grabbing is generally done by rendering a frame and reading the + // color buffer contents back, without presenting, and then + // creating a QImage from the returned data. It is terribly + // inefficient since it involves a full blocking wait for the GPU. + // However, our hands are tied by the existing, synchronous APIs of + // QQuickWindow and such. + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); + rc->ensureInitializedEmitted(); + wd->syncSceneGraph(); + wd->renderSceneGraph(wme->window->size()); + *wme->image = engine->executeAndWaitReadbackRenderTarget(); + } + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_Grab - waking gui to handle result"); + waitCondition.wakeOne(); + mutex.unlock(); + return true; + } + + case WM_PostJob: { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_PostJob"); + QSGD3D12JobEvent *wme = static_cast(e); + Q_ASSERT(wme->window == exposedWindow); + if (exposedWindow) { + wme->job->run(); + delete wme->job; + wme->job = nullptr; + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_PostJob - job done"); + } + return true; + } + + case WM_RequestRepaint: + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_RequestPaint"); + // When GUI posts this event, it is followed by a polishAndSync, so we + // must not exit the event loop yet. + pendingUpdate |= RepaintRequest; + break; + + default: + break; + } + + return QThread::event(e); +} + +void QSGD3D12RenderThread::postEvent(QEvent *e) +{ + eventQueue.addEvent(e); +} + +void QSGD3D12RenderThread::processEvents() +{ + while (eventQueue.hasMoreEvents()) { + QEvent *e = eventQueue.takeEvent(false); + event(e); + delete e; + } +} + +void QSGD3D12RenderThread::processEventsAndWaitForMore() +{ + stopEventProcessing = false; + while (!stopEventProcessing) { + QEvent *e = eventQueue.takeEvent(true); + event(e); + delete e; + } +} + +void QSGD3D12RenderThread::run() +{ + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - run()"); + + engine = new QSGD3D12Engine; + rc->setEngine(engine); + + rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr); + rtAnim->install(); + + if (QQmlDebugConnector::service()) + QQuickProfiler::registerAnimationCallback(); + + while (active) { + if (exposedWindow) + syncAndRender(); + + processEvents(); + QCoreApplication::processEvents(); + + if (pendingUpdate == 0 || !exposedWindow) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - done drawing, sleep"); + sleeping = true; + processEventsAndWaitForMore(); + sleeping = false; + } + } + + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - run() exiting"); + + delete rtAnim; + rtAnim = nullptr; + + rc->moveToThread(renderLoop->thread()); + moveToThread(renderLoop->thread()); + + rc->setEngine(nullptr); + delete engine; + engine = nullptr; +} + +void QSGD3D12RenderThread::sync(bool inExpose) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - sync"); + + mutex.lock(); + Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked"); + + // Recover from device loss. + if (!engine->hasResources()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - sync - device was lost, resetting scenegraph"); + QQuickWindowPrivate::get(exposedWindow)->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + rc->invalidate(); + } + + if (engine->window()) { + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); + bool hadRenderer = wd->renderer != nullptr; + // If the scene graph was touched since the last sync() make sure it sends the + // changed signal. + if (wd->renderer) + wd->renderer->clearChangedFlag(); + + rc->ensureInitializedEmitted(); + wd->syncSceneGraph(); + + if (!hadRenderer && wd->renderer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - created renderer"); + syncResultedInChanges = true; + connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this, + &QSGD3D12RenderThread::onSceneGraphChanged, Qt::DirectConnection); + } + + // Process deferred deletes now, directly after the sync as deleteLater + // on the GUI must now also have resulted in SG changes and the delete + // is a safe operation. + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } + + if (!inExpose) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - sync complete, waking gui"); + waitCondition.wakeOne(); + mutex.unlock(); + } +} + +void QSGD3D12RenderThread::syncAndRender() +{ + if (Q_UNLIKELY(debug_time())) { + sinceLastTime = threadTimer.nsecsElapsed(); + threadTimer.start(); + } + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame); + + QElapsedTimer waitTimer; + waitTimer.start(); + + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - syncAndRender()"); + + syncResultedInChanges = false; + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); + + const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage; + const bool syncRequested = pendingUpdate & SyncRequest; + const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest; + pendingUpdate = 0; + + if (syncRequested) + sync(exposeRequested); + +#ifndef QSG_NO_RENDER_TIMING + if (Q_UNLIKELY(debug_time())) + syncTime = threadTimer.nsecsElapsed(); +#endif + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + if (!syncResultedInChanges && !repaintRequested) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - no changes, render aborted"); + int waitTime = vsyncDelta - (int) waitTimer.elapsed(); + if (waitTime > 0) + msleep(waitTime); + return; + } + + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - rendering started"); + + if (rtAnim->isRunning()) { + wd->animationController->lock(); + rtAnim->advance(); + wd->animationController->unlock(); + } + + bool canRender = wd->renderer != nullptr; + // Recover from device loss. + if (!engine->hasResources()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - syncAndRender - device was lost, posting FullUpdateRequest"); + // Cannot do anything here because gui is not locked. Request a new + // sync+render round on the gui thread and let the sync handle it. + QCoreApplication::postEvent(exposedWindow, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))); + canRender = false; + } + + if (canRender) { + wd->renderSceneGraph(engine->windowSize()); + if (Q_UNLIKELY(debug_time())) + renderTime = threadTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + // The engine is able to have multiple frames in flight. This in effect is + // similar to BufferQueueingOpenGL. Provide an env var to force the + // traditional blocking swap behavior, just in case. + static bool blockOnEachFrame = qEnvironmentVariableIntValue("QT_D3D_BLOCKING_PRESENT") != 0; + + if (!wd->customRenderStage || !wd->customRenderStage->swap()) + engine->present(); + + if (blockOnEachFrame) + engine->waitGPU(); + + // The concept of "frame swaps" is quite misleading by default, when + // blockOnEachFrame is not used, but emit it for compatibility. + wd->fireFrameSwapped(); + } else { + Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1); + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - window not ready, skipping render"); + } + + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - rendering done"); + + if (exposeRequested) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - wake gui after initial expose"); + waitCondition.wakeOne(); + mutex.unlock(); + } + + if (Q_UNLIKELY(debug_time())) + qDebug("Frame rendered with 'd3d12' renderloop in %dms, sync=%d, render=%d, swap=%d - (on render thread)", + int(threadTimer.elapsed()), + int((syncTime/1000000)), + int((renderTime - syncTime) / 1000000), + int(threadTimer.elapsed() - renderTime / 1000000)); + + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame); + + static int devLossTest = qEnvironmentVariableIntValue("QT_D3D_TEST_DEVICE_LOSS"); + if (devLossTest > 0) { + static QElapsedTimer kt; + static bool timerRunning = false; + if (!timerRunning) { + kt.start(); + timerRunning = true; + } else if (kt.elapsed() > 5000) { + --devLossTest; + kt.restart(); + engine->simulateDeviceLoss(); + } + } +} + +template T *windowFor(const QVector &list, QQuickWindow *window) +{ + for (const T &t : list) { + if (t.window == window) + return const_cast(&t); + } + return nullptr; +} + +QSGD3D12ThreadedRenderLoop::QSGD3D12ThreadedRenderLoop() +{ + if (Q_UNLIKELY(debug_loop())) + qDebug("d3d12 THREADED render loop ctor"); + + sg = new QSGD3D12Context; + + anim = sg->createAnimationDriver(this); + connect(anim, &QAnimationDriver::started, this, &QSGD3D12ThreadedRenderLoop::onAnimationStarted); + connect(anim, &QAnimationDriver::stopped, this, &QSGD3D12ThreadedRenderLoop::onAnimationStopped); + anim->install(); +} + +QSGD3D12ThreadedRenderLoop::~QSGD3D12ThreadedRenderLoop() +{ + if (Q_UNLIKELY(debug_loop())) + qDebug("d3d12 THREADED render loop dtor"); + + delete sg; +} + +void QSGD3D12ThreadedRenderLoop::show(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "show" << window; +} + +void QSGD3D12ThreadedRenderLoop::hide(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "hide" << window; + + if (window->isExposed()) + handleObscurity(windowFor(windows, window)); + + releaseResources(window); +} + +void QSGD3D12ThreadedRenderLoop::resize(QQuickWindow *window) +{ + if (!window->isExposed() || window->size().isEmpty()) + return; + + if (Q_UNLIKELY(debug_loop())) + qDebug() << "resize" << window << window->size(); +} + +void QSGD3D12ThreadedRenderLoop::windowDestroyed(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "window destroyed" << window; + + WindowData *w = windowFor(windows, window); + if (!w) + return; + + handleObscurity(w); + handleResourceRelease(w, true); + + QSGD3D12RenderThread *thread = w->thread; + while (thread->isRunning()) + QThread::yieldCurrentThread(); + + Q_ASSERT(thread->thread() == QThread::currentThread()); + delete thread; + + for (int i = 0; i < windows.size(); ++i) { + if (windows.at(i).window == window) { + windows.removeAt(i); + break; + } + } +} + +void QSGD3D12ThreadedRenderLoop::exposureChanged(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "exposure changed" << window; + + if (window->isExposed()) { + handleExposure(window); + } else { + WindowData *w = windowFor(windows, window); + if (w) + handleObscurity(w); + } +} + +QImage QSGD3D12ThreadedRenderLoop::grab(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "grab" << window; + + WindowData *w = windowFor(windows, window); + // Have to support invisible (but created()'ed) windows as well. + // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible. + const bool tempExpose = !w; + if (tempExpose) { + handleExposure(window); + w = windowFor(windows, window); + Q_ASSERT(w); + } + + if (!w->thread->isRunning()) + return QImage(); + + if (!window->handle()) + window->create(); + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->polishItems(); + + QImage result; + w->thread->mutex.lock(); + lockedForSync = true; + w->thread->postEvent(new QSGD3D12GrabEvent(window, &result)); + w->thread->waitCondition.wait(&w->thread->mutex); + lockedForSync = false; + w->thread->mutex.unlock(); + + result.setDevicePixelRatio(window->effectiveDevicePixelRatio()); + + if (tempExpose) + handleObscurity(w); + + return result; +} + +void QSGD3D12ThreadedRenderLoop::update(QQuickWindow *window) +{ + WindowData *w = windowFor(windows, window); + if (!w) + return; + + if (w->thread == QThread::currentThread()) { + w->thread->requestRepaint(); + return; + } + + // We set forceRenderPass because we want to make sure the QQuickWindow + // actually does a full render pass after the next sync. + w->forceRenderPass = true; + scheduleUpdate(w); +} + +void QSGD3D12ThreadedRenderLoop::maybeUpdate(QQuickWindow *window) +{ + WindowData *w = windowFor(windows, window); + if (w) + scheduleUpdate(w); +} + +// called in response to window->requestUpdate() +void QSGD3D12ThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "handleUpdateRequest" << window; + + WindowData *w = windowFor(windows, window); + if (w) + polishAndSync(w, false); +} + +QAnimationDriver *QSGD3D12ThreadedRenderLoop::animationDriver() const +{ + return anim; +} + +QSGContext *QSGD3D12ThreadedRenderLoop::sceneGraphContext() const +{ + return sg; +} + +QSGRenderContext *QSGD3D12ThreadedRenderLoop::createRenderContext(QSGContext *) const +{ + return sg->createRenderContext(); +} + +void QSGD3D12ThreadedRenderLoop::releaseResources(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "releaseResources" << window; + + WindowData *w = windowFor(windows, window); + if (w) + handleResourceRelease(w, false); +} + +void QSGD3D12ThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job) +{ + WindowData *w = windowFor(windows, window); + if (w && w->thread && w->thread->exposedWindow) + w->thread->postEvent(new QSGD3D12JobEvent(window, job)); + else + delete job; +} + +QSurface::SurfaceType QSGD3D12ThreadedRenderLoop::windowSurfaceType() const +{ + return QSurface::OpenGLSurface; +} + +bool QSGD3D12ThreadedRenderLoop::interleaveIncubation() const +{ + bool somethingVisible = false; + for (const WindowData &w : windows) { + if (w.window->isVisible() && w.window->isExposed()) { + somethingVisible = true; + break; + } + } + return somethingVisible && anim->isRunning(); +} + +int QSGD3D12ThreadedRenderLoop::flags() const +{ + return SupportsGrabWithoutExpose; +} + +bool QSGD3D12ThreadedRenderLoop::event(QEvent *e) +{ + if (e->type() == QEvent::Timer) { + QTimerEvent *te = static_cast(e); + if (te->timerId() == animationTimer) { + anim->advance(); + emit timeToIncubate(); + return true; + } + } + + return QObject::event(e); +} + +void QSGD3D12ThreadedRenderLoop::onAnimationStarted() +{ + startOrStopAnimationTimer(); + + for (const WindowData &w : qAsConst(windows)) + w.window->requestUpdate(); +} + +void QSGD3D12ThreadedRenderLoop::onAnimationStopped() +{ + startOrStopAnimationTimer(); +} + +void QSGD3D12ThreadedRenderLoop::startOrStopAnimationTimer() +{ + int exposedWindowCount = 0; + const WindowData *exposed = nullptr; + + for (int i = 0; i < windows.size(); ++i) { + const WindowData &w(windows[i]); + if (w.window->isVisible() && w.window->isExposed()) { + ++exposedWindowCount; + exposed = &w; + } + } + + if (animationTimer && (exposedWindowCount == 1 || !anim->isRunning())) { + killTimer(animationTimer); + animationTimer = 0; + // If animations are running, make sure we keep on animating + if (anim->isRunning()) + exposed->window->requestUpdate(); + } else if (!animationTimer && exposedWindowCount != 1 && anim->isRunning()) { + animationTimer = startTimer(qsgrl_animation_interval()); + } +} + +void QSGD3D12ThreadedRenderLoop::handleExposure(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "handleExposure" << window; + + WindowData *w = windowFor(windows, window); + if (!w) { + if (Q_UNLIKELY(debug_loop())) + qDebug("adding window to list"); + WindowData win; + win.window = window; + QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership + win.thread = new QSGD3D12RenderThread(this, rc); + win.updateDuringSync = false; + win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt + windows.append(win); + w = &windows.last(); + } + + // set this early as we'll be rendering shortly anyway and this avoids + // special casing exposure in polishAndSync. + w->thread->exposedWindow = window; + + if (w->window->size().isEmpty() + || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) { +#ifndef QT_NO_DEBUG + qWarning().noquote().nospace() << "QSGD3D12ThreadedRenderLoop: expose event received for window " + << w->window << " with invalid geometry: " << w->window->geometry() + << " on " << w->window->screen(); +#endif + } + + if (!w->window->handle()) + w->window->create(); + + // Start render thread if it is not running + if (!w->thread->isRunning()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("starting render thread"); + // Push a few things to the render thread. + QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController; + if (controller->thread() != w->thread) + controller->moveToThread(w->thread); + if (w->thread->thread() == QThread::currentThread()) { + w->thread->rc->moveToThread(w->thread); + w->thread->moveToThread(w->thread); + } + + w->thread->active = true; + w->thread->start(); + + if (!w->thread->isRunning()) + qFatal("Render thread failed to start, aborting application."); + } + + polishAndSync(w, true); + + startOrStopAnimationTimer(); +} + +void QSGD3D12ThreadedRenderLoop::handleObscurity(WindowData *w) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "handleObscurity" << w->window; + + if (w->thread->isRunning()) { + w->thread->mutex.lock(); + w->thread->postEvent(new QSGD3D12WindowEvent(w->window, WM_Obscure)); + w->thread->waitCondition.wait(&w->thread->mutex); + w->thread->mutex.unlock(); + } + + startOrStopAnimationTimer(); +} + +void QSGD3D12ThreadedRenderLoop::scheduleUpdate(WindowData *w) +{ + if (!QCoreApplication::instance()) + return; + + if (!w || !w->thread->isRunning()) + return; + + QThread *current = QThread::currentThread(); + if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) { + qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()"; + return; + } + + if (current == w->thread) { + w->updateDuringSync = true; + return; + } + + w->window->requestUpdate(); +} + +void QSGD3D12ThreadedRenderLoop::handleResourceRelease(WindowData *w, bool destroying) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window; + + w->thread->mutex.lock(); + if (w->thread->isRunning() && w->thread->active) { + QQuickWindow *window = w->window; + + // Note that window->handle() is typically null by this time because + // the platform window is already destroyed. This should not be a + // problem for the D3D cleanup. + + w->thread->postEvent(new QSGD3D12TryReleaseEvent(window, destroying)); + w->thread->waitCondition.wait(&w->thread->mutex); + + // Avoid a shutdown race condition. + // If SG is invalidated and 'active' becomes false, the thread's run() + // method will exit. handleExposure() relies on QThread::isRunning() (because it + // potentially needs to start the thread again) and our mutex cannot be used to + // track the thread stopping, so we wait a few nanoseconds extra so the thread + // can exit properly. + if (!w->thread->active) + w->thread->wait(); + } + w->thread->mutex.unlock(); +} + +void QSGD3D12ThreadedRenderLoop::polishAndSync(WindowData *w, bool inExpose) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window; + + QQuickWindow *window = w->window; + if (!w->thread || !w->thread->exposedWindow) { + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - not exposed, abort"); + return; + } + + // Flush pending touch events. + QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); + // The delivery of the event might have caused the window to stop rendering + w = windowFor(windows, window); + if (!w || !w->thread || !w->thread->exposedWindow) { + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - removed after touch event flushing, abort"); + return; + } + + QElapsedTimer timer; + qint64 polishTime = 0; + qint64 waitTime = 0; + qint64 syncTime = 0; + if (Q_UNLIKELY(debug_time())) + timer.start(); + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync); + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->polishItems(); + + if (Q_UNLIKELY(debug_time())) + polishTime = timer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + + w->updateDuringSync = false; + + emit window->afterAnimating(); + + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - lock for sync"); + w->thread->mutex.lock(); + lockedForSync = true; + w->thread->postEvent(new QSGD3D12SyncEvent(window, inExpose, w->forceRenderPass)); + w->forceRenderPass = false; + + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - wait for sync"); + if (Q_UNLIKELY(debug_time())) + waitTime = timer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + w->thread->waitCondition.wait(&w->thread->mutex); + lockedForSync = false; + w->thread->mutex.unlock(); + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - unlock after sync"); + + if (Q_UNLIKELY(debug_time())) + syncTime = timer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + + if (!animationTimer && anim->isRunning()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - advancing animations"); + anim->advance(); + // We need to trigger another sync to keep animations running... + w->window->requestUpdate(); + emit timeToIncubate(); + } else if (w->updateDuringSync) { + w->window->requestUpdate(); + } + + if (Q_UNLIKELY(debug_time())) + qDebug().nospace() + << "Frame prepared with 'd3d12' renderloop" + << ", polish=" << (polishTime / 1000000) + << ", lock=" << (waitTime - polishTime) / 1000000 + << ", blockedForSync=" << (syncTime - waitTime) / 1000000 + << ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000 + << " - (on gui thread) " << window; + + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync); +} + +#include "qsgd3d12threadedrenderloop.moc" + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h new file mode 100644 index 0000000000..46f62948f1 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGD3D12THREADEDRENDERLOOP_P_H +#define QSGD3D12THREADEDRENDERLOOP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QSGD3D12Engine; +class QSGD3D12Context; +class QSGD3D12RenderContext; +class QSGD3D12RenderThread; + +class QSGD3D12ThreadedRenderLoop : public QSGRenderLoop +{ + Q_OBJECT + +public: + QSGD3D12ThreadedRenderLoop(); + ~QSGD3D12ThreadedRenderLoop(); + + void show(QQuickWindow *window) override; + void hide(QQuickWindow *window) override; + void resize(QQuickWindow *window) override; + + void windowDestroyed(QQuickWindow *window) override; + + void exposureChanged(QQuickWindow *window) override; + + QImage grab(QQuickWindow *window) override; + + void update(QQuickWindow *window) override; + void maybeUpdate(QQuickWindow *window) override; + void handleUpdateRequest(QQuickWindow *window) override; + + QAnimationDriver *animationDriver() const override; + + QSGContext *sceneGraphContext() const override; + QSGRenderContext *createRenderContext(QSGContext *) const override; + + void releaseResources(QQuickWindow *window) override; + void postJob(QQuickWindow *window, QRunnable *job) override; + + QSurface::SurfaceType windowSurfaceType() const override; + bool interleaveIncubation() const override; + int flags() const override; + + bool event(QEvent *e) override; + +public Q_SLOTS: + void onAnimationStarted(); + void onAnimationStopped(); + +private: + struct WindowData { + QQuickWindow *window; + QSGD3D12RenderThread *thread; + uint updateDuringSync : 1; + uint forceRenderPass : 1; + }; + + void startOrStopAnimationTimer(); + void handleExposure(QQuickWindow *window); + void handleObscurity(WindowData *w); + void scheduleUpdate(WindowData *w); + void handleResourceRelease(WindowData *w, bool destroying); + void polishAndSync(WindowData *w, bool inExpose); + + QSGD3D12Context *sg; + QAnimationDriver *anim; + int animationTimer = 0; + bool lockedForSync = false; + QVector windows; + + friend class QSGD3D12RenderThread; +}; + +QT_END_NAMESPACE + +#endif // QSGD3D12THREADEDRENDERLOOP_P_H -- cgit v1.2.3 From ee32318034f12f6a6d02c155e8c4d3095f569d92 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 24 Jun 2016 12:29:51 +0200 Subject: Add some d3d12 docs ...and move the software and the new sections on their dedicated page. Change-Id: If15caad62d7efd9a5315549b2efeff6e46d502ed Reviewed-by: Andy Nichols --- .../doc/src/concepts/visualcanvas/adaptations.qdoc | 221 ++++++++++++++++++++- 1 file changed, 211 insertions(+), 10 deletions(-) diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index a1b4650507..1bf6822bbd 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -75,19 +75,38 @@ adaptations. The default adaptation capable of providing the full Qt Quick 2 feature set is the OpenGL adaptation. All of the details of the OpenGL -adpatation can are available here +adpatation can are available here: \l{qtquick-visualcanvas-scenegraph-renderer.html}{OpenGL Adaptation} \section1 Software Adaptation -The Software adaptation is an alternative renderer for \l {Qt Quick} 2 that uses the Raster -paint engine to render the contents of the scene graph instead of OpenGL. -As a result of not using OpenGL to render the scene graph, some features -and optimizations are no longer available. Most Qt Quick 2 applications -will run without modification though any attempts to use unsupported -features will be ignored. By using the Software adpatation it is possible to run Qt -Quick 2 applications on hardware and platforms that do not have OpenGL -support. +The Software adaptation is an alternative renderer for \l {Qt Quick} 2 that +uses the raster paint engine to render the contents of the scene graph. The +details for this adaptation are available here: +\l{qtquick-visualcanvas-adaptations-software.html}{Software Adaptation} + +\section1 Direct3D 12 (experimental) + +The Direct3D 12 adaptation is an alternative renderer for \l {Qt Quick} 2 when +running on Windows 10, both for Win32 and UWP applications. The details for +this adaptation are available here: +\l{qtquick-visualcanvas-adaptations-d3d12.html}{Direct3D 12 Adaptation} + +*/ + + +/*! +\title Qt Quick Software Adaptation +\page qtquick-visualcanvas-adaptations-software.html + +The Software adaptation is an alternative renderer for \l {Qt Quick} 2 that +uses the Raster paint engine to render the contents of the scene graph instead +of OpenGL. As a result of not using OpenGL to render the scene graph, some +features and optimizations are no longer available. Most Qt Quick 2 +applications will run without modification though any attempts to use +unsupported features will be ignored. By using the Software adpatation it is +possible to run Qt Quick 2 applications on hardware and platforms that do not +have OpenGL support. The Software adaptation was previously known as the Qt Quick 2D Renderer. @@ -113,6 +132,188 @@ rasterization and does not respond as well to transformations such as scaling as when using OpenGL. The quality is similar to choosing \l [QML] {Text::renderType} {Text.NativeRendering} with \l [QML] {Text} items. -\section1 Direct3D 12 (experimental) +*/ + + +/*! +\title Qt Quick Direct3D 12 Adaptation +\page qtquick-visualcanvas-adaptations-d3d12.html + +The Direct3D 12 adaptation for Windows 10 (both Win32 (\c windows platform +plugin) and UWP (\c winrt platform plugin)) is shipped as a dynamically loaded +plugin. It will not be functional on earlier Windows versions. The building of +the plugin is enabled automatically whenever the necessary D3D and DXGI +develpoment files are present. In practice this currently means Visual Studio +2015 and newer. + +The adaptation is available both in normal, OpenGL-enabled Qt builds and also +when Qt was configured with \c{-no-opengl}. However, it is never the default, +meaning the user or the application has to explicitly request it by setting the +\c{QT_QUICK_BACKEND} environment variable or by calling +QQuickWindow::setSceneGraphBackend(). + +\section2 Motivation + +This experimental adaptation is the first Qt Quick backend focusing on a +modern, lower-level graphics API in combination with a windowing system +interface different from the traditional approaches used in combination with +OpenGL. + +It also allows better integration with Windows, Direct3D being the primary +vendor-supported solution. This means that there are fewer problems anticipated +with drivers, operations like window resizes, and special events like graphics +device loss caused by device resets or graphics driver updates. + +Peformance-wise the general expectation is a somewhat lower CPU usage compared +to OpenGL due to lower driver overhead, and a higher GPU utilization with less +wasted idle time. The backend does not heavily utilize threads yet, which means +there are opportunities for further improvements in the future, for example to +further optimize image loading. + +The D3D12 backend also introduces support for pre-compiled shaders. All the +backend's own shaders (used by the built-in materials on which the Rectangle, +Image, Text, etc. QML types are built) are compiled to D3D shader bytecode when +compiling Qt. Applications using ShaderEffect items can chose to ship bytecode +either in regular files or via the Qt resource system, or use HLSL source +strings. Unlike OpenGL, the compilation for the latter is properly threaded, +meaning shader compilation will not block the application and its user +interface. + +\section2 Graphics Adapters + +The plugin does not necessarily require hardware acceleration. Using WARP, the +Direct3D software rasterizer, is also an option. By default the first adapter +providing hardware acceleration is chosen. To override this, in order to use +another graphics adapter or to force the usage of the software rasterizer, set +the environment variable \c{QT_D3D_ADAPTER_INDEX} to the index of the adapter. +The discovered adapters are printed at startup when \c{QSG_INFO} or the logging +category \c{qt.scenegraph.general} is enabled. + +\section2 Troubleshooting + +When encountering issues, always set the \c{QSG_INFO} and \c{QT_D3D_DEBUG} +environment variables to 1 in order to get debug and warning messages printed +on the debug output. The latter enables the Direct3D debug layer. Note that the +debug layer should not be enabled in production use since the performance it +can significantly impact performance (CPU load) due to increased API overhead. + +\section2 Render Loops + +By default the D3D12 adaptation uses a single-threaded render loop similar to +OpenGL's \c basic render loop. There is also a threaded variant available, that +can be requested by setting the \c{QSG_RENDER_LOOP} environment variable to \c +threaded. However, due to conceptual limitations in DXGI, the windowing system +interface, the threaded loop is prone to deadlocks when multiple QQuickWindow +or QQuickView instances are shown. Therefore the default is the single-threaded +loop for the time being. This means that with the D3D12 backend applications +are expected to move their work from the main (GUI) thread out to worker +threads, instead of expecting Qt to keep the GUI thread responsive and suitable +for heavy, blocking operations. + +See the \l{qtquick-visualcanvas-scenegraph.html}{Scene Graph page} for more +information on render loops and +\l{https://msdn.microsoft.com/en-us/library/windows/desktop/ee417025(v=vs.85).aspx#multithreading_and_dxgi}{the +MSDN page for DXGI} regarding the issues with multithreading. + +\section2 Renderer + +The scenegraph renderer in the D3D12 adaptation does not currently perform any +batching. This is less of an issue, unlike OpenGL, because state changes are +not presenting any problems in the first place. The simpler renderer logic can +also lead to lower CPU overhead in some cases. The trade-offs between the +various approaches are currently under research. + +\section2 Shader Effects + +The ShaderEffect QML type is fully functional with the D3D12 adaptation as well. +However, the interpretation of the fragmentShader and vertexShader properties is +different than with OpenGL. + +With D3D12, these strings can either be an URL for a local file or a file in +the resource system, or a HLSL source string. The former indicates that the +file in question contains pre-compiled D3D shader bytecode generated by the +\c fxc tool. + +See the ShaderEffect documentation for a detailed description. + +\section2 Unsupported Features + +Particles, sprites, and other OpenGL-dependent tools like +QQuickFramebufferObject are not currently supported. + +Like with the Software adaptation, text is always rendered using the native +method. Distance field-based text rendering is not currently implemented. + +Texture atlases are not currently in use. + +The renderer may lack support for certain minor features, for example drawing +points and lines with a width other than 1. + +Custom Qt Quick items using custom scenegraph nodes can be problematic. +Materials are inherently tied to the graphics API. Therefore only items using +the utility rectangle and image nodes are functional across all adaptations. + +Finally, rendering via QSGEngine and QSGAbstractRenderer is not feasible with +the D3D12 adaptation at the moment. + +\section2 Related APIs + +To integrate custom Direct3D 12 rendering, use QSGRenderNode in combination +with QSGRendererInterface. This approach does not rely on OpenGL contexts or +API specifics like framebuffers, and allows exposing the graphics device and +command buffer from the adaptation. It is not necessarily suitable for easy +integration of all types of content, in particular true 3D, so it will likely +get complemented by an alternative to QQuickFramebufferObject in future +releases. + +To perform runtime decisions based on the adaptation in use, use +QSGRendererInterface from C++ and GraphicsInfo from QML. They can also be used +to check the level of shader support (shading language, compilation approach). + +When creating custom items, use the new QSGRectangleNode and QSGImageNode +classes. These replace the now deprecated QSGSimpleRectNode and +QSGSimpleTextureNode. Unlike their predecessors, the new classes are +interfaces, and implementations are created via the factory functions +QQuickWindow::createRectangleNode() and QQuickWindow::createImageNode(). + +\section2 Advanced Configuration + +The D3D12 adaptation can keep multiple frames in flight, similarly to modern +game engines. This is somewhat different from the traditional render - swap - +wait for vsync model and allows better GPU utilization at the expense of higher +resource usage. + +For a discussion of flip model swap chains and the typical configuration +parameters, refer to +\l{https://software.intel.com/en-us/articles/sample-application-for-direct3d-12-flip-model-swap-chains}{this +article}. + +Vertical synchronization is always enabled, meaning Present() is invoked with +an interval of 1. + +The configuration can be changed by setting the following environment variables: + +\list + +\li \c{QT_D3D_BUFFER_COUNT} - The number of swap chain buffers. Defaults to 3. + +\li \c{QT_D3D_FRAME_COUNT} - The number of frames prepared without blocking. +Defaults to 2. Note that Present will start blocking after queuing 3 frames +(regardless of \c{QT_D3D_BUFFER_COUNT}), unless the waitable object is in use. +Note that every additional frame increases GPU resource usage since geometry +and constant buffer data will have to be duplicated, and involves more +bookkeeping on the CPU side. + +\li \c{QT_D3D_WAITABLE_SWAP_CHAIN_MAX_LATENCY} - When set, the frame latency is +set to the specified value (0-16). This changes the limit for Present() and +will trigger a wait for an available swap chain buffer when beginning each +frame. By default this is disabled. Refer to the article above for a detailed +discussion. + +\endlist + +Note that typical Qt Quick applications are expected to generate fairly low +workloads compared to true 3D applications like games. Therefore this level of +tuning will likely be unnecessary in most cases. */ -- cgit v1.2.3 From faa9ef7ce14ad0342ab464461f33365fcd1c7a1e Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 24 Jun 2016 13:36:01 +0200 Subject: Add missing deprecated tag to QSGSimple*Node. Change-Id: I014baf333e0c859f76c49ba859ce6db90978ff64 Reviewed-by: Andy Nichols --- src/quick/scenegraph/util/qsgsimplerectnode.cpp | 2 ++ src/quick/scenegraph/util/qsgsimpletexturenode.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.cpp b/src/quick/scenegraph/util/qsgsimplerectnode.cpp index e20c051f9a..f991f03136 100644 --- a/src/quick/scenegraph/util/qsgsimplerectnode.cpp +++ b/src/quick/scenegraph/util/qsgsimplerectnode.cpp @@ -53,6 +53,8 @@ QT_BEGIN_NAMESPACE or software backends of the Qt Quick scenegraph. For a proper cross-platform alternative prefer using QSGSimpleRectangleNode via QQuickWindow::createSimpleRectangleNode() or QSGEngine::createSimpleRectangleNode(). + + \deprecated */ diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp index 4ed0445ce1..c16e86c4e1 100644 --- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp @@ -102,6 +102,8 @@ static void qsgsimpletexturenode_update(QSGGeometry *g, or software backends of the Qt Quick scenegraph. For a proper cross-platform alternative prefer using QSGSimpleImageNode via QQuickWindow::createSimpleImageNode() or QSGEngine::createSimpleImageNode(). + + \deprecated */ /*! -- cgit v1.2.3 From 1d872f7ed1c9e0e8b4f34f4449ad7393f5afbec2 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 24 Jun 2016 13:41:51 +0200 Subject: Fix up QSGRendererInterface docs Some enums were missing. Change-Id: I68ff3043eed247b659ccafeb7ee709dd94b653fb Reviewed-by: Andy Nichols --- .../scenegraph/coreapi/qsgrendererinterface.cpp | 30 +++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp index ffde9d8930..149baab3a9 100644 --- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp @@ -68,8 +68,6 @@ QT_BEGIN_NAMESPACE \value Software The Qt Quick 2D Renderer is in use \value OpenGL OpenGL ES 2.0 or higher \value Direct3D12 Direct3D 12 - \value Vulkan Vulkan - \value Metal Metal */ /*! @@ -79,6 +77,32 @@ QT_BEGIN_NAMESPACE \value CommandList The command list or buffer used by the scenegraph */ +/*! + \enum QSGRendererInterface::ShaderType + \value UnknownShadingLanguage - Not yet known due to no window and scenegraph associated + \value GLSL - GLSL or GLSL ES + \value HLSL - HLSL + */ + +/*! + \enum QSGRendererInterface::ShaderCompilationType + \value RuntimeCompilation - Runtime compilation of shader source code is supported + \value OfflineCompilation - Pre-compiled bytecode supported + */ + +/*! + \enum QSGRendererInterface::ShaderSourceType + + \value ShaderSourceString - Shader source can be provided as a string in + the corresponding properties of ShaderEffect + + \value ShaderSourceFile - Local or resource files containing shader source + code are supported + + \value ShaderByteCode - Local or resource files containing shader bytecode are + supported + */ + QSGRendererInterface::~QSGRendererInterface() { } @@ -156,7 +180,7 @@ void *QSGRendererInterface::getResource(const char *resource) const /*! \fn QSGRendererInterface::ShaderSourceTypes QSGRendererInterface::shaderSourceType() const - \return a bitmask of the supported ways of providing shader sources. + \return a bitmask of the supported ways of providing shader sources in ShaderEffect items. \note This function can be called on any thread. However, the renderer interface's lifetime may be tied to the render thread and therefore calling -- cgit v1.2.3 From 188b160dd0f9959ea01647e07478254e928e3411 Mon Sep 17 00:00:00 2001 From: Jian Liang Date: Thu, 23 Jun 2016 23:17:59 +0800 Subject: Fix QQmlTypeData object leak Don't forget to drop refcount of QQmlTypeData object in QQmlType::resolveCompositeBaseType() to prevent leakage. Change-Id: I079839a3347def1c2ac8f9cc07a52debb885b36e Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlmetatype.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index c5d11ee3c3..337c3bce82 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -484,8 +484,8 @@ QQmlType *QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const Q_ASSERT(isComposite()); if (!engine) return 0; - QQmlTypeData *td = engine->typeLoader.getType(sourceUrl()); - if (!td || !td->isComplete()) + QQmlRefPointer td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer::Adopt); + if (td.isNull() || !td->isComplete()) return 0; QQmlCompiledData *cd = td->compiledData(); const QMetaObject *mo = cd->rootPropertyCache->firstCppMetaObject(); -- cgit v1.2.3 From d453275a883b6af4e8c7c45dcc632c3728221047 Mon Sep 17 00:00:00 2001 From: Milian Wolff Date: Mon, 20 Jun 2016 19:31:29 +0200 Subject: Add heaptrack support to qv4mm pool allocator. When you build qtdeclarative with CONFIG+=heaptrack then the allocations from the custom pool allocator can be traced with heaptrack. This works similar to the existing valgrind support. Change-Id: Ia988380415083aa1346c84c8c64b49ea3e17b7e2 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/jsruntime.pri | 4 ++++ src/qml/memory/qv4mm.cpp | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 6ef92511e2..7b58e60b9d 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -112,3 +112,7 @@ SOURCES += \ valgrind { DEFINES += V4_USE_VALGRIND } + +heaptrack { + DEFINES += V4_USE_HEAPTRACK +} diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 8adc783ab2..a8d5624550 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -60,6 +60,10 @@ #include #endif +#ifdef V4_USE_HEAPTRACK +#include +#endif + #if OS(QNX) #include // __tls() #endif @@ -231,6 +235,9 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec #ifdef V4_USE_VALGRIND VALGRIND_DISABLE_ERROR_REPORTING; VALGRIND_MEMPOOL_FREE(engine->memoryManager, m); +#endif +#ifdef V4_USE_HEAPTRACK + heaptrack_report_free(m); #endif Q_V4_PROFILE_DEALLOC(engine, m, header->itemSize, Profiling::SmallItem); ++(*itemsInUse); @@ -361,12 +368,18 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize #ifdef V4_USE_VALGRIND VALGRIND_MAKE_MEM_NOACCESS(allocation.base(), allocSize); VALGRIND_MEMPOOL_ALLOC(this, header, sizeof(Data::ChunkHeader)); +#endif +#ifdef V4_USE_HEAPTRACK + heaptrack_report_alloc(header, sizeof(Data::ChunkHeader)); #endif } found: #ifdef V4_USE_VALGRIND VALGRIND_MEMPOOL_ALLOC(this, m, size); +#endif +#ifdef V4_USE_HEAPTRACK + heaptrack_report_alloc(m, size); #endif Q_V4_PROFILE_ALLOC(engine, size, Profiling::SmallItem); @@ -497,6 +510,9 @@ void MemoryManager::sweep(bool lastSweep) Q_V4_PROFILE_DEALLOC(engine, 0, chunkIter->size(), Profiling::HeapPage); #ifdef V4_USE_VALGRIND VALGRIND_MEMPOOL_FREE(this, header); +#endif +#ifdef V4_USE_HEAPTRACK + heaptrack_report_free(header); #endif --m_d->nChunks[pos]; m_d->availableItems[pos] -= uint(decrease); -- cgit v1.2.3 From 3dfd47362fea05bc354f814b5b743856f5ce7462 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 8 Apr 2016 15:20:23 +0200 Subject: QML: Specialize bindings based on target property type. When we can determine the type of a target property during type compilation, we can skip a whole bunch of code that deals with converting the result of a binding to the correct (target) type. This removes 65 instructions on x86 for such typed bindings. Change-Id: Id2c7c57b9ae6dfbeb921121beae9630604ca1d17 Reviewed-by: Simon Hausmann --- .../qmldbg_debugger/qqmlenginedebugservice.cpp | 2 +- src/qml/jsruntime/qv4qobjectwrapper.cpp | 2 +- src/qml/jsruntime/qv4scopedvalue_p.h | 4 + src/qml/qml/qqmlbinding.cpp | 291 +++++++++++++++------ src/qml/qml/qqmlbinding_p.h | 32 ++- src/qml/qml/qqmlobjectcreator.cpp | 3 +- src/qml/qml/qqmlvaluetypewrapper.cpp | 2 +- .../designer/qquickdesignercustomobjectdata.cpp | 3 +- src/quick/items/qquickstateoperations.cpp | 26 +- src/quick/qtquick2.cpp | 7 +- src/quick/util/qquickpropertychanges.cpp | 24 +- tests/auto/qml/qqmllanguage/testtypes.cpp | 3 +- tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp | 26 +- 13 files changed, 294 insertions(+), 131 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index 209aa6eda6..e49b15218e 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -656,7 +656,7 @@ bool QQmlEngineDebugServiceImpl::setBinding(int objectId, filename, line, column); QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression); } else if (property.isProperty()) { - QQmlBinding *binding = new QQmlBinding(expression.toString(), object, QQmlContextData::get(context), filename, line, column); + QQmlBinding *binding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, expression.toString(), object, QQmlContextData::get(context), filename, line, column); binding->setTarget(property); QQmlPropertyPrivate::setBinding(binding); binding->update(); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index deed9b86a2..cf99b425a2 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -465,7 +465,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP QV4::Scoped bindingFunction(scope, (const Value &)f); bindingFunction->initBindingLocation(); - newBinding = new QQmlBinding(value, object, callingQmlContext); + newBinding = QQmlBinding::create(property, value, object, callingQmlContext); newBinding->setTarget(object, *property); } } diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index aa811ddba4..44d171e087 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -294,6 +294,10 @@ struct Scoped return ptr->cast(); } + const T *operator->() const { + return ptr->cast(); + } + bool operator!() const { return !ptr->m(); } diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 3940176107..9f86e448ef 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -56,27 +56,28 @@ QT_BEGIN_NAMESPACE -QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt) - : QQmlJavaScriptExpression(), - QQmlAbstractBinding() +QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContext *ctxt) { - setNotifyOnValueChanged(true); - QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt)); - setScopeObject(obj); + QQmlBinding *b = newBinding(property); + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt)); + b->setScopeObject(obj); - createQmlBinding(context(), obj, str, QString(), 0); + b->createQmlBinding(b->context(), obj, str, QString(), 0); + + return b; } -QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt) - : QQmlJavaScriptExpression(), - QQmlAbstractBinding() +QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt) { + QQmlBinding *b = newBinding(property); + if (ctxt && !ctxt->isValid()) - return; + return b; const QQmlScriptStringPrivate *scriptPrivate = script.d.data(); if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid())) - return; + return b; QString url; QV4::Function *runtimeFunction = 0; @@ -89,53 +90,61 @@ QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlConte runtimeFunction = ctxtdata->typeCompilationUnit->runtimeFunctions.at(scriptPrivate->bindingId); } - setNotifyOnValueChanged(true); - QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); - setScopeObject(obj ? obj : scriptPrivate->scope); + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); + b->setScopeObject(obj ? obj : scriptPrivate->scope); - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(b->context()->engine)->v4engine(); if (runtimeFunction) { - m_function.set(v4, QV4::FunctionObject::createQmlFunction(ctxtdata, scopeObject(), runtimeFunction)); + b->m_function.set(v4, QV4::FunctionObject::createQmlFunction(ctxtdata, b->scopeObject(), runtimeFunction)); } else { QString code = scriptPrivate->script; - createQmlBinding(context(), scopeObject(), code, url, scriptPrivate->lineNumber); + b->createQmlBinding(b->context(), b->scopeObject(), code, url, scriptPrivate->lineNumber); } + + return b; } -QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt) - : QQmlJavaScriptExpression(), - QQmlAbstractBinding() +QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContextData *ctxt) { - setNotifyOnValueChanged(true); - QQmlJavaScriptExpression::setContext(ctxt); - setScopeObject(obj); + QQmlBinding *b = newBinding(property); + + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(ctxt); + b->setScopeObject(obj); - createQmlBinding(ctxt, obj, str, QString(), 0); + b->createQmlBinding(ctxt, obj, str, QString(), 0); + + return b; } -QQmlBinding::QQmlBinding(const QString &str, QObject *obj, - QQmlContextData *ctxt, - const QString &url, quint16 lineNumber, quint16 columnNumber) - : QQmlJavaScriptExpression(), - QQmlAbstractBinding() +QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, + QQmlContextData *ctxt, const QString &url, quint16 lineNumber, + quint16 columnNumber) { + QQmlBinding *b = newBinding(property); + Q_UNUSED(columnNumber); - setNotifyOnValueChanged(true); - QQmlJavaScriptExpression::setContext(ctxt); - setScopeObject(obj); + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(ctxt); + b->setScopeObject(obj); + + b->createQmlBinding(ctxt, obj, str, url, lineNumber); - createQmlBinding(ctxt, obj, str, url, lineNumber); + return b; } -QQmlBinding::QQmlBinding(const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt) - : QQmlJavaScriptExpression(), - QQmlAbstractBinding() +QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt) { - setNotifyOnValueChanged(true); - QQmlJavaScriptExpression::setContext(ctxt); - setScopeObject(obj); + QQmlBinding *b = newBinding(property); - m_function.set(functionPtr.as()->engine(), functionPtr); + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(ctxt); + b->setScopeObject(obj); + + b->m_function.set(functionPtr.as()->engine(), functionPtr); + + return b; } QQmlBinding::~QQmlBinding() @@ -156,45 +165,86 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) if (QQmlData::wasDeleted(targetObject())) return; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); - QV4::Scope scope(ep->v4engine()); - QV4::ScopedFunctionObject f(scope, m_function.value()); - Q_ASSERT(f); - - if (updatingFlag()) { + // Check for a binding update loop + if (Q_UNLIKELY(updatingFlag())) { QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), getPropertyData(), 0); QQmlAbstractBinding::printBindingLoopError(p); return; } - - QQmlBindingProfiler prof(ep->profiler, this, f); setUpdatingFlag(true); - QQmlJavaScriptExpression::DeleteWatcher watcher(this); + DeleteWatcher watcher(this); - QQmlPropertyData pd = getPropertyData(); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); + QV4::Scope scope(ep->v4engine()); + QV4::ScopedFunctionObject f(scope, m_function.value()); + Q_ASSERT(f); - if (pd.propType == qMetaTypeId()) { + QQmlBindingProfiler prof(ep->profiler, this, f); + doUpdate(this, watcher, flags, scope, f); + + if (!watcher.wasDeleted()) + setUpdatingFlag(false); +} + +// QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or +// double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant +// expression for the switch for the compiler to generate the optimal code, but +// qMetaTypeId() needs to be used for the ID. So QQmlBinding::newBinding uses that +// to instantiate this class. +class QQmlBindingBinding: public QQmlBinding +{ +protected: + void doUpdate(QQmlBinding *binding, const DeleteWatcher &, + QQmlPropertyPrivate::WriteFlags flags, QV4::Scope &, + const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL + { + QQmlPropertyData pd = getPropertyData(); int idx = pd.coreIndex; Q_ASSERT(idx != -1); - QQmlBinding *t = this; int status = -1; - void *a[] = { &t, 0, &status, &flags }; + void *a[] = { &binding, 0, &status, &flags }; QMetaObject::metacall(*m_target, QMetaObject::WriteProperty, idx, a); + } +}; - } else { +#define QUICK_STORE(cpptype, conversion) \ + { \ + cpptype o = (conversion); \ + int status = -1; \ + void *argv[] = { &o, 0, &status, &flags }; \ + QMetaObject::metacall(m_target.data(), QMetaObject::WriteProperty, coreIndex, argv); \ + return true; \ + } \ + + +template +class GenericBinding: public QQmlBinding +{ +protected: + + void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, + QQmlPropertyPrivate::WriteFlags flags, QV4::Scope &scope, + const QV4::ScopedFunctionObject &f) Q_DECL_OVERRIDE Q_DECL_FINAL + { + auto ep = QQmlEnginePrivate::get(scope.engine); ep->referenceScarceResources(); bool isUndefined = false; QV4::ScopedCallData callData(scope); - QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); + binding->QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); bool error = false; - if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) - error = !write(pd, scope.result, isUndefined, flags); + if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) { + if (StaticPropType == QMetaType::UnknownType) { + error = !write(scope.result, isUndefined, flags); + } else { + error = !fastWrite(scope.result, isUndefined, flags); + } + } if (!watcher.wasDeleted()) { @@ -214,30 +264,62 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) ep->dereferenceScarceResources(); } - if (!watcher.wasDeleted()) - setUpdatingFlag(false); -} +private: + // Returns true if successful, false if an error description was set on expression + Q_ALWAYS_INLINE bool fastWrite(const QV4::Value &result, bool isUndefined, + QQmlPropertyPrivate::WriteFlags flags) + { + int coreIndex = getPropertyCoreIndex(); + + Q_ASSERT(m_target.data()); + + if (Q_LIKELY(!isUndefined && coreIndex != -1 )) { + switch (StaticPropType) { + case QMetaType::Int: + if (result.isInteger()) + QUICK_STORE(int, result.integerValue()) + else if (result.isNumber()) + QUICK_STORE(int, result.doubleValue()) + break; + case QMetaType::Double: + if (result.isNumber()) + QUICK_STORE(double, result.asDouble()) + break; + case QMetaType::Float: + if (result.isNumber()) + QUICK_STORE(float, result.asDouble()) + break; + case QMetaType::QString: + if (result.isString()) + QUICK_STORE(QString, result.toQStringNoThrow()) + break; + default: + if (const QV4::QQmlValueTypeWrapper *vtw = result.as()) { + if (vtw->d()->valueType->typeId == StaticPropType) { + return vtw->write(m_target.data(), coreIndex); + } + } + break; + } + } + + return slowWrite(result, isUndefined, flags); + } +}; // Returns true if successful, false if an error description was set on expression -bool QQmlBinding::write(const QQmlPropertyData &core, - const QV4::Value &result, bool isUndefined, - QQmlPropertyPrivate::WriteFlags flags) +Q_ALWAYS_INLINE bool QQmlBinding::write(const QV4::Value &result, bool isUndefined, + QQmlPropertyPrivate::WriteFlags flags) { Q_ASSERT(m_target.data()); - Q_ASSERT(core.coreIndex != -1); -#define QUICK_STORE(cpptype, conversion) \ - { \ - cpptype o = (conversion); \ - int status = -1; \ - void *argv[] = { &o, 0, &status, &flags }; \ - QMetaObject::metacall(m_target.data(), QMetaObject::WriteProperty, core.coreIndex, argv); \ - return true; \ - } \ + int coreIndex = getPropertyCoreIndex(); + int propertyType = getPropertyType(); + Q_ASSERT(m_target.data()); - if (Q_LIKELY(!isUndefined && !core.isValueTypeVirtual())) { - switch (core.propType) { + if (Q_LIKELY(!isUndefined && coreIndex != -1 )) { + switch (propertyType) { case QMetaType::Int: if (result.isInteger()) QUICK_STORE(int, result.integerValue()) @@ -258,15 +340,23 @@ bool QQmlBinding::write(const QQmlPropertyData &core, break; default: if (const QV4::QQmlValueTypeWrapper *vtw = result.as()) { - if (vtw->d()->valueType->typeId == core.propType) { - return vtw->write(m_target.data(), core.coreIndex); + if (vtw->d()->valueType->typeId == propertyType) { + return vtw->write(m_target.data(), coreIndex); } } break; } } + + return slowWrite(result, isUndefined, flags); +} #undef QUICK_STORE +Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QV4::Value &result, bool isUndefined, + QQmlPropertyPrivate::WriteFlags flags) +{ + QQmlPropertyData core = getPropertyData(); + QQmlEngine *engine = context()->engine; QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); @@ -510,4 +600,51 @@ QQmlPropertyData QQmlBinding::getPropertyData() const return d; } +Q_ALWAYS_INLINE int QQmlBinding::getPropertyCoreIndex() const +{ + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(m_targetIndex, &coreIndex); + if (valueTypeIndex != -1) { + return -1; + } else { + return coreIndex; + } +} + +int QQmlBinding::getPropertyType() const +{ + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(m_targetIndex, &coreIndex); + + QQmlData *data = QQmlData::get(*m_target, false); + Q_ASSERT(data && data->propertyCache); + + QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); + Q_ASSERT(propertyData); + + if (valueTypeIndex == -1) + return propertyData->propType; + else + return QMetaType::UnknownType; +} + +QQmlBinding *QQmlBinding::newBinding(const QQmlPropertyData *property) +{ + const int type = (property && property->isFullyResolved()) ? property->propType : QMetaType::UnknownType; + + if (type == qMetaTypeId()) { + return new QQmlBindingBinding; + } else if (type == QMetaType::Int) { + return new GenericBinding; + } else if (type == QMetaType::Double) { + return new GenericBinding; + } else if (type == QMetaType::Float) { + return new GenericBinding; + } else if (type == QMetaType::QString) { + return new GenericBinding; + } else { + return new GenericBinding; + } +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 45c360f07e..ad1b24f340 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -72,12 +72,12 @@ class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression, { friend class QQmlAbstractBinding; public: - QQmlBinding(const QString &, QObject *, QQmlContext *); - QQmlBinding(const QQmlScriptString &, QObject *, QQmlContext *); - QQmlBinding(const QString &, QObject *, QQmlContextData *); - QQmlBinding(const QString &, QObject *, QQmlContextData *, - const QString &url, quint16 lineNumber, quint16 columnNumber); - QQmlBinding(const QV4::Value &, QObject *, QQmlContextData *); + static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContext *); + static QQmlBinding *create(const QQmlPropertyData *, const QQmlScriptString &, QObject *, QQmlContext *); + static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *); + static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *, + const QString &url, quint16 lineNumber, quint16 columnNumber); + static QQmlBinding *create(const QQmlPropertyData *, const QV4::Value &, QObject *, QQmlContextData *); ~QQmlBinding(); void setTarget(const QQmlProperty &); @@ -101,17 +101,27 @@ public: QString expressionIdentifier() Q_DECL_OVERRIDE; void expressionChanged() Q_DECL_OVERRIDE; +protected: + virtual void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, + QQmlPropertyPrivate::WriteFlags flags, QV4::Scope &scope, + const QV4::ScopedFunctionObject &f) = 0; + + QQmlPropertyData getPropertyData() const; + int getPropertyCoreIndex() const; + int getPropertyType() const; + + bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyPrivate::WriteFlags flags); + + bool slowWrite(const QV4::Value &result, bool isUndefined, + QQmlPropertyPrivate::WriteFlags flags); + private: inline bool updatingFlag() const; inline void setUpdatingFlag(bool); inline bool enabledFlag() const; inline void setEnabledFlag(bool); - QQmlPropertyData getPropertyData() const; - - bool write(const QQmlPropertyData &core, - const QV4::Value &result, bool isUndefined, - QQmlPropertyPrivate::WriteFlags flags); + static QQmlBinding *newBinding(const QQmlPropertyData *property); }; bool QQmlBinding::updatingFlag() const diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 6a54555f3f..2e2d87509a 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -813,8 +813,6 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con bs->takeExpression(expr); } else { - QQmlBinding *qmlBinding = new QQmlBinding(function, _scopeObject, context); - // When writing bindings to grouped properties implemented as value types, // such as point.x: { someExpression; }, then the binding is installed on // the point property (_qobjectForBindings) and after evaluating the expression, @@ -824,6 +822,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con if (_valueTypeProperty) targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); + QQmlBinding *qmlBinding = QQmlBinding::create(&targetCorePropertyData, function, _scopeObject, context); sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); qmlBinding->setTarget(_bindingTarget, targetCorePropertyData); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 5cb346a462..b69eea61c5 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -459,7 +459,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QV4::Scoped bindingFunction(scope, (const Value &)f); bindingFunction->initBindingLocation(); - QQmlBinding *newBinding = new QQmlBinding(value, reference->d()->object, context); + QQmlBinding *newBinding = QQmlBinding::create(&cacheData, value, reference->d()->object, context); newBinding->setTarget(reference->d()->object, cacheData); QQmlPropertyPrivate::setBinding(newBinding); return; diff --git a/src/quick/designer/qquickdesignercustomobjectdata.cpp b/src/quick/designer/qquickdesignercustomobjectdata.cpp index 93e7b6133f..42dcb08d45 100644 --- a/src/quick/designer/qquickdesignercustomobjectdata.cpp +++ b/src/quick/designer/qquickdesignercustomobjectdata.cpp @@ -257,7 +257,8 @@ void QQuickDesignerCustomObjectData::setPropertyBinding(QQmlContext *context, return; if (property.isProperty()) { - QQmlBinding *binding = new QQmlBinding(expression, object(), context); + QQmlBinding *binding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, + expression, object(), context); binding->setTarget(property); binding->setNotifyOnValueChanged(true); diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp index 73b1680305..7e485c675c 100644 --- a/src/quick/items/qquickstateoperations.cpp +++ b/src/quick/items/qquickstateoperations.cpp @@ -359,8 +359,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() QQuickStateAction xa(d->target, QLatin1String("x"), x); actions << xa; } else { - QQmlBinding *newBinding = new QQmlBinding(d->xString.value, d->target, qmlContext(this)); QQmlProperty property(d->target, QLatin1String("x")); + QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->xString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction xa; xa.property = property; @@ -378,8 +378,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() QQuickStateAction ya(d->target, QLatin1String("y"), y); actions << ya; } else { - QQmlBinding *newBinding = new QQmlBinding(d->yString.value, d->target, qmlContext(this)); QQmlProperty property(d->target, QLatin1String("y")); + QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->yString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction ya; ya.property = property; @@ -397,8 +397,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() QQuickStateAction sa(d->target, QLatin1String("scale"), scale); actions << sa; } else { - QQmlBinding *newBinding = new QQmlBinding(d->scaleString.value, d->target, qmlContext(this)); QQmlProperty property(d->target, QLatin1String("scale")); + QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->scaleString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction sa; sa.property = property; @@ -416,8 +416,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() QQuickStateAction ra(d->target, QLatin1String("rotation"), rotation); actions << ra; } else { - QQmlBinding *newBinding = new QQmlBinding(d->rotationString.value, d->target, qmlContext(this)); QQmlProperty property(d->target, QLatin1String("rotation")); + QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->rotationString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction ra; ra.property = property; @@ -435,8 +435,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() QQuickStateAction wa(d->target, QLatin1String("width"), width); actions << wa; } else { - QQmlBinding *newBinding = new QQmlBinding(d->widthString.value, d->target, qmlContext(this)); QQmlProperty property(d->target, QLatin1String("width")); + QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->widthString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction wa; wa.property = property; @@ -454,8 +454,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() QQuickStateAction ha(d->target, QLatin1String("height"), height); actions << ha; } else { - QQmlBinding *newBinding = new QQmlBinding(d->heightString.value, d->target, qmlContext(this)); QQmlProperty property(d->target, QLatin1String("height")); + QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->heightString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction ha; ha.property = property; @@ -866,31 +866,31 @@ QQuickAnchorChanges::ActionList QQuickAnchorChanges::actions() d->baselineProp = QQmlProperty(d->target, QLatin1String("anchors.baseline")); if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::LeftAnchor) { - d->leftBinding = new QQmlBinding(d->anchorSet->d_func()->leftScript, d->target, qmlContext(this)); + d->leftBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->leftProp)->core, d->anchorSet->d_func()->leftScript, d->target, qmlContext(this)); d->leftBinding->setTarget(d->leftProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::RightAnchor) { - d->rightBinding = new QQmlBinding(d->anchorSet->d_func()->rightScript, d->target, qmlContext(this)); + d->rightBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->rightProp)->core, d->anchorSet->d_func()->rightScript, d->target, qmlContext(this)); d->rightBinding->setTarget(d->rightProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::HCenterAnchor) { - d->hCenterBinding = new QQmlBinding(d->anchorSet->d_func()->hCenterScript, d->target, qmlContext(this)); + d->hCenterBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->hCenterProp)->core, d->anchorSet->d_func()->hCenterScript, d->target, qmlContext(this)); d->hCenterBinding->setTarget(d->hCenterProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::TopAnchor) { - d->topBinding = new QQmlBinding(d->anchorSet->d_func()->topScript, d->target, qmlContext(this)); + d->topBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->topProp)->core, d->anchorSet->d_func()->topScript, d->target, qmlContext(this)); d->topBinding->setTarget(d->topProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::BottomAnchor) { - d->bottomBinding = new QQmlBinding(d->anchorSet->d_func()->bottomScript, d->target, qmlContext(this)); + d->bottomBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->bottomProp)->core, d->anchorSet->d_func()->bottomScript, d->target, qmlContext(this)); d->bottomBinding->setTarget(d->bottomProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::VCenterAnchor) { - d->vCenterBinding = new QQmlBinding(d->anchorSet->d_func()->vCenterScript, d->target, qmlContext(this)); + d->vCenterBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->vCenterProp)->core, d->anchorSet->d_func()->vCenterScript, d->target, qmlContext(this)); d->vCenterBinding->setTarget(d->vCenterProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::BaselineAnchor) { - d->baselineBinding = new QQmlBinding(d->anchorSet->d_func()->baselineScript, d->target, qmlContext(this)); + d->baselineBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->baselineProp)->core, d->anchorSet->d_func()->baselineScript, d->target, qmlContext(this)); d->baselineBinding->setTarget(d->baselineProp); } diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp index 41e30a1110..bf5b0083e9 100644 --- a/src/quick/qtquick2.cpp +++ b/src/quick/qtquick2.cpp @@ -136,9 +136,10 @@ void QQmlQtQuick2DebugStatesDelegate::updateBinding(QQmlContext *context, QQmlBinding *newBinding = 0; if (!isLiteralValue) { - newBinding = new QQmlBinding(expression.toString(), object, - QQmlContextData::get(context), fileName, - line, column); + newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, + expression.toString(), object, + QQmlContextData::get(context), fileName, + line, column); newBinding->setTarget(property); } diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index df65e0822b..a42ec31058 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -456,11 +456,13 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions() if (e.id != QQmlBinding::Invalid) { QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this))); QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, object(), d->compilationUnit->runtimeFunctions[e.id])); - newBinding = new QQmlBinding(function, object(), context); + newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, + function, object(), context); } // QQmlBinding *newBinding = e.id != QQmlBinding::Invalid ? QQmlBinding::createBinding(e.id, object(), qmlContext(this)) : 0; if (!newBinding) - newBinding = new QQmlBinding(e.expression, object(), context, e.url.toString(), e.line, e.column); + newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, + e.expression, object(), context, e.url.toString(), e.line, e.column); if (d->isExplicit) { // in this case, we don't want to assign a binding, per se, @@ -624,8 +626,11 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString if (entry.name == name) { entry.expression = expression; if (state() && state()->isStateActive()) { - QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this)); - newBinding->setTarget(d->property(name)); + auto prop = d->property(name); + QQmlBinding *newBinding = QQmlBinding::create( + &QQmlPropertyPrivate::get(prop)->core, expression, object(), + qmlContext(this)); + newBinding->setTarget(prop); QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); } return; @@ -643,8 +648,11 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString state()->changeBindingInRevertList(object(), name, oldBinding); } - QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this)); - newBinding->setTarget(d->property(name)); + auto prop = d->property(name); + QQmlBinding *newBinding = QQmlBinding::create( + &QQmlPropertyPrivate::get(prop)->core, expression, object(), + qmlContext(this)); + newBinding->setTarget(prop); QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); } else { QQuickStateAction action; @@ -654,7 +662,9 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString action.specifiedObject = object(); action.specifiedProperty = name; - QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this)); + QQmlBinding *newBinding = QQmlBinding::create( + &QQmlPropertyPrivate::get(action.property)->core, expression, + object(), qmlContext(this)); if (d->isExplicit) { // don't assign the binding, merely evaluate the expression. // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString) diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 908600784e..3921f89d23 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -129,9 +129,10 @@ void CustomBinding::componentComplete() QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this))); QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, m_target, compilationUnit->runtimeFunctions[bindingId])); - QQmlBinding *qmlBinding = new QQmlBinding(function, m_target, context); QQmlProperty property(m_target, name, qmlContext(this)); + QQmlBinding *qmlBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, + function, m_target, context); qmlBinding->setTarget(property); QQmlPropertyPrivate::setBinding(property, qmlBinding); } diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 5a8f2747a9..7e00147963 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -157,7 +157,7 @@ void tst_qqmlproperty::qmlmetaproperty() QObject *obj = new QObject; - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(nullptr, QLatin1String("null"), 0, engine.rootContext())); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(obj, QObjectPrivate::get(obj)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); @@ -406,7 +406,7 @@ void tst_qqmlproperty::qmlmetaproperty_object() { QQmlProperty prop(&object); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); @@ -453,7 +453,7 @@ void tst_qqmlproperty::qmlmetaproperty_object() { QQmlProperty prop(&dobject); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -509,7 +509,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&object, QString("defaultProperty")); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); @@ -556,7 +556,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&dobject, QString("defaultProperty")); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -606,7 +606,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&dobject, QString("onClicked")); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -655,7 +655,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged")); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -710,7 +710,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() { QQmlProperty prop(&object, engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); @@ -757,7 +757,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() { QQmlProperty prop(&dobject, engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -813,7 +813,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&object, QString("defaultProperty"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); @@ -860,7 +860,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&dobject, QString("defaultProperty"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -910,7 +910,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&dobject, QString("onClicked"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); @@ -959,7 +959,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext())); static_cast(binding.data())->setTarget(prop); QVERIFY(binding); QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); -- cgit v1.2.3 From 8d139e5e42dddac7f8cd443d7040cab0e86acd19 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 24 Jun 2016 11:01:29 +0200 Subject: Minor optimization I've seen people write property SomeItem myProperty: null This is an unnecessary initialization that ends up creating a binding expression. Generally there's handling missing for null and undefined, but in this very case we should just optimize away the initializer expression, because QObject style properties are initialized to null by default, as opposed to undefined. Change-Id: I7af4cd45461304753c93cef3926f6e92d3b6c95d Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder.cpp | 14 ++++++- src/qml/compiler/qqmlirbuilder_p.h | 1 + .../data/NullObjectInitializerBase.qml | 5 +++ .../data/nullObjectInitializer.2.qml | 8 ++++ .../qqmlecmascript/data/nullObjectInitializer.qml | 4 ++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 44 ++++++++++++++++++++++ 6 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index c31a7bce4f..c282c13c53 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -886,7 +886,8 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) // process QML-like initializers (e.g. property Object o: Object {}) QQmlJS::AST::Node::accept(node->binding, this); } else if (node->statement) { - appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement); + if (!isRedundantNullInitializerForPropertyDeclaration(_propertyDeclaration, node->statement)) + appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement); } qSwap(_propertyDeclaration, property); } @@ -1344,6 +1345,17 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement) return true; } +bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement) +{ + if (property->type != QV4::CompiledData::Property::Custom) + return false; + QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast(statement); + if (!exprStmt) + return false; + QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression; + return QQmlJS::AST::cast(expr); +} + QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) { QQmlRefPointer compilationUnit = output.javaScriptCompilationUnit; diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index a29727fc9e..5a07684b9f 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -526,6 +526,7 @@ public: QString stringAt(int index) const { return jsGenerator->stringForIndex(index); } static bool isStatementNodeScript(QQmlJS::AST::Statement *statement); + static bool isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement); QList errors; diff --git a/tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml b/tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml new file mode 100644 index 0000000000..4006a2a782 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 +QtObject { + property Component factory: Component { QtObject {} } + property QtObject testProperty: factory.createObject() +} diff --git a/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml new file mode 100644 index 0000000000..d59f8f99f9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 +NullObjectInitializerBase { + testProperty: null + property bool success: false + Component.onCompleted: { + success = testProperty === null; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml new file mode 100644 index 0000000000..32bc62c9f2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property QtObject testProperty: null +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 7abbd1938d..07b5e55384 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -208,6 +208,7 @@ private slots: void dynamicCreationOwnership(); void regExpBug(); void nullObjectBinding(); + void nullObjectInitializer(); void deletedEngine(); void libraryScriptAssert(); void variantsAssignedUndefined(); @@ -5707,6 +5708,49 @@ void tst_qqmlecmascript::nullObjectBinding() delete object; } +void tst_qqmlecmascript::nullObjectInitializer() +{ + { + QQmlComponent component(&engine, testFileUrl("nullObjectInitializer.qml")); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + + QQmlData *ddata = QQmlData::get(obj.data(), /*create*/false); + QVERIFY(ddata); + + { + const int propertyIndex = obj->metaObject()->indexOfProperty("testProperty"); + QVERIFY(propertyIndex > 0); + QVERIFY(!ddata->hasBindingBit(propertyIndex)); + } + + QVariant value = obj->property("testProperty"); + QVERIFY(value.userType() == qMetaTypeId()); + QVERIFY(!value.value()); + } + + { + QQmlComponent component(&engine, testFileUrl("nullObjectInitializer.2.qml")); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + + QQmlData *ddata = QQmlData::get(obj.data(), /*create*/false); + QVERIFY(ddata); + + { + const int propertyIndex = obj->metaObject()->indexOfProperty("testProperty"); + QVERIFY(propertyIndex > 0); + QVERIFY(ddata->hasBindingBit(propertyIndex)); + } + + QVERIFY(obj->property("success").toBool()); + + QVariant value = obj->property("testProperty"); + QVERIFY(value.userType() == qMetaTypeId()); + QVERIFY(!value.value()); + } +} + // Test that bindings don't evaluate once the engine has been destroyed void tst_qqmlecmascript::deletedEngine() { -- cgit v1.2.3 From 1b897195a14b63a553b139983736d8dfdd419ffd Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 20 Jun 2016 16:56:52 +0200 Subject: Fix missing QQuickWidget updates Having an animated QQuickFramebufferObject inside a QQuickWidget is currently broken due to losing update requests. The problems are introduced in 4b4cf31c7a4bcb89cabca09102c4e0a22ab0c6b1 where we may erroneously reset the updatePending false even though there may be an active triggerUpdate ticking via the 5 ms update timer. Task-number: QTBUG-54239 Change-Id: If84af8ec9c992761cfef9049de642a943f91cfe6 Reviewed-by: Paul Olav Tvete --- src/quickwidgets/qquickwidget.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 7f869c6ba5..39f7b6c3d3 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1103,7 +1103,14 @@ void QQuickWidget::showEvent(QShowEvent *) d->createContext(); if (d->offscreenWindow->openglContext()) { d->render(true); - if (d->updatePending) { + // render() may have led to a QQuickWindow::update() call (for + // example, having a scene with a QQuickFramebufferObject::Renderer + // calling update() in its render()) which in turn results in + // renderRequested in the rendercontrol, ending up in + // triggerUpdate. In this case just calling update() is not + // acceptable, we need the full renderSceneGraph issued from + // timerEvent(). + if (!d->eventPending && d->updatePending) { d->updatePending = false; update(); } -- cgit v1.2.3 From a0aadc3226d99d6d950359383df596928f300001 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 27 Jun 2016 14:23:16 +0200 Subject: Fix typos and links in the sw and d3d12 backend docs plus mention the partial update support. Change-Id: I208fb1517b89f9260e80505f911d86c8b06ac4c7 Reviewed-by: Andy Nichols --- src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index 1bf6822bbd..d506e68d6a 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -104,11 +104,15 @@ uses the Raster paint engine to render the contents of the scene graph instead of OpenGL. As a result of not using OpenGL to render the scene graph, some features and optimizations are no longer available. Most Qt Quick 2 applications will run without modification though any attempts to use -unsupported features will be ignored. By using the Software adpatation it is +unsupported features will be ignored. By using the Software adaptation it is possible to run Qt Quick 2 applications on hardware and platforms that do not have OpenGL support. The Software adaptation was previously known as the Qt Quick 2D Renderer. +However, unlike the 2D Renderer, the new, integrated version supports partial +updates. This means that the full update of the window or screen contents is +now avoided, and only the changed areas get flushed. This can significantly +improve performance for many applications. \section2 Shader Effects ShaderEffect components in QtQuick 2 can not be rendered by the Software adptation. @@ -194,8 +198,8 @@ category \c{qt.scenegraph.general} is enabled. When encountering issues, always set the \c{QSG_INFO} and \c{QT_D3D_DEBUG} environment variables to 1 in order to get debug and warning messages printed on the debug output. The latter enables the Direct3D debug layer. Note that the -debug layer should not be enabled in production use since the performance it -can significantly impact performance (CPU load) due to increased API overhead. +debug layer should not be enabled in production use since it can significantly +impact performance (CPU load) due to increased API overhead. \section2 Render Loops @@ -241,8 +245,9 @@ See the ShaderEffect documentation for a detailed description. Particles, sprites, and other OpenGL-dependent tools like QQuickFramebufferObject are not currently supported. -Like with the Software adaptation, text is always rendered using the native -method. Distance field-based text rendering is not currently implemented. +Like with the \l{qtquick-visualcanvas-adaptations-software.html}{Software +adaptation}, text is always rendered using the native method. Distance +field-based text rendering is not currently implemented. Texture atlases are not currently in use. -- cgit v1.2.3 From 97212616d5a7fc68d9ee97015751326ec2908287 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 27 Jun 2016 11:55:33 +0200 Subject: Lazy create QmlEngine on accessing rootContext A common usecase appears to be to set variables in the rootContext before loading a url in a QQuickWidget. We there need to ensure there is a QmlEngine to set variables on when this is attempted. Change-Id: I07aff2104313eeb3fab902ea3c6043c3c82c50f7 Reviewed-by: Laszlo Agocs --- src/quickwidgets/qquickwidget.cpp | 12 +++++++----- src/quickwidgets/qquickwidget_p.h | 4 ++-- tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp | 6 +++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 05097b3ce2..49e7affa31 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -109,13 +109,13 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate())); } -void QQuickWidgetPrivate::ensureEngine() +void QQuickWidgetPrivate::ensureEngine() const { - Q_Q(QQuickWidget); + Q_Q(const QQuickWidget); if (!engine.isNull()) return; - engine = new QQmlEngine(q); + engine = new QQmlEngine(const_cast(q)); engine.data()->setIncubationController(offscreenWindow->incubationController()); } @@ -493,7 +493,8 @@ QUrl QQuickWidget::source() const QQmlEngine* QQuickWidget::engine() const { Q_D(const QQuickWidget); - return d->engine ? const_cast(d->engine.data()) : 0; + d->ensureEngine(); + return const_cast(d->engine.data()); } /*! @@ -506,7 +507,8 @@ QQmlEngine* QQuickWidget::engine() const QQmlContext* QQuickWidget::rootContext() const { Q_D(const QQuickWidget); - return d->engine ? d->engine.data()->rootContext() : 0; + d->ensureEngine(); + return d->engine.data()->rootContext(); } /*! diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h index f0e1f848e3..b01d634fcd 100644 --- a/src/quickwidgets/qquickwidget_p.h +++ b/src/quickwidgets/qquickwidget_p.h @@ -105,7 +105,7 @@ public: QImage grabFramebuffer() Q_DECL_OVERRIDE; void init(QQmlEngine* e = 0); - void ensureEngine(); + void ensureEngine() const; void handleWindowChange(); void invalidateRenderControl(); @@ -115,7 +115,7 @@ public: QUrl source; - QPointer engine; + mutable QPointer engine; QQmlComponent *component; QBasicTimer resizetimer; QQuickWindow *offscreenWindow; diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index 9b4d0dd7d1..ab2c41b6bf 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -305,9 +305,13 @@ void tst_qquickwidget::grabBeforeShow() void tst_qquickwidget::nullEngine() { QQuickWidget widget; - QVERIFY(widget.engine() == Q_NULLPTR); + // Default should have no errors, even with a null qml engine QVERIFY(widget.errors().isEmpty()); QCOMPARE(widget.status(), QQuickWidget::Null); + + // A QML engine should be created lazily. + QVERIFY(widget.rootContext()); + QVERIFY(widget.engine()); } QTEST_MAIN(tst_qquickwidget) -- cgit v1.2.3 From 261675f8a404b6d19df72a7d6c3432cc58fbafe6 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Mon, 27 Jun 2016 13:30:05 +0200 Subject: Software Adaptation: Improve clip node handling This commit solves two issues that were coming up with nested clip nodes. The first was that determining the intersection of two or more nested clip nodes was calculated incorrectly. This was fixed by making sure the calculated clip region was in the correct coordinate space. The second issue was because there was no distinction between an empty unclipped region, and a completely clipped region. So a property was added to specify when a RenderableNode had a clipRegion so that the empty QRegion could be interpreted correctly. This makes sure that we don't end up drawing nodes that were culled. Change-Id: Id7af092f7fc6a4602b44b585b7dee14084136962 Reviewed-by: Laszlo Agocs --- .../adaptations/software/qsgsoftwarerenderablenode.cpp | 14 ++++++++++---- .../adaptations/software/qsgsoftwarerenderablenode_p.h | 3 ++- .../software/qsgsoftwarerenderablenodeupdater.cpp | 13 ++++++++++--- .../software/qsgsoftwarerenderablenodeupdater_p.h | 4 +++- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 7cfbc2dfda..385b257e44 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -58,6 +58,7 @@ QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *nod : m_nodeType(type) , m_isOpaque(true) , m_isDirty(true) + , m_hasClipRegion(false) , m_opacity(1.0f) { switch (m_nodeType) { @@ -179,8 +180,12 @@ void QSGSoftwareRenderableNode::update() m_boundingRect = m_transform.mapRect(boundingRect); - if (m_clipRegion.rectCount() == 1) { - m_boundingRect = m_boundingRect.intersected(m_clipRegion.rects().first()); + if (m_hasClipRegion && m_clipRegion.rectCount() <= 1) { + // If there is a clipRegion, and it is empty, the item wont be rendered + if (m_clipRegion.isEmpty()) + m_boundingRect = QRect(); + else + m_boundingRect = m_boundingRect.intersected(m_clipRegion.rects().first()); } // Overrides @@ -284,12 +289,13 @@ void QSGSoftwareRenderableNode::setTransform(const QTransform &transform) update(); } -void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect) +void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect, bool hasClipRegion) { - if (m_clipRegion == clipRect) + if (m_clipRegion == clipRect && m_hasClipRegion == hasClipRegion) return; m_clipRegion = clipRect; + m_hasClipRegion = hasClipRegion; update(); } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h index d1a71cd580..fcbea7391a 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h @@ -97,7 +97,7 @@ public: bool isDirtyRegionEmpty() const; void setTransform(const QTransform &transform); - void setClipRegion(const QRegion &clipRegion); + void setClipRegion(const QRegion &clipRegion, bool hasClipRegion = true); void setOpacity(float opacity); QTransform transform() const { return m_transform; } QRegion clipRegion() const { return m_clipRegion; } @@ -136,6 +136,7 @@ private: QTransform m_transform; QRegion m_clipRegion; + bool m_hasClipRegion; float m_opacity; QRect m_boundingRect; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp index 1681f3c06b..f13edb87fb 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp @@ -58,6 +58,7 @@ QSGSoftwareRenderableNodeUpdater::QSGSoftwareRenderableNodeUpdater(QSGAbstractSo m_opacityState.push(1.0f); // Invalid RectF by default for no clip m_clipState.push(QRegion()); + m_hasClip = false; m_transformState.push(QTransform()); } @@ -81,10 +82,13 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGTransformNode *) bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node) { // Make sure to translate the clip rect into world coordinates - if (m_clipState.top().isEmpty()) { + if (m_clipState.count() == 1) { m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect()))); - } else - m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect()).intersected(m_clipState.top()))); + m_hasClip = true; + } else { + const QRegion transformedClipRect = m_transformState.top().map(QRegion(node->clipRect().toRect())); + m_clipState.push(transformedClipRect.intersected(m_clipState.top())); + } m_stateMap[node] = currentState(node); return true; } @@ -92,6 +96,8 @@ bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node) void QSGSoftwareRenderableNodeUpdater::endVisit(QSGClipNode *) { m_clipState.pop(); + if (m_clipState.count() == 1) + m_hasClip = false; } bool QSGSoftwareRenderableNodeUpdater::visit(QSGGeometryNode *node) @@ -264,6 +270,7 @@ QSGSoftwareRenderableNodeUpdater::NodeState QSGSoftwareRenderableNodeUpdater::cu NodeState state; state.opacity = m_opacityState.top(); state.clip = m_clipState.top(); + state.hasClip = m_hasClip; state.transform = m_transformState.top(); state.parent = node->parent(); return state; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h index 97492d4347..d7c12e8b02 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h @@ -93,6 +93,7 @@ private: struct NodeState { float opacity; QRegion clip; + bool hasClip; QTransform transform; QSGNode *parent; }; @@ -105,6 +106,7 @@ private: QSGAbstractSoftwareRenderer *m_renderer; QStack m_opacityState; QStack m_clipState; + bool m_hasClip; QStack m_transformState; QHash m_stateMap; }; @@ -122,7 +124,7 @@ bool QSGSoftwareRenderableNodeUpdater::updateRenderableNode(QSGSoftwareRenderabl //Update the node renderableNode->setTransform(m_transformState.top()); renderableNode->setOpacity(m_opacityState.top()); - renderableNode->setClipRegion(m_clipState.top()); + renderableNode->setClipRegion(m_clipState.top(), m_hasClip); renderableNode->update(); m_stateMap[node] = currentState(node); -- cgit v1.2.3 From da650dd913939042973804a7cd8c02b1fb18b66c Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 22 Jun 2016 12:09:40 +0200 Subject: Make QQuickWidget honor QQuickWindow::isPersistentSceneGraph Make the behavior compatible with QQuickWindow. [ChangeLog][QtQuick][Important Behavior Changes] QQuickWidget now follows the same behavior as QQuickWindow when it comes to the persistent scenegraph setting controlled by QQuickWindow::setPersistentSceneGraph(). In earlier releases the setting was ignored and the scenegraph was torn down on every hide event. This is not the case anymore. Task-number: QTBUG-54133 Change-Id: I2a68948c92b7b4e6dabf2c9323955f1b47563d65 Reviewed-by: Andy Nichols --- src/quickwidgets/qquickwidget.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 775f6f5698..4a98122fea 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -402,6 +402,22 @@ QObject *QQuickWidgetPrivate::focusObject() entire purpose of QQuickWidget is to render Quick scenes without a separate native window, hence making it a native widget should always be avoided. + \section1 Scene graph and context persistency + + QQuickWidget honors QQuickWindow::isPersistentSceneGraph(), meaning that + applications can decide - by calling + QQuickWindow::setPersistentSceneGraph() on the window returned from the + quickWindow() function - to let scenegraph nodes and other Qt Quick scene + related resources be released whenever the widget becomes hidden. By default + persistency is enabled, just like with QQuickWindow. + + When running with the OpenGL backend of the scene graph, QQuickWindow + offers the possibility to disable persistent OpenGL contexts as well. This + setting is currently ignored by QQuickWidget and the context is always + persistent. The OpenGL context is thus not destroyed when hiding the + widget. The context is destroyed only when the widget is destroyed or when + the widget gets reparented into another top-level widget's child hierarchy. + \section1 Limitations Putting other widgets underneath and making the QQuickWidget transparent will not lead @@ -777,8 +793,9 @@ void QQuickWidgetPrivate::createContext() { #ifndef QT_NO_OPENGL Q_Q(QQuickWidget); - // On hide-show we invalidate() but our context is kept. - // We nonetheless need to initialize() again. + + // On hide-show we may invalidate() (when !isPersistentSceneGraph) but our + // context is kept. We may need to initialize() again, though. const bool reinit = context && !offscreenWindow->openglContext(); if (!reinit) { @@ -812,9 +829,10 @@ void QQuickWidgetPrivate::createContext() offscreenSurface->create(); } - if (context->makeCurrent(offscreenSurface)) - renderControl->initialize(context); - else + if (context->makeCurrent(offscreenSurface)) { + if (!offscreenWindow->openglContext()) + renderControl->initialize(context); + } else #endif qWarning("QQuickWidget: Failed to make context current"); } @@ -1230,7 +1248,8 @@ void QQuickWidget::showEvent(QShowEvent *) void QQuickWidget::hideEvent(QHideEvent *) { Q_D(QQuickWidget); - d->invalidateRenderControl(); + if (!d->offscreenWindow->isPersistentSceneGraph()) + d->invalidateRenderControl(); QWindowPrivate *offscreenPrivate = QWindowPrivate::get(d->offscreenWindow); if (offscreenPrivate->visible) { offscreenPrivate->visible = false; -- cgit v1.2.3 From fda948b9932d801759cd7b585ef02de8db94c7f2 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 24 Jun 2016 11:09:36 +0200 Subject: Privately export QObjects included by QQuickPathView and QQuickListView The Tumbler in Qt Quick Controls 2 needs this. The first part of this was already done with 3da510a2, but it turns out that all Q_OBJECTS in headers included from other modules must also be exported. Change-Id: Ic9ec19cfd944b85213471fc0983afb0d121a3638 Reviewed-by: Mitch Curtis Reviewed-by: J-P Nurmi Reviewed-by: Shawn Rutledge --- src/quick/items/qquicklistview_p.h | 4 +++- src/quick/util/qquickpath_p.h | 23 ++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h index 7e86a47433..5a5e8a55fe 100644 --- a/src/quick/items/qquicklistview_p.h +++ b/src/quick/items/qquicklistview_p.h @@ -53,11 +53,13 @@ #include "qquickitemview_p.h" +#include + QT_BEGIN_NAMESPACE class QQuickListView; class QQuickListViewPrivate; -class Q_AUTOTEST_EXPORT QQuickViewSection : public QObject +class Q_QUICK_PRIVATE_EXPORT QQuickViewSection : public QObject { Q_OBJECT Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index e2c99de44e..fa6cbc668a 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -55,6 +55,7 @@ #include #include +#include #include #include @@ -69,7 +70,7 @@ struct QQuickPathData QList curves; }; -class Q_AUTOTEST_EXPORT QQuickPathElement : public QObject +class Q_QUICK_PRIVATE_EXPORT QQuickPathElement : public QObject { Q_OBJECT public: @@ -78,7 +79,7 @@ Q_SIGNALS: void changed(); }; -class Q_AUTOTEST_EXPORT QQuickPathAttribute : public QQuickPathElement +class Q_QUICK_PRIVATE_EXPORT QQuickPathAttribute : public QQuickPathElement { Q_OBJECT @@ -103,7 +104,7 @@ private: qreal _value; }; -class Q_AUTOTEST_EXPORT QQuickCurve : public QQuickPathElement +class Q_QUICK_PRIVATE_EXPORT QQuickCurve : public QQuickPathElement { Q_OBJECT @@ -145,7 +146,7 @@ private: QQmlNullableValue _relativeY; }; -class Q_AUTOTEST_EXPORT QQuickPathLine : public QQuickCurve +class Q_QUICK_PRIVATE_EXPORT QQuickPathLine : public QQuickCurve { Q_OBJECT public: @@ -154,7 +155,7 @@ public: void addToPath(QPainterPath &path, const QQuickPathData &); }; -class Q_AUTOTEST_EXPORT QQuickPathQuad : public QQuickCurve +class Q_QUICK_PRIVATE_EXPORT QQuickPathQuad : public QQuickCurve { Q_OBJECT @@ -194,7 +195,7 @@ private: QQmlNullableValue _relativeControlY; }; -class Q_AUTOTEST_EXPORT QQuickPathCubic : public QQuickCurve +class Q_QUICK_PRIVATE_EXPORT QQuickPathCubic : public QQuickCurve { Q_OBJECT @@ -260,7 +261,7 @@ private: QQmlNullableValue _relativeControl2Y; }; -class Q_AUTOTEST_EXPORT QQuickPathCatmullRomCurve : public QQuickCurve +class Q_QUICK_PRIVATE_EXPORT QQuickPathCatmullRomCurve : public QQuickCurve { Q_OBJECT public: @@ -269,7 +270,7 @@ public: void addToPath(QPainterPath &path, const QQuickPathData &); }; -class Q_AUTOTEST_EXPORT QQuickPathArc : public QQuickCurve +class Q_QUICK_PRIVATE_EXPORT QQuickPathArc : public QQuickCurve { Q_OBJECT Q_PROPERTY(qreal radiusX READ radiusX WRITE setRadiusX NOTIFY radiusXChanged) @@ -311,7 +312,7 @@ private: ArcDirection _direction; }; -class Q_AUTOTEST_EXPORT QQuickPathSvg : public QQuickCurve +class Q_QUICK_PRIVATE_EXPORT QQuickPathSvg : public QQuickCurve { Q_OBJECT Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged) @@ -330,7 +331,7 @@ private: QString _path; }; -class Q_AUTOTEST_EXPORT QQuickPathPercent : public QQuickPathElement +class Q_QUICK_PRIVATE_EXPORT QQuickPathPercent : public QQuickPathElement { Q_OBJECT Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged) @@ -359,7 +360,7 @@ struct QQuickCachedBezier }; class QQuickPathPrivate; -class Q_AUTOTEST_EXPORT QQuickPath : public QObject, public QQmlParserStatus +class Q_QUICK_PRIVATE_EXPORT QQuickPath : public QObject, public QQmlParserStatus { Q_OBJECT -- cgit v1.2.3 From c0e4ef9f63d41f96287274b2fc7923dacac29c23 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Tue, 28 Jun 2016 14:36:43 +0200 Subject: Doc: corrected link issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Can't link to 'Qt Quick Controls - Basic Layouts Example' Change-Id: I74abd84549a870e800a6c5fb361565a78e8605ee Reviewed-by: Topi Reiniö --- src/quick/doc/src/examples.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/doc/src/examples.qdoc b/src/quick/doc/src/examples.qdoc index e69c2f6551..e41b472ee1 100644 --- a/src/quick/doc/src/examples.qdoc +++ b/src/quick/doc/src/examples.qdoc @@ -135,7 +135,7 @@ Creator. \div {class="doc-column"} \b{Layouts and Views} \list - \li \l{Qt Quick Controls - Basic Layouts Example}{Basic Layouts} + \li \l{Qt Quick Layouts - Basic Example} \li \l{Qt Quick Examples - Positioners}{Positioners} \li \l{Qt Quick Examples - Views}{Views} \li \l{Qt Quick Examples - Window and Screen}{Windows and Screen} -- cgit v1.2.3 From d5cb1bf4a9e19a0a4471ba5c935f441463a73414 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Tue, 28 Jun 2016 14:12:34 +0200 Subject: Doc: solved link issue snippet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cannot find file to quote from: qml/windowconstraints.qml Change-Id: Ic01061c316d56f1d2d1bf2be394fe9395ce9c79a Reviewed-by: Topi Reiniö Reviewed-by: Martin Smith --- src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc index 1b6e7dc539..20a6d131f5 100644 --- a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc +++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc @@ -84,7 +84,7 @@ stretches horizontally. The azure rectangle can be resized from 50x150 to 300x150, and the plum rectangle can be resized from 100x100 to ∞x100. - \snippet windowconstraints.qml rowlayout + \snippet qml/windowconstraints.qml rowlayout \image rowlayout-minimum.png "RowLayout at its minimum" -- cgit v1.2.3 From d44e75271cfccde26761ad9ed8accdd872f478d1 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Tue, 28 Jun 2016 14:12:30 +0200 Subject: Cleanup some unused functions and variables with QT_NO_OPENGL Without theses additional #ifdefs building with clang fails. Change-Id: Ib6993b6d14bf73b606e1bcb991f7436a9c4c9638 Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/util/qsgflatcolormaterial.cpp | 3 ++- src/quick/scenegraph/util/qsgtexture.cpp | 4 ++++ src/quick/scenegraph/util/qsgtexturematerial.cpp | 2 ++ src/quick/scenegraph/util/qsgvertexcolormaterial.cpp | 3 ++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp index 5d7817163e..2ce27275cd 100644 --- a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp +++ b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp @@ -57,9 +57,10 @@ public: private: virtual void initialize(); - +#ifndef QT_NO_OPENGL int m_matrix_id; int m_color_id; +#endif }; QSGMaterialType FlatColorMaterialShader::type; diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 19403deb73..4cf339aeb8 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -70,7 +70,9 @@ #include #endif +#ifndef QT_NO_OPENGL static QElapsedTimer qsg_renderer_timer; +#endif #ifndef QT_NO_DEBUG static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"); @@ -84,11 +86,13 @@ static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK" QT_BEGIN_NAMESPACE +#ifndef QT_NO_OPENGL inline static bool isPowerOfTwo(int x) { // Assumption: x >= 1 return x == (x & -x); } +#endif QSGTexturePrivate::QSGTexturePrivate() : wrapChanged(false) diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp index 3db8163376..119828bc81 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.cpp +++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp @@ -46,11 +46,13 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_OPENGL inline static bool isPowerOfTwo(int x) { // Assumption: x >= 1 return x == (x & -x); } +#endif QSGMaterialType QSGOpaqueTextureMaterialShader::type; diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp index dedbc86385..847ec289d8 100644 --- a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp +++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp @@ -55,9 +55,10 @@ public: private: virtual void initialize(); - +#ifndef QT_NO_OPENGL int m_matrix_id; int m_opacity_id; +#endif }; QSGMaterialType QSGVertexColorMaterialShader::type; -- cgit v1.2.3 From 285007f9687ef8aca6faed89f0c56696c2612548 Mon Sep 17 00:00:00 2001 From: Paolo Angelelli Date: Tue, 28 Jun 2016 12:00:53 +0200 Subject: Add mipmapping support to QSGImageNode This patch adds two virtual methods to QSGImageNode to set/get the filtering mode for mipmapping, which makes it also possible to enable it, which was previously not possible Change-Id: Ie08a11aab35d8ba335841ca0eb73ef4b3d184d7e Reviewed-by: Laszlo Agocs --- src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp | 14 ++++++++++++++ src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h | 3 +++ .../adaptations/software/qsgsoftwarepublicnodes_p.h | 3 +++ src/quick/scenegraph/util/qsgdefaultimagenode.cpp | 15 +++++++++++++++ src/quick/scenegraph/util/qsgdefaultimagenode_p.h | 3 +++ src/quick/scenegraph/util/qsgimagenode.cpp | 15 +++++++++++++++ src/quick/scenegraph/util/qsgimagenode.h | 3 +++ 7 files changed, 56 insertions(+) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp index de9869aee8..783caa280f 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp @@ -118,6 +118,20 @@ QSGTexture::Filtering QSGD3D12ImageNode::filtering() const return m_material.filtering(); } +void QSGD3D12ImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.mipmapFiltering() == filtering) + return; + + m_material.setMipmapFiltering(filtering); + markDirty(DirtyMaterial); +} + +QSGTexture::Filtering QSGD3D12ImageNode::mipmapFiltering() const +{ + return m_material.mipmapFiltering(); +} + void QSGD3D12ImageNode::setRect(const QRectF &r) { if (m_rect == r) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h index 14e34bc0ac..6150083aaf 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h @@ -92,6 +92,9 @@ public: void setFiltering(QSGTexture::Filtering filtering) override; QSGTexture::Filtering filtering() const override; + void setMipmapFiltering(QSGTexture::Filtering filtering) override; + QSGTexture::Filtering mipmapFiltering() const override; + void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) override; TextureCoordinatesTransformMode textureCoordinatesTransform() const override; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h index 8f5216d03a..8a11f25c82 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h @@ -94,6 +94,9 @@ public: void setFiltering(QSGTexture::Filtering) override { } QSGTexture::Filtering filtering() const override { return QSGTexture::None; } + void setMipmapFiltering(QSGTexture::Filtering) override { } + QSGTexture::Filtering mipmapFiltering() const override { return QSGTexture::None; } + void setTextureCoordinatesTransform(TextureCoordinatesTransformMode) override { } TextureCoordinatesTransformMode textureCoordinatesTransform() const override { return NoTransform; } diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode.cpp b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp index ed3d73adb4..6afe591dca 100644 --- a/src/quick/scenegraph/util/qsgdefaultimagenode.cpp +++ b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp @@ -79,6 +79,21 @@ QSGTexture::Filtering QSGDefaultImageNode::filtering() const return m_material.filtering(); } +void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.mipmapFiltering() == filtering) + return; + + m_material.setMipmapFiltering(filtering); + m_opaque_material.setMipmapFiltering(filtering); + markDirty(DirtyMaterial); +} + +QSGTexture::Filtering QSGDefaultImageNode::mipmapFiltering() const +{ + return m_material.mipmapFiltering(); +} + void QSGDefaultImageNode::setRect(const QRectF &r) { if (m_rect == r) diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h index 5316542cff..eb6c487c18 100644 --- a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h +++ b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h @@ -76,6 +76,9 @@ public: void setFiltering(QSGTexture::Filtering filtering) override; QSGTexture::Filtering filtering() const override; + void setMipmapFiltering(QSGTexture::Filtering filtering) override; + QSGTexture::Filtering mipmapFiltering() const override; + void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) override; TextureCoordinatesTransformMode textureCoordinatesTransform() const override; diff --git a/src/quick/scenegraph/util/qsgimagenode.cpp b/src/quick/scenegraph/util/qsgimagenode.cpp index fbc4314bd8..a78bfc1c66 100644 --- a/src/quick/scenegraph/util/qsgimagenode.cpp +++ b/src/quick/scenegraph/util/qsgimagenode.cpp @@ -127,6 +127,21 @@ QT_BEGIN_NAMESPACE Returns the filtering for this image node. */ +/*! + \fn void QSGImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) + + Sets the mipmap filtering to be used for this image node to \a filtering. + + For smooth scaling between mip maps, use QSGTexture::Linear. + For normal scaling, use QSGTexture::Nearest. + */ + +/*! + \fn QSGTexture::Filtering QSGImageNode::mipmapFiltering() const + + Returns the mipmap filtering for this image node. + */ + /*! \enum QSGImageNode::TextureCoordinatesTransformFlag diff --git a/src/quick/scenegraph/util/qsgimagenode.h b/src/quick/scenegraph/util/qsgimagenode.h index a094924ac3..7eab42c4e6 100644 --- a/src/quick/scenegraph/util/qsgimagenode.h +++ b/src/quick/scenegraph/util/qsgimagenode.h @@ -64,6 +64,9 @@ public: virtual void setFiltering(QSGTexture::Filtering filtering) = 0; virtual QSGTexture::Filtering filtering() const = 0; + virtual void setMipmapFiltering(QSGTexture::Filtering filtering) = 0; + virtual QSGTexture::Filtering mipmapFiltering() const = 0; + enum TextureCoordinatesTransformFlag { NoTransform = 0x00, MirrorHorizontally = 0x01, -- cgit v1.2.3 From a174ed2a996bf6311e849c8d8f31bbe307c6838b Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 22 Jun 2016 12:17:15 +0200 Subject: Handle AA_ShareOpenGLContexts consistently in QQuickWidget QOpenGLWidget has offered a lot more fine-grained control over the lifetime of the OpenGL context it uses for rendering. In practice QQuickWidget also requires at least a part of this. To unify the behavior when it comes to reparenting to a different top-level window, add the bail out condition for AA_ShareOpenGLContexts to QQuickWidget as well. [ChangeLog][QtQuick][Important Behavior Changes] QQuickWidget now behaves identically to QOpenGLWidget when it comes to handling window changes when reparenting the widget into a hierarchy belonging to another top-level widget. Previously the OpenGL context always got destroyed and recreated in order to ensure texture resource sharing with the new top-level widget. From now on this is only true when when AA_ShareOpenGLContexts it not set. Task-number: QTBUG-54133 Change-Id: Ifda0db76fdf71dae1b9606fb5d59cee6edc2f5a4 Reviewed-by: Andy Nichols --- src/quickwidgets/qquickwidget.cpp | 25 ++++++++++++++++++++++ .../quickwidgets/qquickwidget/tst_qquickwidget.cpp | 23 ++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 4a98122fea..9dba007540 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -154,7 +154,18 @@ void QQuickWidgetPrivate::invalidateRenderControl() void QQuickWidgetPrivate::handleWindowChange() { + if (offscreenWindow->isPersistentSceneGraph() && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) + return; + + // In case of !isPersistentSceneGraph or when we need a new context due to + // the need to share resources with the new window's context, we must both + // invalidate the scenegraph and destroy the context. With + // QQuickRenderControl destroying the context must be preceded by an + // invalidate to prevent being left with dangling context references in the + // rendercontrol. + invalidateRenderControl(); + if (!useSoftwareRenderer) destroyContext(); } @@ -417,6 +428,20 @@ QObject *QQuickWidgetPrivate::focusObject() persistent. The OpenGL context is thus not destroyed when hiding the widget. The context is destroyed only when the widget is destroyed or when the widget gets reparented into another top-level widget's child hierarchy. + However, some applications, in particular those that have their own + graphics resources due to performing custom OpenGL rendering in the Qt + Quick scene, may wish to disable the latter since they may not be prepared + to handle the loss of the context when moving a QQuickWidget into another + window. Such applications can set the + QCoreApplication::AA_ShareOpenGLContexts attribute. For a discussion on the + details of resource initialization and cleanup, refer to the QOpenGLWidget + documentation. + + \note QQuickWidget offers less fine-grained control over its internal + OpenGL context than QOpenGLWidget, and there are subtle differences, most + notably that disabling the persistent scene graph will lead to destroying + the context on a window change regardless of the presence of + QCoreApplication::AA_ShareOpenGLContexts. \section1 Limitations diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index 09359060f6..7676bd6c31 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -34,6 +34,7 @@ #include #include "../../shared/util.h" #include +#include #include #include @@ -55,6 +56,7 @@ private slots: void readback(); void renderingSignals(); void grabBeforeShow(); + void reparentToNewWindow(); }; @@ -301,6 +303,27 @@ void tst_qquickwidget::grabBeforeShow() QVERIFY(!widget.grab().isNull()); } +void tst_qquickwidget::reparentToNewWindow() +{ + QWidget window1; + QWidget window2; + + QQuickWidget *qqw = new QQuickWidget(&window1); + qqw->setSource(testFileUrl("rectangle.qml")); + window1.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window1, 5000)); + window2.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window2, 5000)); + + QSignalSpy afterRenderingSpy(qqw->quickWindow(), &QQuickWindow::afterRendering); + qqw->setParent(&window2); + qqw->show(); + QTRY_VERIFY(afterRenderingSpy.size() > 0); + + QImage img = qqw->grabFramebuffer(); + QCOMPARE(img.pixel(5, 5), qRgb(255, 0, 0)); +} + QTEST_MAIN(tst_qquickwidget) #include "tst_qquickwidget.moc" -- cgit v1.2.3 From ac44a5b574289e8a24c7ad76760fdbb0a0728478 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Tue, 28 Jun 2016 13:17:30 +0200 Subject: qmlplugindump: Avoid warnings about missing createPlatformOpenGLContext Fixes a regression introduced in 541eb6b704b53a6e88. There's no point in setting AA_ShareOpenGLContexts for the minimal plugin. Change-Id: Ibcc65ede41acf5a8cf22eacef4b94c20048a354a Reviewed-by: Marco Benelli --- tools/qmlplugindump/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index d821c25b4e..5a39e497d2 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -979,8 +979,9 @@ int main(int argc, char *argv[]) if (!requireWindowManager) qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("minimal")); + else + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); QGuiApplication app(argc, argv); const QStringList args = app.arguments(); const QString appName = QFileInfo(app.applicationFilePath()).baseName(); -- cgit v1.2.3 From 12bff27aa6f1caac9bfacf365c542cf5da6e3148 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 27 Jun 2016 16:10:04 +0200 Subject: Add support for reading GLSL from files with ShaderEffect An enabler for using file selectors instead of writing GraphicsInfo-based conditions in the vertexShader and fragmentShader properties. [ChangeLog][QtQuick] ShaderEffect with OpenGL now supports reading GLSL shader sources from local files and from the resource system. Whenever a fragmentShader or vertexShader property is a valid such URL, the value is treated as a file specification instead of actual source code. Change-Id: I9df2a397adc8f82f1eb6b83d27e9e3d9d1a02f46 Reviewed-by: Andy Nichols --- src/quick/items/qquickopenglshadereffect.cpp | 15 +++++++++++++++ src/quick/scenegraph/qsgdefaultcontext.cpp | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index 312721ed04..1e66971fdb 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -51,6 +51,7 @@ #include "qquickshadereffectsource_p.h" #include "qquickshadereffectmesh_p.h" +#include #include QT_BEGIN_NAMESPACE @@ -321,6 +322,20 @@ void QQuickOpenGLShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderT if (shaderType == Key::VertexShader) attributes.clear(); + // A qrc or file URL means the shader source is to be read from the specified file. + QUrl srcUrl(QString::fromUtf8(source.sourceCode[shaderType])); + if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) { + const QString fn = QQmlFile::urlToLocalFileOrQrc(srcUrl); + QFile f(fn); + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + source.sourceCode[shaderType] = f.readAll(); + f.close(); + } else { + qWarning("ShaderEffect: Failed to read %s", qPrintable(fn)); + source.sourceCode[shaderType] = QByteArray(); + } + } + const QByteArray &code = source.sourceCode[shaderType]; if (code.isEmpty()) { // Optimize for default code. diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp index a33f4b174f..8272be987b 100644 --- a/src/quick/scenegraph/qsgdefaultcontext.cpp +++ b/src/quick/scenegraph/qsgdefaultcontext.cpp @@ -272,7 +272,7 @@ QSGRendererInterface::ShaderCompilationTypes QSGDefaultContext::shaderCompilatio QSGRendererInterface::ShaderSourceTypes QSGDefaultContext::shaderSourceType() const { - return ShaderSourceString; + return ShaderSourceString | ShaderSourceFile; } QT_END_NAMESPACE -- cgit v1.2.3 From 5ee02242d72ac9a536584b36ba549480a97303f7 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 27 Jun 2016 17:13:37 +0200 Subject: Unify ShaderEffect property setting rendererInterface() should not require isSceneGraphInitialized() to be true - the API and language queries like graphicsApi() have no need for the scenegraph, they only need the plugin to be loaded, i.e. that the QQuickWindow is constructed. This is the key to be able to make GraphicsInfo report graphicsApi and shaderType with the correct values as early as possible - meaning as soon as the item is associated with a window. The initialization of the scenegraph (the exact timing of which varies backend to backend) does not matter here. The fragment and vertex shader property setting is now unified in the two ShaderEffect implementations: - If the component is complete, the shader is attempted to be processed right from the setter. - Otherwise the item will trigger processing once the component is complete. - If there is no window when processing is trigerred, it is deferred via polish. To implement item polish handling we need a new virtual in QQuickItemPrivate since we cannot intrdouce virtuals into the public classes. This way one can write a condition (and later potentially use file selectors) like this: fragmentShader: GraphicsInfo.shaderType == GraphicsInfo.GLSL ? "..." : ... without having to worry about getting an unintended value processed due to GraphicsInfo not yet reporting an up-to-date value. parseLog() forces, for GL at least, shader processing to prevent autotests from breaking. Change-Id: If55c69d746c29cd07348ddad2d6b0f2b5dd7f3a2 Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12context.cpp | 7 +-- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 22 +------- src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h | 9 +-- .../scenegraph/d3d12/qsgd3d12rendercontext.cpp | 30 ++++++++++ .../scenegraph/d3d12/qsgd3d12rendercontext_p.h | 10 +++- src/quick/items/qquickgenericshadereffect.cpp | 65 ++++++++++------------ src/quick/items/qquickgenericshadereffect_p.h | 9 ++- src/quick/items/qquickgraphicsinfo.cpp | 35 ++++++------ src/quick/items/qquickitem_p.h | 2 + src/quick/items/qquickopenglshadereffect.cpp | 31 +++++++++-- src/quick/items/qquickopenglshadereffect_p.h | 4 +- src/quick/items/qquickshadereffect.cpp | 29 ++++++++-- src/quick/items/qquickshadereffect_p.h | 3 + src/quick/items/qquickwindow.cpp | 25 +++++++-- .../scenegraph/coreapi/qsgrendererinterface.cpp | 41 +++++++------- src/quick/scenegraph/qsgcontext.cpp | 10 +++- 16 files changed, 201 insertions(+), 131 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp index 07c9287f80..1b29ddd59c 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp @@ -109,12 +109,7 @@ QSurfaceFormat QSGD3D12Context::defaultSurfaceFormat() const QSGRendererInterface *QSGD3D12Context::rendererInterface(QSGRenderContext *renderContext) { - QSGD3D12RenderContext *rc = static_cast(renderContext); - if (!rc->engine()) { - qWarning("No D3D12 engine available yet (no render thread due to window not exposed?)"); - return nullptr; - } - return rc->engine(); + return static_cast(renderContext); } QSGRectangleNode *QSGD3D12Context::createRectangleNode() diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index b54d10aa85..e1b076c39a 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -535,31 +535,11 @@ void QSGD3D12Engine::simulateDeviceLoss() d->simulateDeviceLoss(); } -QSGRendererInterface::GraphicsApi QSGD3D12Engine::graphicsApi() const -{ - return Direct3D12; -} - -void *QSGD3D12Engine::getResource(Resource resource) const +void *QSGD3D12Engine::getResource(QSGRendererInterface::Resource resource) const { return d->getResource(resource); } -QSGRendererInterface::ShaderType QSGD3D12Engine::shaderType() const -{ - return HLSL; -} - -QSGRendererInterface::ShaderCompilationTypes QSGD3D12Engine::shaderCompilationType() const -{ - return RuntimeCompilation | OfflineCompilation; -} - -QSGRendererInterface::ShaderSourceTypes QSGD3D12Engine::shaderSourceType() const -{ - return ShaderSourceString | ShaderByteCode; -} - static inline quint32 alignedSize(quint32 size, quint32 byteAlign) { return (size + byteAlign - 1) & ~(byteAlign - 1); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h index 2ebe1e733a..ad89696bbe 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h @@ -281,7 +281,7 @@ inline uint qHash(const QSGD3D12PipelineState &key, uint seed = 0) + key.topologyType; } -class QSGD3D12Engine : public QSGRendererInterface +class QSGD3D12Engine { public: QSGD3D12Engine(); @@ -372,12 +372,7 @@ public: void simulateDeviceLoss(); - // QSGRendererInterface - GraphicsApi graphicsApi() const override; - void *getResource(Resource resource) const override; - ShaderType shaderType() const override; - ShaderCompilationTypes shaderCompilationType() const override; - ShaderSourceTypes shaderSourceType() const override; + void *getResource(QSGRendererInterface::Resource resource) const; private: QSGD3D12EnginePrivate *d; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp index b32bfe063a..7e62421380 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp @@ -126,4 +126,34 @@ void QSGD3D12RenderContext::ensureInitializedEmitted() emit initialized(); } +QSGRendererInterface::GraphicsApi QSGD3D12RenderContext::graphicsApi() const +{ + return Direct3D12; +} + +void *QSGD3D12RenderContext::getResource(Resource resource) const +{ + if (!m_engine) { + qWarning("getResource: No D3D12 engine available yet (window not exposed?)"); + return nullptr; + } + + return m_engine->getResource(resource); +} + +QSGRendererInterface::ShaderType QSGD3D12RenderContext::shaderType() const +{ + return HLSL; +} + +QSGRendererInterface::ShaderCompilationTypes QSGD3D12RenderContext::shaderCompilationType() const +{ + return RuntimeCompilation | OfflineCompilation; +} + +QSGRendererInterface::ShaderSourceTypes QSGD3D12RenderContext::shaderSourceType() const +{ + return ShaderSourceString | ShaderByteCode; +} + QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h index 86a300831d..a1029b019e 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h @@ -52,12 +52,13 @@ // #include +#include QT_BEGIN_NAMESPACE class QSGD3D12Engine; -class QSGD3D12RenderContext : public QSGRenderContext +class QSGD3D12RenderContext : public QSGRenderContext, public QSGRendererInterface { public: QSGD3D12RenderContext(QSGContext *ctx); @@ -73,6 +74,13 @@ public: void ensureInitializedEmitted(); void setInitializedPending() { m_pendingInitialized = true; } + // QSGRendererInterface + GraphicsApi graphicsApi() const override; + void *getResource(Resource resource) const override; + ShaderType shaderType() const override; + ShaderCompilationTypes shaderCompilationType() const override; + ShaderSourceTypes shaderSourceType() const override; + private: QSGD3D12Engine *m_engine = nullptr; bool m_pendingInitialized = false; diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp index 11259a588a..683453d4ee 100644 --- a/src/quick/items/qquickgenericshadereffect.cpp +++ b/src/quick/items/qquickgenericshadereffect.cpp @@ -59,10 +59,11 @@ QQuickGenericShaderEffect::QQuickGenericShaderEffect(QQuickShaderEffect *item, Q , m_blending(true) , m_supportsAtlasTextures(false) , m_mgr(nullptr) + , m_fragNeedsUpdate(true) + , m_vertNeedsUpdate(true) , m_dirty(0) { qRegisterMetaType("ShaderInfo::Type"); - connect(m_item, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(itemWindowChanged(QQuickWindow*))); for (int i = 0; i < NShader; ++i) m_inProgress[i] = nullptr; } @@ -88,12 +89,11 @@ void QQuickGenericShaderEffect::setFragmentShader(const QByteArray &src) return; m_fragShader = src; - m_dirty |= QSGShaderEffectNode::DirtyShaders; + m_fragNeedsUpdate = true; if (m_item->isComponentComplete()) - updateShader(Fragment, src); + maybeUpdateShaders(); - m_item->update(); emit m_item->fragmentShaderChanged(); } @@ -103,12 +103,11 @@ void QQuickGenericShaderEffect::setVertexShader(const QByteArray &src) return; m_vertShader = src; - m_dirty |= QSGShaderEffectNode::DirtyShaders; + m_vertNeedsUpdate = true; if (m_item->isComponentComplete()) - updateShader(Vertex, src); + maybeUpdateShaders(); - m_item->update(); emit m_item->vertexShaderChanged(); } @@ -187,6 +186,12 @@ void QQuickGenericShaderEffect::setSupportsAtlasTextures(bool supports) emit m_item->supportsAtlasTexturesChanged(); } +QString QQuickGenericShaderEffect::parseLog() +{ + maybeUpdateShaders(); + return log(); +} + QString QQuickGenericShaderEffect::log() const { QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager(); @@ -294,10 +299,14 @@ QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQui return node; } -void QQuickGenericShaderEffect::handleComponentComplete() +void QQuickGenericShaderEffect::maybeUpdateShaders() { - updateShader(Vertex, m_vertShader); - updateShader(Fragment, m_fragShader); + if (m_vertNeedsUpdate) + m_vertNeedsUpdate = !updateShader(Vertex, m_vertShader); + if (m_fragNeedsUpdate) + m_fragNeedsUpdate = !updateShader(Fragment, m_fragShader); + if (m_vertNeedsUpdate || m_fragNeedsUpdate) + m_item->polish(); } void QQuickGenericShaderEffect::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) @@ -326,9 +335,8 @@ QSGGuiThreadShaderEffectManager *QQuickGenericShaderEffect::shaderEffectManager( // return null if this is not the gui thread and not already created if (QThread::currentThread() != m_item->thread()) return m_mgr; - // need a window and a rendercontext (i.e. the scenegraph backend is ready) QQuickWindow *w = m_item->window(); - if (w && w->isSceneGraphInitialized()) { + if (w) { // note: just the window, don't care about isSceneGraphInitialized() here m_mgr = QQuickWindowPrivate::get(w)->context->sceneGraphContext()->createGuiThreadShaderEffectManager(); if (m_mgr) { connect(m_mgr, SIGNAL(logAndStatusChanged()), m_item, SIGNAL(logChanged())); @@ -336,34 +344,12 @@ QSGGuiThreadShaderEffectManager *QQuickGenericShaderEffect::shaderEffectManager( connect(m_mgr, SIGNAL(textureChanged()), this, SLOT(markGeometryDirtyAndUpdateIfSupportsAtlas())); connect(m_mgr, &QSGGuiThreadShaderEffectManager::shaderCodePrepared, this, &QQuickGenericShaderEffect::shaderCodePrepared); } - } else if (!w) { - // Wait until itemWindowChanged() gets called. Return null for now. - } else { - // Have window, but no scenegraph -> ensure the signal is connected. Return null for now. - const_cast(this)->itemWindowChanged(w); } } return m_mgr; } -void QQuickGenericShaderEffect::itemWindowChanged(QQuickWindow *w) -{ - if (w) { - if (w->isSceneGraphInitialized()) - backendChanged(); - else - connect(w, SIGNAL(sceneGraphInitialized()), this, SLOT(backendChanged()), Qt::UniqueConnection); - } -} - -void QQuickGenericShaderEffect::backendChanged() -{ - disconnect(m_item->window(), SIGNAL(sceneGraphInitialized()), this, SLOT(backendChanged())); - emit m_item->logChanged(); - emit m_item->statusChanged(); -} - void QQuickGenericShaderEffect::disconnectSignals(Shader shaderType) { for (auto &sm : m_signalMappers[shaderType]) { @@ -407,11 +393,11 @@ struct ShaderInfoCache Q_GLOBAL_STATIC(ShaderInfoCache, shaderInfoCache) -void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray &src) +bool QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray &src) { QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager(); if (!mgr) - return; + return false; const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects(); @@ -439,7 +425,7 @@ void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray // source->bytecode compilation as well in this step. mgr->prepareShaderCode(typeHint, src, m_inProgress[shaderType]); // the rest is handled in shaderCodePrepared() - return; + return true; } } else { m_shaders[shaderType].hasShaderCode = false; @@ -459,6 +445,9 @@ void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray } updateShaderVars(shaderType); + m_dirty |= QSGShaderEffectNode::DirtyShaders; + m_item->update(); + return true; } void QQuickGenericShaderEffect::shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint, @@ -486,6 +475,8 @@ void QQuickGenericShaderEffect::shaderCodePrepared(bool ok, QSGGuiThreadShaderEf m_shaders[shaderType].hasShaderCode = true; shaderInfoCache()->insert(src, m_shaders[shaderType].shaderInfo); updateShaderVars(shaderType); + m_dirty |= QSGShaderEffectNode::DirtyShaders; + m_item->update(); } void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType) diff --git a/src/quick/items/qquickgenericshadereffect_p.h b/src/quick/items/qquickgenericshadereffect_p.h index 5ec83eb60f..ab19816493 100644 --- a/src/quick/items/qquickgenericshadereffect_p.h +++ b/src/quick/items/qquickgenericshadereffect_p.h @@ -90,19 +90,20 @@ public: bool supportsAtlasTextures() const { return m_supportsAtlasTextures; } void setSupportsAtlasTextures(bool supports); + QString parseLog(); + void handleEvent(QEvent *); void handleGeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); QSGNode *handleUpdatePaintNode(QSGNode *, QQuickItem::UpdatePaintNodeData *); void handleComponentComplete(); void handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value); + void maybeUpdateShaders(); private slots: void propertyChanged(int mappedId); void sourceDestroyed(QObject *object); void markGeometryDirtyAndUpdate(); void markGeometryDirtyAndUpdateIfSupportsAtlas(); - void itemWindowChanged(QQuickWindow *w); - void backendChanged(); void shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint, const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result); @@ -115,7 +116,7 @@ private: NShader }; - void updateShader(Shader shaderType, const QByteArray &src); + bool updateShader(Shader shaderType, const QByteArray &src); void updateShaderVars(Shader shaderType); void disconnectSignals(Shader shaderType); bool sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const; @@ -129,7 +130,9 @@ private: bool m_supportsAtlasTextures; mutable QSGGuiThreadShaderEffectManager *m_mgr; QByteArray m_fragShader; + bool m_fragNeedsUpdate; QByteArray m_vertShader; + bool m_vertNeedsUpdate; QSGShaderEffectNode::ShaderData m_shaders[NShader]; QSGShaderEffectNode::DirtyShaderFlags m_dirty; diff --git a/src/quick/items/qquickgraphicsinfo.cpp b/src/quick/items/qquickgraphicsinfo.cpp index 79b0edf031..761d0c3cad 100644 --- a/src/quick/items/qquickgraphicsinfo.cpp +++ b/src/quick/items/qquickgraphicsinfo.cpp @@ -112,11 +112,11 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object) \endlist \note The value is only up-to-date once the item is associated with a - window and the window's scenegraph has initialized. Bindings relying on the - value have to keep this in mind since the value may change from - GraphicsInfo.UnknownShadingLanguage to the actual value after component - initialization is complete. This is particularly relevant for ShaderEffect - items inside ShaderEffectSource items set as property values. + window. Bindings relying on the value have to keep this in mind since the + value may change from GraphicsInfo.UnknownShadingLanguage to the actual + value after component initialization is complete. This is particularly + relevant for ShaderEffect items inside ShaderEffectSource items set as + property values. \since 5.8 \since QtQuick 2.8 @@ -140,11 +140,10 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object) expected to focus more on GraphicsInfo.OfflineCompilation, however. \note The value is only up-to-date once the item is associated with a - window and the window's scenegraph has initialized. Bindings relying on the - value have to keep this in mind since the value may change from \c 0 to the - actual bitmask after component initialization is complete. This is - particularly relevant for ShaderEffect items inside ShaderEffectSource - items set as property values. + window. Bindings relying on the value have to keep this in mind since the + value may change from \c 0 to the actual bitmask after component + initialization is complete. This is particularly relevant for ShaderEffect + items inside ShaderEffectSource items set as property values. \since 5.8 \since QtQuick 2.8 @@ -172,11 +171,10 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object) bytecode. \note The value is only up-to-date once the item is associated with a - window and the window's scenegraph has initialized. Bindings relying on the - value have to keep this in mind since the value may change from \c 0 to the - actual bitmask after component initialization is complete. This is - particularly relevant for ShaderEffect items inside ShaderEffectSource - items set as property values. + window. Bindings relying on the value have to keep this in mind since the + value may change from \c 0 to the actual bitmask after component + initialization is complete. This is particularly relevant for ShaderEffect + items inside ShaderEffectSource items set as property values. \since 5.8 \since QtQuick 2.8 @@ -240,9 +238,8 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object) void QQuickGraphicsInfo::updateInfo() { - const bool sgReady = m_window && m_window->isSceneGraphInitialized(); - - if (sgReady) { + // The queries via the RIF do not depend on isSceneGraphInitialized(), they only need a window. + if (m_window) { QSGRendererInterface *rif = m_window->rendererInterface(); if (rif) { GraphicsApi newAPI = GraphicsApi(rif->graphicsApi()); @@ -261,7 +258,7 @@ void QQuickGraphicsInfo::updateInfo() QSurfaceFormat format = QSurfaceFormat::defaultFormat(); #ifndef QT_NO_OPENGL - if (sgReady) { + if (m_window && m_window->isSceneGraphInitialized()) { QOpenGLContext *context = m_window->openglContext(); if (context) format = context->format(); diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 3468172484..8328dd513b 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -610,6 +610,8 @@ public: // recursive helper to let a visual parent mark its visual children void markObjects(QV4::ExecutionEngine *e); + + virtual void updatePolish() { } }; /* diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index 1e66971fdb..f65062ae38 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -556,6 +556,8 @@ QQuickOpenGLShaderEffect::QQuickOpenGLShaderEffect(QQuickShaderEffect *item, QOb , m_dirtyGeometry(true) , m_customVertexShader(false) , m_supportsAtlasTextures(false) + , m_vertNeedsUpdate(true) + , m_fragNeedsUpdate(true) { } @@ -573,8 +575,9 @@ void QQuickOpenGLShaderEffect::setFragmentShader(const QByteArray &code) m_dirtyProgram = true; m_dirtyParseLog = true; + m_fragNeedsUpdate = true; if (m_item->isComponentComplete()) - m_common.updateShader(m_item, Key::FragmentShader); + maybeUpdateShaders(); m_item->update(); if (m_status != QQuickShaderEffect::Uncompiled) { @@ -593,8 +596,9 @@ void QQuickOpenGLShaderEffect::setVertexShader(const QByteArray &code) m_dirtyParseLog = true; m_customVertexShader = true; + m_vertNeedsUpdate = true; if (m_item->isComponentComplete()) - m_common.updateShader(m_item, Key::VertexShader); + maybeUpdateShaders(); m_item->update(); if (m_status != QQuickShaderEffect::Uncompiled) { @@ -677,6 +681,8 @@ void QQuickOpenGLShaderEffect::setSupportsAtlasTextures(bool supports) QString QQuickOpenGLShaderEffect::parseLog() { + maybeUpdateShaders(true); + if (m_dirtyParseLog) { m_common.updateParseLog(m_mesh != 0); m_dirtyParseLog = false; @@ -862,10 +868,25 @@ QSGNode *QQuickOpenGLShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQuic return node; } -void QQuickOpenGLShaderEffect::handleComponentComplete() +void QQuickOpenGLShaderEffect::maybeUpdateShaders(bool force) { - m_common.updateShader(m_item, Key::VertexShader); - m_common.updateShader(m_item, Key::FragmentShader); + // Defer processing if a window is not yet associated with the item. This + // is because the actual scenegraph backend is not known so conditions + // based on GraphicsInfo.shaderType and similar evaluate to wrong results. + if (!m_item->window() && !force) { + m_item->polish(); + return; + } + + if (m_vertNeedsUpdate) { + m_vertNeedsUpdate = false; + m_common.updateShader(m_item, Key::VertexShader); + } + + if (m_fragNeedsUpdate) { + m_fragNeedsUpdate = false; + m_common.updateShader(m_item, Key::FragmentShader); + } } void QQuickOpenGLShaderEffect::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) diff --git a/src/quick/items/qquickopenglshadereffect_p.h b/src/quick/items/qquickopenglshadereffect_p.h index 0e54813443..bc60ba1e83 100644 --- a/src/quick/items/qquickopenglshadereffect_p.h +++ b/src/quick/items/qquickopenglshadereffect_p.h @@ -132,8 +132,8 @@ public: void handleEvent(QEvent *); void handleGeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); QSGNode *handleUpdatePaintNode(QSGNode *, QQuickItem::UpdatePaintNodeData *); - void handleComponentComplete(); void handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value); + void maybeUpdateShaders(bool force = false); private Q_SLOTS: void updateGeometry(); @@ -169,6 +169,8 @@ private: uint m_dirtyGeometry : 1; uint m_customVertexShader : 1; uint m_supportsAtlasTextures : 1; + uint m_vertNeedsUpdate : 1; + uint m_fragNeedsUpdate : 1; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index 4f1a9a28ec..f7c103a58f 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -39,6 +39,7 @@ #include #include +#include #ifndef QT_NO_OPENGL #include #endif @@ -382,10 +383,18 @@ QT_BEGIN_NAMESPACE \sa {Item Layers} */ +class QQuickShaderEffectPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickShaderEffect) + +public: + void updatePolish() override; +}; + QSGContextFactoryInterface::Flags qsg_backend_flags(); QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent) - : QQuickItem(parent), + : QQuickItem(*new QQuickShaderEffectPrivate, parent), #ifndef QT_NO_OPENGL m_glImpl(nullptr), #endif @@ -712,12 +721,12 @@ void QQuickShaderEffect::componentComplete() { #ifndef QT_NO_OPENGL if (m_glImpl) { - m_glImpl->handleComponentComplete(); + m_glImpl->maybeUpdateShaders(); QQuickItem::componentComplete(); return; } #endif - m_impl->handleComponentComplete(); + m_impl->maybeUpdateShaders(); QQuickItem::componentComplete(); } @@ -745,7 +754,19 @@ QString QQuickShaderEffect::parseLog() // for OpenGL-based autotests if (m_glImpl) return m_glImpl->parseLog(); #endif - return QString(); + return m_impl->parseLog(); +} + +void QQuickShaderEffectPrivate::updatePolish() +{ + Q_Q(QQuickShaderEffect); +#ifndef QT_NO_OPENGL + if (q->m_glImpl) { + q->m_glImpl->maybeUpdateShaders(); + return; + } +#endif + q->m_impl->maybeUpdateShaders(); } QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index 62d7e6fe5f..17b009039a 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -58,6 +58,7 @@ QT_BEGIN_NAMESPACE class QQuickOpenGLShaderEffect; class QQuickGenericShaderEffect; +class QQuickShaderEffectPrivate; class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem { @@ -134,6 +135,8 @@ private: QQuickOpenGLShaderEffect *m_glImpl; #endif QQuickGenericShaderEffect *m_impl; + + Q_DECLARE_PRIVATE(QQuickShaderEffect) }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 10b769b5b0..c3c886de35 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -293,7 +293,9 @@ void QQuickWindowPrivate::polishItems() int recursionSafeguard = INT_MAX; while (!itemsToPolish.isEmpty() && --recursionSafeguard > 0) { QQuickItem *item = itemsToPolish.takeLast(); - QQuickItemPrivate::get(item)->polishScheduled = false; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->polishScheduled = false; + itemPrivate->updatePolish(); item->updatePolish(); } @@ -4450,16 +4452,31 @@ qreal QQuickWindow::effectiveDevicePixelRatio() const } /*! - Returns the current renderer interface if there is one. Otherwise null is returned. + \return the current renderer interface. The value is always valid and is never null. + + \note This function can be called at any time after constructing the + QQuickWindow, even while isSceneGraphInitialized() is still false. However, + some renderer interface functions, in particular + QSGRendererInterface::getResource() will not be functional until the + scenegraph is up and running. Backend queries, like + QSGRendererInterface::graphicsApi() or QSGRendererInterface::shaderType(), + will always be functional on the other hand. - \sa QSGRenderNode, QSGRendererInterface, isSceneGraphInitialized() + \sa QSGRenderNode, QSGRendererInterface \since 5.8 */ QSGRendererInterface *QQuickWindow::rendererInterface() const { Q_D(const QQuickWindow); - return isSceneGraphInitialized() ? d->context->sceneGraphContext()->rendererInterface(d->context) : nullptr; + + // no context validity check - it is essential to be able to return a + // renderer interface instance before scenegraphInitialized() is emitted + // (depending on the backend, that can happen way too late for some of the + // rif use cases, like examining the graphics api or shading language in + // use) + + return d->context->sceneGraphContext()->rendererInterface(d->context); } /*! diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp index 149baab3a9..e818d35dd7 100644 --- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp @@ -54,12 +54,21 @@ QT_BEGIN_NAMESPACE necessary to query certain values, for instance the graphics device (e.g. the Direct3D or Vulkan device) that is used by the scenegraph. - \note QSGRendererInterface is only available after the scenegraph is - initialized. Additionally, there may be backend-specific limitations on - when the functions can be called. The only way that is guaranteed to - succeed is calling them when the rendering of a node (i.e. the preparation - of the command list for the next frame) is active. In practice this - typically means QSGRenderNode::render(). + QSGRendererInterface's functions have varying availability. API and + language queries, like graphicsApi() or shaderType() are always available, + meaning it is sufficient to construct a QQuickWindow or QQuickView, and the + graphics API or shading language in use can be queried right after via + QQuickWindow::rendererInterface(). This guarantees that utilities like the + GraphicsInfo QML type are able to report the correct values as early as + possible, without having conditional property values - depending on for + instance shaderType() - evaluate to unexpected values. + + Engine-specific accessors, like getResource(), are however available only + after the scenegraph is initialized. Additionally, there may be + backend-specific limitations on when such functions can be called. The only + way that is guaranteed to succeed is calling them when the rendering of a + node (i.e. the preparation of the command list for the next frame) is + active. In practice this typically means QSGRenderNode::render(). */ /*! @@ -112,10 +121,7 @@ QSGRendererInterface::~QSGRendererInterface() Returns the graphics API that is in use by the Qt Quick scenegraph. - \note This function can be called on any thread. However, the renderer - interface's lifetime may be tied to the render thread and therefore calling - this function from other threads during the process of application shutdown - or QQuickWindow closing is likely to become invalid. + \note This function can be called on any thread. */ /*! @@ -155,10 +161,7 @@ void *QSGRendererInterface::getResource(const char *resource) const \return the shading language supported by the Qt Quick backend the application is using. - \note This function can be called on any thread. However, the renderer - interface's lifetime may be tied to the render thread and therefore calling - this function from other threads during the process of application shutdown - or QQuickWindow closing is likely to become invalid. + \note This function can be called on any thread. \sa QtQuick::GraphicsInfo */ @@ -169,10 +172,7 @@ void *QSGRendererInterface::getResource(const char *resource) const \return a bitmask of the shader compilation approaches supported by the Qt Quick backend the application is using. - \note This function can be called on any thread. However, the renderer - interface's lifetime may be tied to the render thread and therefore calling - this function from other threads during the process of application shutdown - or QQuickWindow closing is likely to become invalid. + \note This function can be called on any thread. \sa QtQuick::GraphicsInfo */ @@ -182,10 +182,7 @@ void *QSGRendererInterface::getResource(const char *resource) const \return a bitmask of the supported ways of providing shader sources in ShaderEffect items. - \note This function can be called on any thread. However, the renderer - interface's lifetime may be tied to the render thread and therefore calling - this function from other threads during the process of application shutdown - or QQuickWindow closing is likely to become invalid. + \note This function can be called on any thread. \sa QtQuick::GraphicsInfo */ diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index fa77c2d6cc..83aceb79f4 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -318,7 +318,15 @@ QSize QSGContext::minimumFBOSize() const Returns a pointer to the (presumably) global renderer interface. \note This function may be called on the gui thread in order to get access - to QSGRendererInterface::graphicsApi(). + to QSGRendererInterface::graphicsApi() and other getters. + + \note it is expected that the simple queries (graphicsApi, shaderType, + etc.) are available regardless of the render context validity (i.e. + scenegraph status). This does not apply to engine-specific getters like + getResource(). In the end this means that this function must always return + a valid object in subclasses, even when renderContext->isValid() is false. + The typical pattern is to implement the QSGRendererInterface in the + QSGContext or QSGRenderContext subclass itself, whichever is more suitable. */ QSGRendererInterface *QSGContext::rendererInterface(QSGRenderContext *renderContext) { -- cgit v1.2.3 From ee595e9c0a4a24733195db22244cbf17b8514f72 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 28 Jun 2016 15:24:17 +0200 Subject: D3D12: Add support for file selectors in ShaderEffect Now that both OpenGL and D3D supports files, it is trivial to add support for file selectors. This means that one can add for example a shaders/wobble.frag with GLSL source code and a shaders/+hlsl/wobble.frag with D3D bytecode, while simply writing fragmentShader: "file:shaders/wobble.frag" in QML. The rest is automatic. Change-Id: Iaf71a6998bbd31050bc6c2b2f33b03d27c59fb6c Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp | 7 ++++++- src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp index 8c46e781e9..baf7571910 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp @@ -43,6 +43,7 @@ #include "qsgd3d12engine_p.h" #include #include +#include #include #include @@ -850,7 +851,11 @@ void QSGD3D12GuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type ty // For simplicity, assume that file = bytecode, string = HLSL. QUrl srcUrl(src); if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) { - const QString fn = QQmlFile::urlToLocalFileOrQrc(src); + if (!m_fileSelector) { + m_fileSelector = new QFileSelector(this); + m_fileSelector->setExtraSelectors(QStringList() << QStringLiteral("hlsl")); + } + const QString fn = m_fileSelector->select(QQmlFile::urlToLocalFileOrQrc(srcUrl)); QFile f(fn); if (!f.open(QIODevice::ReadOnly)) { qWarning("ShaderEffect: Failed to read %s", qPrintable(fn)); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h index c36ee1a6e6..ee17e59130 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h @@ -60,6 +60,7 @@ class QSGD3D12RenderContext; class QSGD3D12GuiThreadShaderEffectManager; class QSGD3D12ShaderEffectNode; class QSGD3D12Texture; +class QFileSelector; class QSGD3D12ShaderLinker { @@ -166,6 +167,7 @@ private: bool reflect(ShaderInfo *result); QString m_log; Status m_status = Uncompiled; + QFileSelector *m_fileSelector = nullptr; friend class QSGD3D12ShaderCompileTask; }; -- cgit v1.2.3 From b87fd4752afd6b559d69ec2e08b2e3df69373b9e Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 28 Jun 2016 15:42:53 +0200 Subject: D3D12: Add support for shader sources in files Now we support all combinations: source strings, HLSL source in files and bytecode in files. The type of file contents is detected automatically. Change-Id: Id6ab269df52463fb95a9a1ef2598ed5576a873d3 Reviewed-by: Andy Nichols --- .../scenegraph/d3d12/qsgd3d12rendercontext.cpp | 2 +- .../scenegraph/d3d12/qsgd3d12shadereffectnode.cpp | 36 ++++++++++++++-------- .../doc/src/concepts/visualcanvas/adaptations.qdoc | 15 +++++++-- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp index 7e62421380..84b12303d9 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp @@ -153,7 +153,7 @@ QSGRendererInterface::ShaderCompilationTypes QSGD3D12RenderContext::shaderCompil QSGRendererInterface::ShaderSourceTypes QSGD3D12RenderContext::shaderSourceType() const { - return ShaderSourceString | ShaderByteCode; + return ShaderSourceString | ShaderSourceFile | ShaderByteCode; } QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp index baf7571910..d459ec29ab 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp @@ -840,16 +840,19 @@ void QSGD3D12ShaderCompileTask::run() emit mgr->logAndStatusChanged(); } +static const int BYTECODE_MAGIC = 0x43425844; // 'DXBC' + void QSGD3D12GuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) { // The D3D12 backend's ShaderEffect implementation supports both HLSL - // source strings and bytecode in files as input. The latter is strongly - // recommended, but in order to make ShaderEffect users' (and - // qtgraphicaleffects') life easier, and since we link to d3dcompiler + // source strings and bytecode or source in files as input. Bytecode is + // strongly recommended, but in order to make ShaderEffect users' (and + // anything that stiches shader strings together dynamically, e.g. + // qtgraphicaleffects) life easier, and since we link to d3dcompiler // anyways, compiling from source is also supported. - // For simplicity, assume that file = bytecode, string = HLSL. - QUrl srcUrl(src); + QByteArray shaderSourceCode = src; + QUrl srcUrl(QString::fromUtf8(src)); if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) { if (!m_fileSelector) { m_fileSelector = new QFileSelector(this); @@ -862,16 +865,25 @@ void QSGD3D12GuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type ty emit shaderCodePrepared(false, typeHint, src, result); return; } - result->blob = f.readAll(); + QByteArray blob = f.readAll(); f.close(); - const bool ok = reflect(result); - m_status = ok ? Compiled : Error; - emit shaderCodePrepared(ok, typeHint, src, result); - emit logAndStatusChanged(); - return; + if (blob.size() > 4) { + const quint32 *p = reinterpret_cast(blob.constData()); + if (*p == BYTECODE_MAGIC) { + // already compiled D3D bytecode, skip straight to reflection + result->blob = blob; + const bool ok = reflect(result); + m_status = ok ? Compiled : Error; + emit shaderCodePrepared(ok, typeHint, src, result); + emit logAndStatusChanged(); + return; + } + // assume the file contained HLSL source code + shaderSourceCode = blob; + } } - QThreadPool::globalInstance()->start(new QSGD3D12ShaderCompileTask(this, typeHint, src, result)); + QThreadPool::globalInstance()->start(new QSGD3D12ShaderCompileTask(this, typeHint, shaderSourceCode, result)); } bool QSGD3D12GuiThreadShaderEffectManager::reflect(ShaderInfo *result) diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index d506e68d6a..fbbc947c4c 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -236,9 +236,18 @@ different than with OpenGL. With D3D12, these strings can either be an URL for a local file or a file in the resource system, or a HLSL source string. The former indicates that the file in question contains pre-compiled D3D shader bytecode generated by the -\c fxc tool. - -See the ShaderEffect documentation for a detailed description. +\c fxc tool, or, alternatively, HLSL source code. The type of the file is detected +automatically. This means that the D3D12 backend supports all options from +GraphicsInfo.shaderCompilationType and GraphicsInfo.shaderSourceType. + +Unlike OpenGL, there is a QFileSelector with the extra selector \c hlsl used +whenever opening a file. This allows easy creation of ShaderEffect items that +are functional across both backends, for example by placing the GLSL source +code into "shaders/effect.frag", the HLSL source code or - preferably - +pre-compiled bytecode into "shaders/+hlsl/effect.frag", while simply writing +\c{fragmentShader: "qrc:shaders/effect.frag"} in QML. + +See the ShaderEffect documentation for more details. \section2 Unsupported Features -- cgit v1.2.3 From f552d99f3d937ee9a8938673cd22246edbe70317 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 28 Jun 2016 16:33:42 +0200 Subject: Update ShaderEffect docs Change-Id: If20fa968fa596c82aa8eacbaedde9fa8ead5a8fd Reviewed-by: Andy Nichols --- src/quick/items/qquickshadereffect.cpp | 132 +++++++++++++++++++++++++++++---- 1 file changed, 117 insertions(+), 15 deletions(-) diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index f7c103a58f..57462219f9 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -169,7 +169,12 @@ QT_BEGIN_NAMESPACE \note Scene Graph textures have origin in the top-left corner rather than bottom-left which is common in OpenGL. - For information about the GLSL version being used, see \l QtQuick::OpenGLInfo. + For information about the GLSL version being used, see \l QtQuick::GraphicsInfo. + + Starting from Qt 5.8 ShaderEffect also supports reading the GLSL source + code from files. Whenever the fragmentShader or vertexShader property value + is a URL with the \c file or \c qrc schema, it is treated as a file + reference and the source code is read from the specified file. \section1 Direct3D and HLSL @@ -221,16 +226,17 @@ QT_BEGIN_NAMESPACE properties. Note that these are bitmasks, because some backends may support multiple approaches. - In case of Direct3D 12, both bytecode in files and HLSL source strings are - supported. If the vertexShader and fragmentShader properties form a valid - URL with the \c file or \c qrc schema, the bytecode is read from the - specified file. Otherwise, the string is treated as HLSL source code and is - compiled at runtime, assuming Shader Model 5.0 and an entry point of - \c{"main"}. This allows dynamically constructing shader strings. However, - whenever the shader source code is static, it is strongly recommended to - pre-compile to bytecode using the \c fxc tool and refer to these files from - QML. This will be a lot more efficient at runtime and allows catching - syntax errors in the shaders at compile time. + In case of Direct3D 12, all combinations are supported. If the vertexShader + and fragmentShader properties form a valid URL with the \c file or \c qrc + schema, the bytecode or HLSL source code is read from the specified file. + The type of the file contents is detected automatically. Otherwise, the + string is treated as HLSL source code and is compiled at runtime, assuming + Shader Model 5.0 and an entry point of \c{"main"}. This allows dynamically + constructing shader strings. However, whenever the shader source code is + static, it is strongly recommended to pre-compile to bytecode using the + \c fxc tool and refer to these files from QML. This will be a lot more + efficient at runtime and allows catching syntax errors in the shaders at + compile time. Unlike OpenGL, the Direct3D backend is able to perform runtime shader compilation on dedicated threads. This is managed transparently to the @@ -238,11 +244,14 @@ QT_BEGIN_NAMESPACE strings do not block the rendering or other parts of the application until the bytecode is ready. + Using files with bytecode is more flexible also when it comes to the entry + point name (it can be anything, not limited to \c main) and the shader + model (it can be something newer than 5.0, for instance 5.1). + \table 70% \row - \li \image declarative-shadereffectitem.png \li \qml - import QtQuick 2.8 + import QtQuick 2.0 Rectangle { width: 200; height: 100 @@ -282,6 +291,14 @@ QT_BEGIN_NAMESPACE in the constant buffer at offset 0, even though the pixel shader does not use the value. + If desired, the HLSL source code can be placed directly into the QML + source, similarly to how its done with GLSL. The only difference in this + case is the entry point name, which must be \c main when using inline + source strings. + + Alternatively, we could also have referred to a file containing the source + of the effect instead of the compiled bytecode version. + Some effects will want to provide a vertex shader as well. Below is a similar effect with both the vertex and fragment shader provided by the application. This time the colorization factor is provided by the QML item @@ -290,9 +307,8 @@ QT_BEGIN_NAMESPACE \table 70% \row - \li \image declarative-shadereffectitem.png \li \qml - import QtQuick 2.8 + import QtQuick 2.0 Rectangle { width: 200; height: 100 @@ -350,6 +366,92 @@ QT_BEGIN_NAMESPACE appropriate, meaning texture coordinates in HLSL version of the shaders will not need any adjustments compared to the equivalent GLSL code. + \section1 Cross-platform, Cross-API ShaderEffect Items + + Some applications will want to be functional with multiple accelerated + graphics backends. This has consequences for ShaderEffect items because the + supported shading languages may vary from backend to backend. + + There are two approaches to handle this: either write conditional property + values based on GraphicsInfo.shaderType, or use file selectors. In practice + the latter is strongly recommended as it leads to more concise and cleaner + application code. The only case it is not suitable is when the source + strings are constructed dynamically. + + \table 70% + \row + \li \qml + import QtQuick 2.8 // for GraphicsInfo + + Rectangle { + width: 200; height: 100 + Row { + Image { id: img; + sourceSize { width: 100; height: 100 } source: "qt-logo.png" } + ShaderEffect { + width: 100; height: 100 + property variant src: img + property variant color: Qt.vector3d(0.344, 0.5, 0.156) + fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ? + "varying highp vec2 coord; + uniform sampler2D src; + uniform lowp float qt_Opacity; + void main() { + lowp vec4 tex = texture2D(src, coord); + gl_FragColor = vec4(vec3(dot(tex.rgb, + vec3(0.344, 0.5, 0.156))), + tex.a) * qt_Opacity;" + : GraphicsInfo.shaderType === GraphicsInfo.HLSL ? + "cbuffer ConstantBuffer : register(b0) + { + float4x4 qt_Matrix; + float qt_Opacity; + }; + Texture2D src : register(t0); + SamplerState srcSampler : register(s0); + float4 ExamplePixelShader(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET + { + float4 tex = src.Sample(srcSampler, coord); + float3 col = dot(tex.rgb, float3(0.344, 0.5, 0.156)); + return float4(col, tex.a) * qt_Opacity; + }" + : "" + } + } + } + \endqml + \row + + \li This is the first approach based on GraphicsInfo. Note that the value + reported by GraphicsInfo is not up-to-date until the ShaderEffect item gets + associated with a QQuickWindow. Before that, the reported value is + GraphicsInfo.UnknownShadingLanguage. The alternative is to place the GLSL + source code and the compiled D3D bytecode into the files + \c{shaders/effect.frag} and \c{shaders/+hlsl/effect.frag}, include them in + the Qt resource system, and let the ShaderEffect's internal QFileSelector + do its job. The selector-less version is the GLSL source, while the \c hlsl + selector is used when running on the D3D12 backend. The file under + \c{+hlsl} can then contain either HLSL source code or compiled bytecode + from the \c fxc tool. + \qml + import QtQuick 2.8 // for GraphicsInfo + + Rectangle { + width: 200; height: 100 + Row { + Image { id: img; + sourceSize { width: 100; height: 100 } source: "qt-logo.png" } + ShaderEffect { + width: 100; height: 100 + property variant src: img + property variant color: Qt.vector3d(0.344, 0.5, 0.156) + fragmentShader: "qrc:shaders/effect.frag" // selects the correct variant automatically + } + } + } + \endqml + \endtable + \section1 ShaderEffect and Item Layers The ShaderEffect type can be combined with \l {Item Layers} {layered items}. -- cgit v1.2.3 From 15849d78b7cca848b87badcfee33db1db70b8b52 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 28 Jun 2016 17:12:09 +0200 Subject: Migrate shadereffects example to be cross-backend ...while demonstrating best practices like file selectors. Task-number: QTBUG-52914 Change-Id: Ibf7bca77b7b142f5ccb956e05efc5be974d53c79 Reviewed-by: Andy Nichols --- .../shadereffects/content/shaders/+hlsl/blur.frag | 18 ++++ .../content/shaders/+hlsl/colorize.frag | 17 ++++ .../shadereffects/content/shaders/+hlsl/genie.vert | 31 +++++++ .../content/shaders/+hlsl/outline.frag | 21 +++++ .../content/shaders/+hlsl/shadow.frag | 20 ++++ .../content/shaders/+hlsl/wobble.frag | 17 ++++ .../quick/shadereffects/content/shaders/blur.frag | 14 +++ .../shadereffects/content/shaders/colorize.frag | 12 +++ .../quick/shadereffects/content/shaders/genie.vert | 21 +++++ .../shadereffects/content/shaders/outline.frag | 16 ++++ .../shadereffects/content/shaders/shadow.frag | 14 +++ .../shadereffects/content/shaders/wobble.frag | 13 +++ .../quick/shadereffects/doc/src/shadereffects.qdoc | 15 +++ examples/quick/shadereffects/shadereffects.qml | 102 ++------------------- examples/quick/shadereffects/shadereffects.qrc | 12 +++ 15 files changed, 249 insertions(+), 94 deletions(-) create mode 100644 examples/quick/shadereffects/content/shaders/+hlsl/blur.frag create mode 100644 examples/quick/shadereffects/content/shaders/+hlsl/colorize.frag create mode 100644 examples/quick/shadereffects/content/shaders/+hlsl/genie.vert create mode 100644 examples/quick/shadereffects/content/shaders/+hlsl/outline.frag create mode 100644 examples/quick/shadereffects/content/shaders/+hlsl/shadow.frag create mode 100644 examples/quick/shadereffects/content/shaders/+hlsl/wobble.frag create mode 100644 examples/quick/shadereffects/content/shaders/blur.frag create mode 100644 examples/quick/shadereffects/content/shaders/colorize.frag create mode 100644 examples/quick/shadereffects/content/shaders/genie.vert create mode 100644 examples/quick/shadereffects/content/shaders/outline.frag create mode 100644 examples/quick/shadereffects/content/shaders/shadow.frag create mode 100644 examples/quick/shadereffects/content/shaders/wobble.frag diff --git a/examples/quick/shadereffects/content/shaders/+hlsl/blur.frag b/examples/quick/shadereffects/content/shaders/+hlsl/blur.frag new file mode 100644 index 0000000000..481a238d2a --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+hlsl/blur.frag @@ -0,0 +1,18 @@ +cbuffer ConstantBuffer : register(b0) +{ + float4x4 qt_Matrix; + float qt_Opacity; + float2 delta; +}; + +Texture2D source : register(t0); +SamplerState sourceSampler : register(s0); + +float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET +{ + return (0.0538 * source.Sample(sourceSampler, coord - 3.182 * delta) + + 0.3229 * source.Sample(sourceSampler, coord - 1.364 * delta) + + 0.2466 * source.Sample(sourceSampler, coord) + + 0.3229 * source.Sample(sourceSampler, coord + 1.364 * delta) + + 0.0538 * source.Sample(sourceSampler, coord + 3.182 * delta)) * qt_Opacity; +} diff --git a/examples/quick/shadereffects/content/shaders/+hlsl/colorize.frag b/examples/quick/shadereffects/content/shaders/+hlsl/colorize.frag new file mode 100644 index 0000000000..d6e65b6b10 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+hlsl/colorize.frag @@ -0,0 +1,17 @@ +cbuffer ConstantBuffer : register(b0) +{ + float4x4 qt_Matrix; + float qt_Opacity; + float4 tint; +}; + +Texture2D source : register(t0); +SamplerState sourceSampler : register(s0); + +float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET +{ + float4 c = source.Sample(sourceSampler, coord); + float lo = min(min(c.x, c.y), c.z); + float hi = max(max(c.x, c.y), c.z); + return float4(lerp(float3(lo, lo, lo), float3(hi, hi, hi), tint.xyz), c.w) * qt_Opacity; +} diff --git a/examples/quick/shadereffects/content/shaders/+hlsl/genie.vert b/examples/quick/shadereffects/content/shaders/+hlsl/genie.vert new file mode 100644 index 0000000000..40876e7996 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+hlsl/genie.vert @@ -0,0 +1,31 @@ +cbuffer ConstantBuffer : register(b0) +{ + float4x4 qt_Matrix; + float qt_Opacity; + float bend; + float minimize; + float side; + float width; + float height; +}; + +struct PSInput +{ + float4 position : SV_POSITION; + float2 coord : TEXCOORD0; +}; + +PSInput main(float4 position : POSITION, float2 coord : TEXCOORD0) +{ + PSInput result; + result.coord = coord; + + float4 pos = position; + pos.y = lerp(position.y, height, minimize); + float t = pos.y / height; + t = (3.0 - 2.0 * t) * t * t; + pos.x = lerp(position.x, side * width, t * bend); + result.position = mul(qt_Matrix, pos); + + return result; +} diff --git a/examples/quick/shadereffects/content/shaders/+hlsl/outline.frag b/examples/quick/shadereffects/content/shaders/+hlsl/outline.frag new file mode 100644 index 0000000000..b6e7e51f35 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+hlsl/outline.frag @@ -0,0 +1,21 @@ +cbuffer ConstantBuffer : register(b0) +{ + float4x4 qt_Matrix; + float qt_Opacity; + float2 delta; +}; + +Texture2D source : register(t0); +SamplerState sourceSampler : register(s0); + +float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET +{ + float4 tl = source.Sample(sourceSampler, coord - delta); + float4 tr = source.Sample(sourceSampler, coord + float2(delta.x, -delta.y)); + float4 bl = source.Sample(sourceSampler, coord - float2(delta.x, -delta.y)); + float4 br = source.Sample(sourceSampler, coord + delta); + float4 gx = (tl + bl) - (tr + br); + float4 gy = (tl + tr) - (bl + br); + return float4(0.0, 0.0, 0.0, + clamp(dot(sqrt(gx * gx + gy * gy), float4(1.0, 1.0, 1.0, 1.0)), 0.0, 1.0) * qt_Opacity); +} diff --git a/examples/quick/shadereffects/content/shaders/+hlsl/shadow.frag b/examples/quick/shadereffects/content/shaders/+hlsl/shadow.frag new file mode 100644 index 0000000000..a86a25e007 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+hlsl/shadow.frag @@ -0,0 +1,20 @@ +cbuffer ConstantBuffer : register(b0) +{ + float4x4 qt_Matrix; + float qt_Opacity; + float2 offset; + float2 delta; + float darkness; +}; + +Texture2D source : register(t0); +SamplerState sourceSampler : register(s0); +Texture2D shadow : register(t1); +SamplerState shadowSampler : register(s1); + +float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET +{ + float4 fg = source.Sample(sourceSampler, coord); + float4 bg = shadow.Sample(shadowSampler, coord + delta); + return (fg + float4(0.0, 0.0, 0.0, darkness * bg.a) * (1.0 - fg.a)) * qt_Opacity; +} diff --git a/examples/quick/shadereffects/content/shaders/+hlsl/wobble.frag b/examples/quick/shadereffects/content/shaders/+hlsl/wobble.frag new file mode 100644 index 0000000000..c28612a2fd --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+hlsl/wobble.frag @@ -0,0 +1,17 @@ +cbuffer ConstantBuffer : register(b0) +{ + float4x4 qt_Matrix; + float qt_Opacity; + float amplitude; + float frequency; + float time; +}; + +Texture2D source : register(t0); +SamplerState sourceSampler : register(s0); + +float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET +{ + float2 p = sin(time + frequency * coord); + return source.Sample(sourceSampler, coord + amplitude * float2(p.y, -p.x)) * qt_Opacity; +} diff --git a/examples/quick/shadereffects/content/shaders/blur.frag b/examples/quick/shadereffects/content/shaders/blur.frag new file mode 100644 index 0000000000..9173945eed --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/blur.frag @@ -0,0 +1,14 @@ +uniform lowp float qt_Opacity; +uniform sampler2D source; +uniform highp vec2 delta; + +varying highp vec2 qt_TexCoord0; + +void main() +{ + gl_FragColor =(0.0538 * texture2D(source, qt_TexCoord0 - 3.182 * delta) + + 0.3229 * texture2D(source, qt_TexCoord0 - 1.364 * delta) + + 0.2466 * texture2D(source, qt_TexCoord0) + + 0.3229 * texture2D(source, qt_TexCoord0 + 1.364 * delta) + + 0.0538 * texture2D(source, qt_TexCoord0 + 3.182 * delta)) * qt_Opacity; +} diff --git a/examples/quick/shadereffects/content/shaders/colorize.frag b/examples/quick/shadereffects/content/shaders/colorize.frag new file mode 100644 index 0000000000..1219ef2460 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/colorize.frag @@ -0,0 +1,12 @@ +uniform sampler2D source; +uniform lowp vec4 tint; +uniform lowp float qt_Opacity; + +varying highp vec2 qt_TexCoord0; + +void main() { + lowp vec4 c = texture2D(source, qt_TexCoord0); + lowp float lo = min(min(c.x, c.y), c.z); + lowp float hi = max(max(c.x, c.y), c.z); + gl_FragColor = qt_Opacity * vec4(mix(vec3(lo), vec3(hi), tint.xyz), c.w); +} diff --git a/examples/quick/shadereffects/content/shaders/genie.vert b/examples/quick/shadereffects/content/shaders/genie.vert new file mode 100644 index 0000000000..3ce371819f --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/genie.vert @@ -0,0 +1,21 @@ +attribute highp vec4 qt_Vertex; +attribute highp vec2 qt_MultiTexCoord0; + +uniform highp mat4 qt_Matrix; +uniform highp float bend; +uniform highp float minimize; +uniform highp float side; +uniform highp float width; +uniform highp float height; + +varying highp vec2 qt_TexCoord0; + +void main() { + qt_TexCoord0 = qt_MultiTexCoord0; + highp vec4 pos = qt_Vertex; + pos.y = mix(qt_Vertex.y, height, minimize); + highp float t = pos.y / height; + t = (3. - 2. * t) * t * t; + pos.x = mix(qt_Vertex.x, side * width, t * bend); + gl_Position = qt_Matrix * pos; +} diff --git a/examples/quick/shadereffects/content/shaders/outline.frag b/examples/quick/shadereffects/content/shaders/outline.frag new file mode 100644 index 0000000000..9b46719873 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/outline.frag @@ -0,0 +1,16 @@ +uniform sampler2D source; +uniform highp vec2 delta; +uniform highp float qt_Opacity; + +varying highp vec2 qt_TexCoord0; + +void main() { + lowp vec4 tl = texture2D(source, qt_TexCoord0 - delta); + lowp vec4 tr = texture2D(source, qt_TexCoord0 + vec2(delta.x, -delta.y)); + lowp vec4 bl = texture2D(source, qt_TexCoord0 - vec2(delta.x, -delta.y)); + lowp vec4 br = texture2D(source, qt_TexCoord0 + delta); + mediump vec4 gx = (tl + bl) - (tr + br); + mediump vec4 gy = (tl + tr) - (bl + br); + gl_FragColor.xyz = vec3(0.); + gl_FragColor.w = clamp(dot(sqrt(gx * gx + gy * gy), vec4(1.)), 0., 1.) * qt_Opacity; +} diff --git a/examples/quick/shadereffects/content/shaders/shadow.frag b/examples/quick/shadereffects/content/shaders/shadow.frag new file mode 100644 index 0000000000..8650ee4f4c --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/shadow.frag @@ -0,0 +1,14 @@ +uniform lowp float qt_Opacity; +uniform highp vec2 offset; +uniform sampler2D source; +uniform sampler2D shadow; +uniform highp float darkness; +uniform highp vec2 delta; + +varying highp vec2 qt_TexCoord0; + +void main() { + lowp vec4 fg = texture2D(source, qt_TexCoord0); + lowp vec4 bg = texture2D(shadow, qt_TexCoord0 + delta); + gl_FragColor = (fg + vec4(0., 0., 0., darkness * bg.a) * (1. - fg.a)) * qt_Opacity; +} diff --git a/examples/quick/shadereffects/content/shaders/wobble.frag b/examples/quick/shadereffects/content/shaders/wobble.frag new file mode 100644 index 0000000000..fedbb68254 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/wobble.frag @@ -0,0 +1,13 @@ +uniform lowp float qt_Opacity; +uniform highp float amplitude; +uniform highp float frequency; +uniform highp float time; +uniform sampler2D source; + +varying highp vec2 qt_TexCoord0; + +void main() +{ + highp vec2 p = sin(time + frequency * qt_TexCoord0); + gl_FragColor = texture2D(source, qt_TexCoord0 + amplitude * vec2(p.y, -p.x)) * qt_Opacity; +} diff --git a/examples/quick/shadereffects/doc/src/shadereffects.qdoc b/examples/quick/shadereffects/doc/src/shadereffects.qdoc index dc2a2681f5..7b1d68ebb5 100644 --- a/examples/quick/shadereffects/doc/src/shadereffects.qdoc +++ b/examples/quick/shadereffects/doc/src/shadereffects.qdoc @@ -51,6 +51,21 @@ shader: \snippet shadereffects/shadereffects.qml fragment + In order to support multiple graphics APIs, not just OpenGL, the shader + source is not embedded into QML. Instead, file selectors are used to select + the correct variant at runtime. Based on the Qt Quick backend in use, Qt + will automatically select either \c{shaders/wobble.frag} with the GLSL + source code or \c{shaders/+hlsl/wobble.frag} with the HLSL source code. + + \note For simplicity shader source code is used in all variants of the + files. However, with the Direct3D backend of Qt Quick pre-compiled shaders + are also supported. For example, try the following commands in the + \c{content/shaders/+hlsl} directory: \c{move wobble.frag wobble.frag.src} + followed by \c{fxc /E main /T ps_5_0 /Fo wobble.frag wobble.frag.src}. Now + \c wobble.frag contains Direct3D bytecode and that is what gets shipped + with the application instead of the shader source. Further changes are not + necessary, the application will function like before. + You can use any custom property on the ShaderEffect in your shader. This makes animated shader code very easy: \snippet shadereffects/shadereffects.qml properties diff --git a/examples/quick/shadereffects/shadereffects.qml b/examples/quick/shadereffects/shadereffects.qml index 926394ed0f..0c24a7bbf2 100644 --- a/examples/quick/shadereffects/shadereffects.qml +++ b/examples/quick/shadereffects/shadereffects.qml @@ -101,7 +101,7 @@ Rectangle { height: 140 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - font.pixelSize: 120 + font.pixelSize: 118 font.family: "Times" color: "blue" text: "Qt" @@ -128,17 +128,7 @@ Rectangle { property real time: 0 NumberAnimation on time { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 600 } //! [fragment] - fragmentShader: - "uniform lowp float qt_Opacity;" + - "uniform highp float amplitude;" + - "uniform highp float frequency;" + - "uniform highp float time;" + - "uniform sampler2D source;" + - "varying highp vec2 qt_TexCoord0;" + - "void main() {" + - " highp vec2 p = sin(time + frequency * qt_TexCoord0);" + - " gl_FragColor = texture2D(source, qt_TexCoord0 + amplitude * vec2(p.y, -p.x)) * qt_Opacity;" + - "}" + fragmentShader: "qrc:shadereffects/content/shaders/wobble.frag" //! [fragment] Slider { id: wobbleSlider @@ -163,32 +153,10 @@ Rectangle { height: theItem.height property variant delta: Qt.size(1.0 / width, 0.0) property variant source: theSource - fragmentShader: " - uniform lowp float qt_Opacity; - uniform sampler2D source; - uniform highp vec2 delta; - varying highp vec2 qt_TexCoord0; - void main() { - gl_FragColor =(0.0538 * texture2D(source, qt_TexCoord0 - 3.182 * delta) - + 0.3229 * texture2D(source, qt_TexCoord0 - 1.364 * delta) - + 0.2466 * texture2D(source, qt_TexCoord0) - + 0.3229 * texture2D(source, qt_TexCoord0 + 1.364 * delta) - + 0.0538 * texture2D(source, qt_TexCoord0 + 3.182 * delta)) * qt_Opacity; - }" + fragmentShader: "qrc:shadereffects/content/shaders/blur.frag" } } - fragmentShader: " - uniform lowp float qt_Opacity; - uniform sampler2D source; - uniform highp vec2 delta; - varying highp vec2 qt_TexCoord0; - void main() { - gl_FragColor =(0.0538 * texture2D(source, qt_TexCoord0 - 3.182 * delta) - + 0.3229 * texture2D(source, qt_TexCoord0 - 1.364 * delta) - + 0.2466 * texture2D(source, qt_TexCoord0) - + 0.3229 * texture2D(source, qt_TexCoord0 + 1.364 * delta) - + 0.0538 * texture2D(source, qt_TexCoord0 + 3.182 * delta)) * qt_Opacity; - }" + fragmentShader: "qrc:shadereffects/content/shaders/blur.frag" } } property real angle: 0 @@ -196,19 +164,7 @@ Rectangle { NumberAnimation on angle { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 6000 } property variant delta: Qt.size(offset.x / width, offset.y / height) property real darkness: shadowSlider.value - fragmentShader: " - uniform lowp float qt_Opacity; - uniform highp vec2 offset; - uniform sampler2D source; - uniform sampler2D shadow; - uniform highp float darkness; - uniform highp vec2 delta; - varying highp vec2 qt_TexCoord0; - void main() { - lowp vec4 fg = texture2D(source, qt_TexCoord0); - lowp vec4 bg = texture2D(shadow, qt_TexCoord0 + delta); - gl_FragColor = (fg + vec4(0., 0., 0., darkness * bg.a) * (1. - fg.a)) * qt_Opacity; - }" + fragmentShader: "qrc:shadereffects/content/shaders/shadow.frag" Slider { id: shadowSlider anchors.left: parent.left @@ -222,38 +178,14 @@ Rectangle { height: 160 property variant source: theSource property variant delta: Qt.size(0.5 / width, 0.5 / height) - fragmentShader: " - uniform sampler2D source; - uniform highp vec2 delta; - uniform highp float qt_Opacity; - varying highp vec2 qt_TexCoord0; - void main() { - lowp vec4 tl = texture2D(source, qt_TexCoord0 - delta); - lowp vec4 tr = texture2D(source, qt_TexCoord0 + vec2(delta.x, -delta.y)); - lowp vec4 bl = texture2D(source, qt_TexCoord0 - vec2(delta.x, -delta.y)); - lowp vec4 br = texture2D(source, qt_TexCoord0 + delta); - mediump vec4 gx = (tl + bl) - (tr + br); - mediump vec4 gy = (tl + tr) - (bl + br); - gl_FragColor.xyz = vec3(0.); - gl_FragColor.w = clamp(dot(sqrt(gx * gx + gy * gy), vec4(1.)), 0., 1.) * qt_Opacity; - }" + fragmentShader: "qrc:shadereffects/content/shaders/outline.frag" } ShaderEffect { width: 160 height: 160 property variant source: theSource property color tint: root.sliderToColor(colorizeSlider.value) - fragmentShader: " - uniform sampler2D source; - uniform lowp vec4 tint; - uniform lowp float qt_Opacity; - varying highp vec2 qt_TexCoord0; - void main() { - lowp vec4 c = texture2D(source, qt_TexCoord0); - lowp float lo = min(min(c.x, c.y), c.z); - lowp float hi = max(max(c.x, c.y), c.z); - gl_FragColor = qt_Opacity * vec4(mix(vec3(lo), vec3(hi), tint.xyz), c.w); - }" + fragmentShader: "qrc:shadereffects/content/shaders/colorize.frag" Slider { id: colorizeSlider anchors.left: parent.left @@ -288,25 +220,7 @@ Rectangle { //! [properties] //! [vertex] mesh: Qt.size(10, 10) - vertexShader: " - uniform highp mat4 qt_Matrix; - uniform highp float bend; - uniform highp float minimize; - uniform highp float side; - uniform highp float width; - uniform highp float height; - attribute highp vec4 qt_Vertex; - attribute highp vec2 qt_MultiTexCoord0; - varying highp vec2 qt_TexCoord0; - void main() { - qt_TexCoord0 = qt_MultiTexCoord0; - highp vec4 pos = qt_Vertex; - pos.y = mix(qt_Vertex.y, height, minimize); - highp float t = pos.y / height; - t = (3. - 2. * t) * t * t; - pos.x = mix(qt_Vertex.x, side * width, t * bend); - gl_Position = qt_Matrix * pos; - }" + vertexShader: "qrc:shadereffects/content/shaders/genie.vert" //! [vertex] Slider { id: genieSlider diff --git a/examples/quick/shadereffects/shadereffects.qrc b/examples/quick/shadereffects/shadereffects.qrc index ff296a0155..e66b98a6df 100644 --- a/examples/quick/shadereffects/shadereffects.qrc +++ b/examples/quick/shadereffects/shadereffects.qrc @@ -4,5 +4,17 @@ content/face-smile.png content/qt-logo.png content/Slider.qml + content/shaders/wobble.frag + content/shaders/+hlsl/wobble.frag + content/shaders/blur.frag + content/shaders/+hlsl/blur.frag + content/shaders/shadow.frag + content/shaders/+hlsl/shadow.frag + content/shaders/outline.frag + content/shaders/+hlsl/outline.frag + content/shaders/colorize.frag + content/shaders/+hlsl/colorize.frag + content/shaders/genie.vert + content/shaders/+hlsl/genie.vert -- cgit v1.2.3 From 240c2ef60e854575dced056d916f8a8922905e8f Mon Sep 17 00:00:00 2001 From: Andrea Bernabei Date: Tue, 31 May 2016 14:21:44 +0100 Subject: Flickable: fix minXExtent/minYExtent when content is smaller than view At the moment, defining leftMargin (or topMargin) and contentWidth (or contentHeight) so that "leftMargin+contentWidth < flickable.width" (or topMargin+contentHeight < flickable.height) leads to widthRatio (or heightRatio) having value != 1. The value should, however, be 1, as the content is completely visible inside the view, margins included. As a sideeffect, under the assumptions described above, it will now not be possible to scroll the leftMargin (or topMargin) out of screen, something which was possible (and it shouldn't have) before this fix. Task-number: QTBUG-53726 Change-Id: I22426c8038e90a2cfc7445914206eae0e781a3fb Reviewed-by: Robin Burchell Reviewed-by: Albert Astals Cid Reviewed-by: Shawn Rutledge --- src/quick/items/qquickflickable.cpp | 4 ++-- .../qquickflickable/data/ratios_smallContent.qml | 19 +++++++++++++++ .../quick/qquickflickable/tst_qquickflickable.cpp | 28 ++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/auto/quick/qquickflickable/data/ratios_smallContent.qml diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 32d445dbc5..b0980cd2c1 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1580,13 +1580,13 @@ qreal QQuickFlickable::minXExtent() const qreal QQuickFlickable::maxXExtent() const { Q_D(const QQuickFlickable); - return qMin(0, width() - vWidth() - d->hData.endMargin); + return qMin(minXExtent(), width() - vWidth() - d->hData.endMargin); } /* returns -ve */ qreal QQuickFlickable::maxYExtent() const { Q_D(const QQuickFlickable); - return qMin(0, height() - vHeight() - d->vData.endMargin); + return qMin(minYExtent(), height() - vHeight() - d->vData.endMargin); } void QQuickFlickable::componentComplete() diff --git a/tests/auto/quick/qquickflickable/data/ratios_smallContent.qml b/tests/auto/quick/qquickflickable/data/ratios_smallContent.qml new file mode 100644 index 0000000000..07bad683ee --- /dev/null +++ b/tests/auto/quick/qquickflickable/data/ratios_smallContent.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 + +Flickable { + property double heightRatioIs: visibleArea.heightRatio + property double widthRatioIs: visibleArea.widthRatio + + width: 200 + height: 200 + contentWidth: item.width + contentHeight: item.height + topMargin: 20 + leftMargin: 40 + + Item { + id: item + width: 100 + height: 100 + } +} diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index dc7171746c..2e134ff5ad 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -95,6 +95,7 @@ private slots: void movementFromProgrammaticFlick(); void cleanup(); void contentSize(); + void ratios_smallContent(); private: void flickWithTouch(QQuickWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to); @@ -1817,6 +1818,33 @@ void tst_qquickflickable::contentSize() QCOMPARE(chspy.count(), 1); } +// QTBUG-53726 +void tst_qquickflickable::ratios_smallContent() +{ + QScopedPointer window(new QQuickView); + window->setSource(testFileUrl("ratios_smallContent.qml")); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + window->setTitle(QTest::currentTestFunction()); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + QQuickItem *root = window->rootObject(); + QVERIFY(root); + QQuickFlickable *obj = qobject_cast(root); + QVERIFY(obj != 0); + + //doublecheck the item, as specified by contentWidth/Height, fits in the view + //use tryCompare to allow a bit of stabilization in component's properties + QTRY_COMPARE(obj->leftMargin() + obj->contentWidth() + obj->rightMargin() <= obj->width(), true); + QTRY_COMPARE(obj->topMargin() + obj->contentHeight() + obj->bottomMargin() <= obj->height(), true); + + //the whole item fits in the flickable, heightRatio should be 1 + QCOMPARE(obj->property("heightRatioIs").toDouble(), 1.); + QCOMPARE(obj->property("widthRatioIs").toDouble(), 1.); +} + + QTEST_MAIN(tst_qquickflickable) #include "tst_qquickflickable.moc" -- cgit v1.2.3 From 7019f7c3237d1894ad2e3ccc4ca9c366a9bba2e4 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 29 Jun 2016 14:25:33 +0200 Subject: D3D12: Avoid multiple invalidated signals Calling invalidate() multiple times for any reason should only result in doing cleanup and emitting signals once. The somewhat weird manual pending flag is also removed now. Change-Id: I46eca63a300fe9acb4ebd0bd9b601d8583dced1c Reviewed-by: Andy Nichols --- .../scenegraph/d3d12/qsgd3d12rendercontext.cpp | 25 +++++++++++++--------- .../scenegraph/d3d12/qsgd3d12rendercontext_p.h | 6 ++---- .../scenegraph/d3d12/qsgd3d12renderloop.cpp | 6 +----- .../d3d12/qsgd3d12threadedrenderloop.cpp | 8 ++----- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp index 84b12303d9..1a63117ec0 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp @@ -64,8 +64,22 @@ bool QSGD3D12RenderContext::isValid() const return m_engine != nullptr; } +void QSGD3D12RenderContext::initialize(void *) +{ + if (m_initialized) + return; + + m_initialized = true; + emit initialized(); +} + void QSGD3D12RenderContext::invalidate() { + if (!m_initialized) + return; + + m_initialized = false; + if (Q_UNLIKELY(debug_render())) qDebug("rendercontext invalidate engine %p, %d/%d/%d", m_engine, m_texturesToDelete.count(), m_textures.count(), m_fontEnginesToClean.count()); @@ -114,16 +128,7 @@ void QSGD3D12RenderContext::setEngine(QSGD3D12Engine *engine) m_engine = engine; if (m_engine) - emit initialized(); -} - -void QSGD3D12RenderContext::ensureInitializedEmitted() -{ - if (!m_pendingInitialized) - return; - - m_pendingInitialized = false; - emit initialized(); + initialize(nullptr); } QSGRendererInterface::GraphicsApi QSGD3D12RenderContext::graphicsApi() const diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h index a1029b019e..5cd0b52f4b 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h @@ -63,6 +63,7 @@ class QSGD3D12RenderContext : public QSGRenderContext, public QSGRendererInterfa public: QSGD3D12RenderContext(QSGContext *ctx); bool isValid() const override; + void initialize(void *context) override; void invalidate() override; void renderNextFrame(QSGRenderer *renderer, uint fbo) override; QSGTexture *createTexture(const QImage &image, uint flags) const override; @@ -71,9 +72,6 @@ public: void setEngine(QSGD3D12Engine *engine); QSGD3D12Engine *engine() { return m_engine; } - void ensureInitializedEmitted(); - void setInitializedPending() { m_pendingInitialized = true; } - // QSGRendererInterface GraphicsApi graphicsApi() const override; void *getResource(Resource resource) const override; @@ -83,7 +81,7 @@ public: private: QSGD3D12Engine *m_engine = nullptr; - bool m_pendingInitialized = false; + bool m_initialized = false; }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 288a7ce4ad..bb7c1d5437 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -276,9 +276,6 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) data.rc->invalidate(); data.engine->releaseResources(); needsWindow = true; - // Be nice and emit the rendercontext's initialized() later on so that - // QQuickWindow::sceneGraphInitialized() behaves in a manner similar to GL. - data.rc->setInitializedPending(); } if (needsWindow) { // Must only ever get here when there is no window or releaseResources() has been called. @@ -296,10 +293,9 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) wd->cleanupNodesOnShutdown(); QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); data.rc->invalidate(); - data.rc->setInitializedPending(); } - data.rc->ensureInitializedEmitted(); + data.rc->initialize(nullptr); wd->syncSceneGraph(); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp index 9f32a6d8bf..26efa171f5 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp @@ -327,10 +327,6 @@ bool QSGD3D12RenderThread::event(QEvent *e) rc->invalidate(); engine->releaseResources(); needsWindow = true; - // Be nice and emit the rendercontext's initialized() later on at - // some point so that QQuickWindow::sceneGraphInitialized() behaves - // in a manner similar to GL. - rc->setInitializedPending(); } if (needsWindow) { // Must only ever get here when there is no window or releaseResources() has been called. @@ -412,7 +408,7 @@ bool QSGD3D12RenderThread::event(QEvent *e) // However, our hands are tied by the existing, synchronous APIs of // QQuickWindow and such. QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); - rc->ensureInitializedEmitted(); + rc->initialize(nullptr); wd->syncSceneGraph(); wd->renderSceneGraph(wme->window->size()); *wme->image = engine->executeAndWaitReadbackRenderTarget(); @@ -547,7 +543,7 @@ void QSGD3D12RenderThread::sync(bool inExpose) if (wd->renderer) wd->renderer->clearChangedFlag(); - rc->ensureInitializedEmitted(); + rc->initialize(nullptr); wd->syncSceneGraph(); if (!hadRenderer && wd->renderer) { -- cgit v1.2.3 From 6b8070d5a9598caa1c42753bb9034eb6137e7237 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 29 Jun 2016 14:53:31 +0200 Subject: Make QSGRenderContext::invalidate a no-op Calling invalidate() from the destructor is a bad idea since what we get is the QSGRenderContext implementation, not the one from the more-derived class. (it could have been intentional as well - it is confusing in any case and unnecessary). There is no adaptation relying in this anyways - invalidate() is always invoked manually, typically from windowDestroyed. This is very good since it avoids the at first less-than-obvious trouble with emitting invalidated() from the rendercontext dtor. (that can in turn can trigger random amount of code potentially calling back into the rendercontext and rely on virtuals which are not functional anymore due to the vtable not there for the functions in the more-derived class) Change-Id: I44d78c5a819230f7006d33d4341eff45d8f77c88 Reviewed-by: Andy Nichols --- src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp | 3 ++- src/quick/scenegraph/qsgcontext.cpp | 3 --- src/quick/scenegraph/qsgdefaultrendercontext.cpp | 3 ++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp index 921071e32e..d9a298f855 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp @@ -134,7 +134,8 @@ void QSGSoftwareRenderContext::initializeIfNeeded() void QSGSoftwareRenderContext::invalidate() { - QSGRenderContext::invalidate(); + m_sg->renderContextInvalidated(this); + emit invalidated(); } QSGTexture *QSGSoftwareRenderContext::createTexture(const QImage &image, uint flags) const diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 0009de8c67..688fc7db08 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -343,7 +343,6 @@ QSGRenderContext::QSGRenderContext(QSGContext *context) QSGRenderContext::~QSGRenderContext() { - invalidate(); } void QSGRenderContext::initialize(void *context) @@ -353,8 +352,6 @@ void QSGRenderContext::initialize(void *context) void QSGRenderContext::invalidate() { - m_sg->renderContextInvalidated(this); - emit invalidated(); } void QSGRenderContext::endSync() diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 870c0488c3..4fcc81fb18 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -163,7 +163,8 @@ void QSGDefaultRenderContext::invalidate() m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant()); m_gl = 0; - QSGRenderContext::invalidate(); + m_sg->renderContextInvalidated(this); + emit invalidated(); } static QBasicMutex qsg_framerender_mutex; -- cgit v1.2.3 From 589f92b188ff61a3b965d5d5c67d89333425cc97 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 29 Jun 2016 16:09:56 +0200 Subject: D3D12: Fix grabbing with the new gui thread render loop Grabbing was not completely ported from the separate render thread based version, and so some of the grab tests in tst_qquickwindow did not pass. This is now corrected. Change-Id: I830d9b439082cf8ebc1bfe75ce756ef5dd7c54c0 Reviewed-by: Andy Nichols --- .../scenegraph/d3d12/qsgd3d12renderloop.cpp | 54 ++++++++++++++-------- .../scenegraph/d3d12/qsgd3d12renderloop_p.h | 2 + 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index bb7c1d5437..4684e7243f 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -128,39 +128,49 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) delete engine; } -void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window) +void QSGD3D12RenderLoop::exposeWindow(QQuickWindow *window) { + WindowData data; + data.engine = new QSGD3D12Engine; + data.rc = static_cast(QQuickWindowPrivate::get(window)->context); + data.rc->setEngine(data.engine); + m_windows[window] = data; + + const int samples = window->format().samples(); + const qreal dpr = window->effectiveDevicePixelRatio(); + if (Q_UNLIKELY(debug_loop())) - qDebug() << "exposure changed" << window; + qDebug() << "initializing D3D12 engine" << window << window->size() << dpr << samples; - if (window->isExposed()) { - if (!m_windows.contains(window)) { - WindowData data; - data.engine = new QSGD3D12Engine; - data.rc = static_cast(QQuickWindowPrivate::get(window)->context); - data.rc->setEngine(data.engine); - m_windows[window] = data; + data.engine->attachToWindow(window->winId(), window->size(), dpr, samples); +} - const int samples = window->format().samples(); - const qreal dpr = window->effectiveDevicePixelRatio(); +void QSGD3D12RenderLoop::obscureWindow(QQuickWindow *window) +{ + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->fireAboutToStop(); +} - if (debug_loop()) - qDebug() << "initializing D3D12 engine" << window << window->size() << dpr << samples; +void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "exposure changed" << window; - data.engine->attachToWindow(window->winId(), window->size(), dpr, samples); - } + if (window->isExposed()) { + if (!m_windows.contains(window)) + exposeWindow(window); m_windows[window].updatePending = true; renderWindow(window); } else if (m_windows.contains(window)) { - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); - wd->fireAboutToStop(); + obscureWindow(window); } } QImage QSGD3D12RenderLoop::grab(QQuickWindow *window) { - if (!m_windows.contains(window)) - return QImage(); + const bool tempExpose = !m_windows.contains(window); + if (tempExpose) + exposeWindow(window); m_windows[window].grabOnly = true; @@ -168,6 +178,10 @@ QImage QSGD3D12RenderLoop::grab(QQuickWindow *window) QImage grabbed = m_grabContent; m_grabContent = QImage(); + + if (tempExpose) + obscureWindow(window); + return grabbed; } @@ -234,7 +248,7 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) qDebug() << "renderWindow" << window; QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); - if (!wd->isRenderable() || !m_windows.contains(window)) + if (!m_windows.contains(window) || !window->geometry().isValid()) return; WindowData &data(m_windows[window]); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h index fbd9d66d4a..3cd0fa0043 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h @@ -91,6 +91,8 @@ public: int flags() const override; private: + void exposeWindow(QQuickWindow *window); + void obscureWindow(QQuickWindow *window); void renderWindow(QQuickWindow *window); QSGD3D12Context *sg; -- cgit v1.2.3 From 023170b8276659118fd28d787597d749f80501e9 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 29 Jun 2016 16:39:48 +0200 Subject: D3D12: Fix expose handling to avoid unnecessary rendering attempts when closing windows containing certain Quick Controls (1). Basically this introduces a condition that is there in the threaded loop's polishAndSync but was missing from renderWindow. When the window is not "exposed" either normally or via grab(), it should just return. Change-Id: I42602e33ba144a1c56586a4b92fa088e85099d0d Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp | 21 +++++++++++++++++---- src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h | 1 + 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 4684e7243f..d845b65c28 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -84,13 +84,17 @@ void QSGD3D12RenderLoop::hide(QQuickWindow *window) void QSGD3D12RenderLoop::resize(QQuickWindow *window) { - if (!window->isExposed() || window->size().isEmpty()) + if (!m_windows.contains(window) || window->size().isEmpty()) return; if (Q_UNLIKELY(debug_loop())) qDebug() << "resize" << window; - WindowData &data(m_windows[window]); + const WindowData &data(m_windows[window]); + + if (!data.exposed) + return; + if (data.engine) data.engine->setWindowSize(window->size(), window->effectiveDevicePixelRatio()); } @@ -131,6 +135,7 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) void QSGD3D12RenderLoop::exposeWindow(QQuickWindow *window) { WindowData data; + data.exposed = true; data.engine = new QSGD3D12Engine; data.rc = static_cast(QQuickWindowPrivate::get(window)->context); data.rc->setEngine(data.engine); @@ -147,6 +152,7 @@ void QSGD3D12RenderLoop::exposeWindow(QQuickWindow *window) void QSGD3D12RenderLoop::obscureWindow(QQuickWindow *window) { + m_windows[window].exposed = false; QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); wd->fireAboutToStop(); } @@ -154,12 +160,14 @@ void QSGD3D12RenderLoop::obscureWindow(QQuickWindow *window) void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window) { if (Q_UNLIKELY(debug_loop())) - qDebug() << "exposure changed" << window; + qDebug() << "exposure changed" << window << window->isExposed(); if (window->isExposed()) { if (!m_windows.contains(window)) exposeWindow(window); - m_windows[window].updatePending = true; + WindowData &data(m_windows[window]); + data.exposed = true; + data.updatePending = true; renderWindow(window); } else if (m_windows.contains(window)) { obscureWindow(window); @@ -252,6 +260,11 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) return; WindowData &data(m_windows[window]); + if (!data.exposed) { // not the same as window->isExposed(), when grabbing invisible windows for instance + if (Q_UNLIKELY(debug_loop())) + qDebug("renderWindow - not exposed, abort"); + return; + } const bool needsSwap = data.updatePending; data.updatePending = false; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h index 3cd0fa0043..2f2ebbef33 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h @@ -102,6 +102,7 @@ private: QSGD3D12Engine *engine = nullptr; bool updatePending = false; bool grabOnly = false; + bool exposed = false; }; QHash m_windows; -- cgit v1.2.3 From da839643397dda062cf7371e01fc1c990b7cf7c3 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 29 Jun 2016 17:53:13 +0200 Subject: Add a core profile file selector to OpenGL ShaderEffect Similarly to what we do with "hlsl" in the D3D12 backend, we can use "glslcore" to provide a file-based alternative to the GraphcsInfo or OpenGLInfo-based conditions in the fragmentShader and vertexShader properties. This is particularly useful in a few places inside Qt, for example Quick Controls, that have to cater to all possiblities. Change-Id: I5d89e7b1534afbc323a663869bab7796bd1a337d Reviewed-by: Andy Nichols --- src/quick/items/qquickopenglshadereffect.cpp | 12 +++++++++++- src/quick/items/qquickopenglshadereffect_p.h | 4 +++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index f65062ae38..b77a7830ff 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -53,6 +53,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -325,7 +326,16 @@ void QQuickOpenGLShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderT // A qrc or file URL means the shader source is to be read from the specified file. QUrl srcUrl(QString::fromUtf8(source.sourceCode[shaderType])); if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) { - const QString fn = QQmlFile::urlToLocalFileOrQrc(srcUrl); + if (!fileSelector) { + fileSelector = new QFileSelector(item); + // There may not be an OpenGL context accessible here. So rely on + // the window's requestedFormat(). + if (item->window() + && item->window()->requestedFormat().profile() == QSurfaceFormat::CoreProfile) { + fileSelector->setExtraSelectors(QStringList() << QStringLiteral("glslcore")); + } + } + const QString fn = fileSelector->select(QQmlFile::urlToLocalFileOrQrc(srcUrl)); QFile f(fn); if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { source.sourceCode[shaderType] = f.readAll(); diff --git a/src/quick/items/qquickopenglshadereffect_p.h b/src/quick/items/qquickopenglshadereffect_p.h index bc60ba1e83..062eedd744 100644 --- a/src/quick/items/qquickopenglshadereffect_p.h +++ b/src/quick/items/qquickopenglshadereffect_p.h @@ -66,6 +66,7 @@ QT_BEGIN_NAMESPACE class QSGContext; class QSignalMapper; +class QFileSelector; class QQuickOpenGLCustomMaterialShader; // Common class for QQuickOpenGLShaderEffect and QQuickCustomParticle. @@ -74,7 +75,7 @@ struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon typedef QQuickOpenGLShaderEffectMaterialKey Key; typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData; - QQuickOpenGLShaderEffectCommon(QObject *host) : host(host) { } + QQuickOpenGLShaderEffectCommon(QObject *host) : host(host), fileSelector(nullptr) { } ~QQuickOpenGLShaderEffectCommon(); void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); void connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); @@ -95,6 +96,7 @@ struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon QVector uniformData[Key::ShaderTypeCount]; QVector signalMappers[Key::ShaderTypeCount]; QString parseLog; + QFileSelector *fileSelector; }; -- cgit v1.2.3 From c3561cdb36e538132ea07491ab6418294117b8f2 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 30 Jun 2016 14:27:42 +0200 Subject: D3D12: Advance anims in sync with vsync in the RL Make the default render loop behave like the 'windows' one does with OpenGL: advance animations manually after each render step. This provides much better results than the previous approach (i.e. 'basic') where the animations became quite jerky with heavier workloads. Change-Id: Ic933e136479d2a04036af15212669027ef2408c3 Reviewed-by: Andy Nichols --- .../scenegraph/d3d12/qsgd3d12renderloop.cpp | 212 ++++++++++++++++----- .../scenegraph/d3d12/qsgd3d12renderloop_p.h | 17 +- 2 files changed, 185 insertions(+), 44 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index d845b65c28..551133e7bb 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -44,7 +44,10 @@ #include "qsgd3d12shadereffectnode_p.h" #include #include +#include #include +#include +#include QT_BEGIN_NAMESPACE @@ -57,12 +60,29 @@ QT_BEGIN_NAMESPACE DECLARE_DEBUG_VAR(loop) DECLARE_DEBUG_VAR(time) + +// This render loop operates on the gui (main) thread. +// Conceptually it matches the OpenGL 'windows' render loop. + +static inline int qsgrl_animation_interval() +{ + const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0; + return refreshRate < 1 ? 16 : int(1000 / refreshRate); +} + QSGD3D12RenderLoop::QSGD3D12RenderLoop() { if (Q_UNLIKELY(debug_loop())) qDebug("new d3d12 render loop"); sg = new QSGD3D12Context; + + m_anims = sg->createAnimationDriver(this); + connect(m_anims, &QAnimationDriver::started, this, &QSGD3D12RenderLoop::onAnimationStarted); + connect(m_anims, &QAnimationDriver::stopped, this, &QSGD3D12RenderLoop::onAnimationStopped); + m_anims->install(); + + m_vsyncDelta = qsgrl_animation_interval(); } QSGD3D12RenderLoop::~QSGD3D12RenderLoop() @@ -130,6 +150,8 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) delete rc; delete engine; + + delete wd->animationController; } void QSGD3D12RenderLoop::exposeWindow(QQuickWindow *window) @@ -165,12 +187,31 @@ void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window) if (window->isExposed()) { if (!m_windows.contains(window)) exposeWindow(window); + + // Stop non-visual animation timer as we now have a window rendering. + if (m_animationTimer && somethingVisible()) { + killTimer(m_animationTimer); + m_animationTimer = 0; + } + // If we have a pending timer and we get an expose, we need to stop it. + // Otherwise we get two frames and two animation ticks in the same time interval. + if (m_updateTimer) { + killTimer(m_updateTimer); + m_updateTimer = 0; + } + WindowData &data(m_windows[window]); data.exposed = true; data.updatePending = true; - renderWindow(window); + + render(); + } else if (m_windows.contains(window)) { obscureWindow(window); + + // Potentially start the non-visual animation timer if nobody is rendering. + if (m_anims->isRunning() && !somethingVisible() && !m_animationTimer) + m_animationTimer = startTimer(m_vsyncDelta); } } @@ -193,32 +234,41 @@ QImage QSGD3D12RenderLoop::grab(QQuickWindow *window) return grabbed; } -void QSGD3D12RenderLoop::update(QQuickWindow *window) +bool QSGD3D12RenderLoop::somethingVisible() const { - if (!m_windows.contains(window)) - return; + for (auto it = m_windows.constBegin(), itEnd = m_windows.constEnd(); it != itEnd; ++it) { + if (it.key()->isVisible() && it.key()->isExposed()) + return true; + } + return false; +} - m_windows[window].updatePending = true; - window->requestUpdate(); +void QSGD3D12RenderLoop::maybePostUpdateTimer() +{ + if (!m_updateTimer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("starting update timer"); + m_updateTimer = startTimer(m_vsyncDelta / 3); + } } -void QSGD3D12RenderLoop::maybeUpdate(QQuickWindow *window) +void QSGD3D12RenderLoop::update(QQuickWindow *window) { - update(window); + maybeUpdate(window); } -// called in response to window->requestUpdate() -void QSGD3D12RenderLoop::handleUpdateRequest(QQuickWindow *window) +void QSGD3D12RenderLoop::maybeUpdate(QQuickWindow *window) { - if (Q_UNLIKELY(debug_loop())) - qDebug() << "handleUpdateRequest" << window; + if (!m_windows.contains(window) || !somethingVisible()) + return; - renderWindow(window); + m_windows[window].updatePending = true; + maybePostUpdateTimer(); } QAnimationDriver *QSGD3D12RenderLoop::animationDriver() const { - return nullptr; + return m_anims; } QSGContext *QSGD3D12RenderLoop::sceneGraphContext() const @@ -250,6 +300,92 @@ QSurface::SurfaceType QSGD3D12RenderLoop::windowSurfaceType() const return QSurface::OpenGLSurface; } +bool QSGD3D12RenderLoop::interleaveIncubation() const +{ + return m_anims->isRunning() && somethingVisible(); +} + +void QSGD3D12RenderLoop::onAnimationStarted() +{ + if (!somethingVisible()) { + if (!m_animationTimer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("starting non-visual animation timer"); + m_animationTimer = startTimer(m_vsyncDelta); + } + } else { + maybePostUpdateTimer(); + } +} + +void QSGD3D12RenderLoop::onAnimationStopped() +{ + if (m_animationTimer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("stopping non-visual animation timer"); + killTimer(m_animationTimer); + m_animationTimer = 0; + } +} + +bool QSGD3D12RenderLoop::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::Timer: + { + QTimerEvent *te = static_cast(event); + if (te->timerId() == m_animationTimer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("animation tick while no windows exposed"); + m_anims->advance(); + } else if (te->timerId() == m_updateTimer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("update timeout - rendering"); + killTimer(m_updateTimer); + m_updateTimer = 0; + render(); + } + return true; + } + default: + break; + } + + return QObject::event(event); +} + +void QSGD3D12RenderLoop::render() +{ + bool rendered = false; + for (auto it = m_windows.begin(), itEnd = m_windows.end(); it != itEnd; ++it) { + if (it->updatePending) { + it->updatePending = false; + renderWindow(it.key()); + rendered = true; + } + } + + if (!rendered) { + if (Q_UNLIKELY(debug_loop())) + qDebug("render - no changes, sleep"); + QThread::msleep(m_vsyncDelta); + } + + if (m_anims->isRunning()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("render - advancing animations"); + + m_anims->advance(); + + // It is not given that animations triggered another maybeUpdate() + // and thus another render pass, so to keep things running, + // make sure there is another frame pending. + maybePostUpdateTimer(); + + emit timeToIncubate(); + } +} + void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) { if (Q_UNLIKELY(debug_loop())) @@ -266,14 +402,8 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) return; } - const bool needsSwap = data.updatePending; - data.updatePending = false; - - if (!data.grabOnly) { + if (!data.grabOnly) wd->flushFrameSynchronousEvents(); - if (!m_windows.contains(window)) - return; - } QElapsedTimer renderTimer; qint64 renderTime = 0, syncTime = 0, polishTime = 0; @@ -336,28 +466,28 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) renderTime = renderTimer.nsecsElapsed(); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); - if (data.grabOnly) { + if (!data.grabOnly) { + // The engine is able to have multiple frames in flight. This in effect is + // similar to BufferQueueingOpenGL. Provide an env var to force the + // traditional blocking swap behavior, just in case. + static bool blockOnEachFrame = qEnvironmentVariableIntValue("QT_D3D_BLOCKING_PRESENT") != 0; + + if (window->isVisible()) { + data.engine->present(); + if (blockOnEachFrame) + data.engine->waitGPU(); + // The concept of "frame swaps" is quite misleading by default, when + // blockOnEachFrame is not used, but emit it for compatibility. + wd->fireFrameSwapped(); + } else { + if (blockOnEachFrame) + data.engine->waitGPU(); + } + } else { m_grabContent = data.engine->executeAndWaitReadbackRenderTarget(); data.grabOnly = false; } - // The engine is able to have multiple frames in flight. This in effect is - // similar to BufferQueueingOpenGL. Provide an env var to force the - // traditional blocking swap behavior, just in case. - static bool blockOnEachFrame = qEnvironmentVariableIntValue("QT_D3D_BLOCKING_PRESENT") != 0; - - if (needsSwap && window->isVisible()) { - data.engine->present(); - if (blockOnEachFrame) - data.engine->waitGPU(); - // The concept of "frame swaps" is quite misleading by default, when - // blockOnEachFrame is not used, but emit it for compatibility. - wd->fireFrameSwapped(); - } else { - if (blockOnEachFrame) - data.engine->waitGPU(); - } - qint64 swapTime = 0; if (profileFrames) swapTime = renderTimer.nsecsElapsed(); @@ -375,10 +505,6 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) lastFrameTime = QTime::currentTime(); } - // Might have been set during syncSceneGraph() - if (data.updatePending) - maybeUpdate(window); - // Simulate device loss if requested. static int devLossTest = qEnvironmentVariableIntValue("QT_D3D_TEST_DEVICE_LOSS"); if (devLossTest > 0) { diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h index 2f2ebbef33..c0333ffad0 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h @@ -61,6 +61,8 @@ class QSGD3D12RenderContext; class QSGD3D12RenderLoop : public QSGRenderLoop { + Q_OBJECT + public: QSGD3D12RenderLoop(); ~QSGD3D12RenderLoop(); @@ -77,7 +79,6 @@ public: void update(QQuickWindow *window) override; void maybeUpdate(QQuickWindow *window) override; - void handleUpdateRequest(QQuickWindow *window) override; QAnimationDriver *animationDriver() const override; @@ -88,14 +89,28 @@ public: void postJob(QQuickWindow *window, QRunnable *job) override; QSurface::SurfaceType windowSurfaceType() const override; + bool interleaveIncubation() const override; int flags() const override; + bool event(QEvent *event) override; + +public Q_SLOTS: + void onAnimationStarted(); + void onAnimationStopped(); + private: void exposeWindow(QQuickWindow *window); void obscureWindow(QQuickWindow *window); void renderWindow(QQuickWindow *window); + void render(); + void maybePostUpdateTimer(); + bool somethingVisible() const; QSGD3D12Context *sg; + QAnimationDriver *m_anims; + int m_vsyncDelta; + int m_updateTimer = 0; + int m_animationTimer = 0; struct WindowData { QSGD3D12RenderContext *rc = nullptr; -- cgit v1.2.3 From 4c3a3dbee9701a62eec8455713451ac1afaeda10 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 30 Jun 2016 15:53:03 +0200 Subject: D3D12: Fix 8-bit QImage mapping The engine treats these specially due to the glyph textures sharing the same code path, but ordinary images do not need this. Convert them to 32-bit instead like it's done with OpenGL. Change-Id: If484e61c7269e55ba4ebaaba34819e064c10be7c Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp index c4ba80bd48..a5f3eb7a31 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp @@ -52,7 +52,13 @@ void QSGD3D12Texture::create(const QImage &image, uint flags) const bool alphaRequest = flags & QSGRenderContext::CreateTexture_Alpha; m_alphaWanted = alphaRequest && image.hasAlphaChannel(); - m_image = image; + // The engine maps 8-bit formats to R8. This is fine for glyphs and such + // but may not be what apps expect for ordinary image data. The OpenGL + // implementation maps these to ARGB32_Pre so let's follow suit. + if (image.depth() != 8) + m_image = image; + else + m_image = image.convertToFormat(m_alphaWanted ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); m_id = m_engine->genTexture(); Q_ASSERT(m_id); -- cgit v1.2.3 From 7ab621d9a5393be5996654cd67c85b76fc3f5b2b Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 1 Jul 2016 11:58:08 +0200 Subject: D3D12: Move the info prints to categorized logging Print info messages on startup only when QSG_INFO / qt.scenegraph.general are set, similarly to what happens with OpenGL. Change-Id: I11c0329462bb6f66968246c4412fff940797671b Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 36 +++++++++++++++---------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index e1b076c39a..fefd23ddc1 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -42,6 +42,7 @@ #include "cs_mipmapgen.hlslh" #include #include +#include #include #include @@ -70,6 +71,10 @@ QT_BEGIN_NAMESPACE DECLARE_DEBUG_VAR(render) +// Except for system info on startup. +Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.general") + + static const int DEFAULT_SWAP_CHAIN_BUFFER_COUNT = 3; static const int DEFAULT_FRAME_IN_FLIGHT_COUNT = 2; static const int DEFAULT_WAITABLE_SWAP_CHAIN_MAX_LATENCY = 0; @@ -184,7 +189,7 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte DXGI_ADAPTER_DESC1 desc; adapter->GetDesc1(&desc); const QString name = QString::fromUtf16((char16_t *) desc.Description); - qDebug("Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags); + qCDebug(QSG_LOG_INFO, "Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags); } if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX")) { @@ -193,7 +198,7 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte && SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) { adapter->GetDesc1(&desc); const QString name = QString::fromUtf16((char16_t *) desc.Description); - qDebug("Using requested adapter '%s'", qPrintable(name)); + qCDebug(QSG_LOG_INFO, "Using requested adapter '%s'", qPrintable(name)); *outAdapter = adapter.Detach(); return; } @@ -206,7 +211,7 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) { const QString name = QString::fromUtf16((char16_t *) desc.Description); - qDebug("Using adapter '%s'", qPrintable(name)); + qCDebug(QSG_LOG_INFO, "Using adapter '%s'", qPrintable(name)); break; } } @@ -270,7 +275,7 @@ void QSGD3D12DeviceManager::ensureCreated() } if (warp) { - qDebug("Using WARP"); + qCDebug(QSG_LOG_INFO, "Using WARP"); m_factory->EnumWarpAdapter(IID_PPV_ARGS(&adapter)); HRESULT hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)); if (FAILED(hr)) { @@ -283,14 +288,14 @@ void QSGD3D12DeviceManager::ensureCreated() if (SUCCEEDED(adapter.As(&adapter3))) { DXGI_QUERY_VIDEO_MEMORY_INFO vidMemInfo; if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &vidMemInfo))) { - qDebug("Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", - vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, - vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); + qCDebug(QSG_LOG_INFO, "Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", + vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, + vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); } if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &vidMemInfo))) { - qDebug("Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", - vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, - vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); + qCDebug(QSG_LOG_INFO, "Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", + vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, + vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); } } } @@ -700,13 +705,16 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int else waitableSwapChainMaxLatency = qBound(0, qEnvironmentVariableIntValue(latReqEnvVar), 16); - qDebug("d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d", - swapChainBufferCount, frameInFlightCount); + if (qEnvironmentVariableIsSet("QSG_INFO")) + const_cast(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true); + + qCDebug(QSG_LOG_INFO, "d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d", + swapChainBufferCount, frameInFlightCount); if (waitableSwapChainMaxLatency) - qDebug("Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency); + qCDebug(QSG_LOG_INFO, "Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency); if (qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0) { - qDebug("Enabling debug layer"); + qCDebug(QSG_LOG_INFO, "Enabling debug layer"); ComPtr debugController; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) debugController->EnableDebugLayer(); -- cgit v1.2.3 From 262dbaaa8ddc38dbadbeec1106f87b464727e13b Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 1 Jul 2016 12:40:40 +0200 Subject: D3D12: Improve debug layer support Filter out the annoying RT clear color warning which we have no control over if the user called setColor on the QQuickWindow and a non-default render target is in use. Start breaking on serious error messages and make it possible to break on warnings as well. (QT_D3D_DEBUG_BREAK_ON_WARNINGS) All of this is only relevant when the debug layer is enabled. (QT_D3D_DEBUG) Change-Id: Idfc6e3f3165ea50b54938d358543763bc7433c7c Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 30 +++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index fefd23ddc1..844e3bb7cf 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -713,7 +713,8 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int if (waitableSwapChainMaxLatency) qCDebug(QSG_LOG_INFO, "Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency); - if (qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0) { + const bool debugLayer = qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0; + if (debugLayer) { qCDebug(QSG_LOG_INFO, "Enabling debug layer"); ComPtr debugController; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) @@ -724,6 +725,29 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int device = dev->ref(); dev->registerDeviceLossObserver(this); + if (debugLayer) { + ComPtr infoQueue; + if (SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(&infoQueue)))) { + infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); + infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); + const bool breakOnWarning = qEnvironmentVariableIntValue("QT_D3D_DEBUG_BREAK_ON_WARNING") != 0; + infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, breakOnWarning); + D3D12_INFO_QUEUE_FILTER filter = {}; + D3D12_MESSAGE_ID suppressedMessages[] = { + // When using a render target other than the default one we + // have no way to know the custom clear color, if there is one. + D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE + }; + filter.DenyList.NumIDs = _countof(suppressedMessages); + filter.DenyList.pIDList = suppressedMessages; + // setting the filter would enable Info messages which we don't need + D3D12_MESSAGE_SEVERITY infoSev = D3D12_MESSAGE_SEVERITY_INFO; + filter.DenyList.NumSeverities = 1; + filter.DenyList.pSeverityList = &infoSev; + infoQueue->PushStorageFilter(&filter); + } + } + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; if (FAILED(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)))) { @@ -1001,7 +1025,9 @@ void QSGD3D12EnginePrivate::setupDefaultRenderTargets() device->CreateRenderTargetView(defaultRT[i].Get(), nullptr, defaultRTV[i]); } else { const QSize size(windowSize.width() * windowDpr, windowSize.height() * windowDpr); - const QColor cc(Qt::white); // ### what if setClearColor? non-fatal but debug layer warns... + // Not optimal if the user called setClearColor, but there's so + // much we can do. The debug layer warning is suppressed so we're good to go. + const QColor cc(Qt::white); const QVector4D clearColor(cc.redF(), cc.greenF(), cc.blueF(), cc.alphaF()); ID3D12Resource *msaaRT = createColorBuffer(defaultRTV[i], size, clearColor, windowSamples); if (msaaRT) -- cgit v1.2.3 From 3e0763077a5ce420f42606418917ab7d87b6b763 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 1 Jul 2016 12:58:33 +0200 Subject: D3D12: Fix the rendernode example for multisampling Show how this can be handled in the custom QSGRenderNode implementation. Change-Id: Id6f18ca568a4850060f957998532815bce5c4ac5 Reviewed-by: Andy Nichols --- examples/quick/scenegraph/rendernode/d3d12renderer.cpp | 15 ++++++++++++++- examples/quick/scenegraph/rendernode/main.cpp | 7 +++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp index bc03720407..b7dc62fe86 100644 --- a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp +++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp @@ -153,7 +153,20 @@ void D3D12RenderNode::init() psoDesc.NumRenderTargets = 1; psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; // not in use due to !DepthEnable, but this would be the correct format otherwise - psoDesc.SampleDesc.Count = 1; + // We are rendering on the default render target so if the QuickWindow/View + // has requested samples > 0 then we have to follow suit. + const uint samples = qMax(1, m_item->window()->format().samples()); + psoDesc.SampleDesc.Count = samples; + if (samples > 1) { + D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaInfo = {}; + msaaInfo.Format = psoDesc.RTVFormats[0]; + msaaInfo.SampleCount = samples; + if (SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msaaInfo, sizeof(msaaInfo)))) { + if (msaaInfo.NumQualityLevels > 0) + psoDesc.SampleDesc.Quality = msaaInfo.NumQualityLevels - 1; + } + } + if (FAILED(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pipelineState)))) { qWarning("Failed to create graphics pipeline state"); return; diff --git a/examples/quick/scenegraph/rendernode/main.cpp b/examples/quick/scenegraph/rendernode/main.cpp index 9128cdc5be..3e1714313e 100644 --- a/examples/quick/scenegraph/rendernode/main.cpp +++ b/examples/quick/scenegraph/rendernode/main.cpp @@ -49,6 +49,13 @@ int main(int argc, char **argv) qmlRegisterType("SceneGraphRendering", 2, 0, "CustomRenderItem"); QQuickView view; + + if (QCoreApplication::arguments().contains(QStringLiteral("--multisample"))) { + QSurfaceFormat fmt; + fmt.setSamples(4); + view.setFormat(fmt); + } + view.setResizeMode(QQuickView::SizeRootObjectToView); view.setSource(QUrl("qrc:///scenegraph/rendernode/main.qml")); view.resize(1024, 768); -- cgit v1.2.3 From aae5b6191c3e591bdb0a3a88149b842866fafbd4 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 1 Jul 2016 13:08:44 +0200 Subject: D3D12: Enhance docs wrt multisample, mipmaps and textures Change-Id: I3ca0fe9be7d2faa2381bf598686eddb7703f5597 Reviewed-by: Andy Nichols --- .../doc/src/concepts/visualcanvas/adaptations.qdoc | 28 +++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index fbbc947c4c..3053196692 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -204,7 +204,7 @@ impact performance (CPU load) due to increased API overhead. \section2 Render Loops By default the D3D12 adaptation uses a single-threaded render loop similar to -OpenGL's \c basic render loop. There is also a threaded variant available, that +OpenGL's \c windows render loop. There is also a threaded variant available, that can be requested by setting the \c{QSG_RENDER_LOOP} environment variable to \c threaded. However, due to conceptual limitations in DXGI, the windowing system interface, the threaded loop is prone to deadlocks when multiple QQuickWindow @@ -249,6 +249,32 @@ pre-compiled bytecode into "shaders/+hlsl/effect.frag", while simply writing See the ShaderEffect documentation for more details. +\section2 Multisample Render Targets + +The Direct3D 12 adaptation ignores the QSurfaceFormat set on the QQuickWindow +or QQuickView (or set via QSurfaceFormat::setDefaultFormat()), with one +exception: QSurfaceFormat::samples() is still taken into account. When the +value is greater than 1, multisample offscreen render targets will be created +with the specified sample count and a quality of the maximum supported quality +level. The backend automatically performs resolving into the non-multisample +swapchain buffers after each frame. + +\section2 Mipmaps + +Mipmap generation is supported and handled transparently to the applications +via a built-in compute shader, but is experimental and only supports +power-of-two images at the moment. Textures of other size will work too, but +this involves a QImage-based scaling on the CPU first. Therefore avoid enabling +mipmapping for NPOT images whenever possible. + +\section2 Image formats + +When creating textures via the C++ scenegraph APIs like +QQuickWindow::createTextureFromImage(), 32-bit formats will not involve any +conversion, they will map directly to the corresponding \c{R8G8B8A8_UNORM} or +\c{B8G8R8A8_UNORM} format. Everything else will trigger a QImage-based format +conversion on the CPU first. + \section2 Unsupported Features Particles, sprites, and other OpenGL-dependent tools like -- cgit v1.2.3 From 33b798e3cbf9107b0463f58e907b745c80e94038 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 1 Jul 2016 13:23:04 +0200 Subject: Remove "XXX todo" comments Change-Id: I6e5d7b9eee20c2f7c665e58226d4d759f9ce864d Reviewed-by: Robin Burchell --- src/quick/items/qquickitem.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index c5c17615ee..0352f5ace8 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -369,7 +369,6 @@ Q_SIGNALS: void clipChanged(bool); Q_REVISION(1) void windowChanged(QQuickWindow* window); - // XXX todo void childrenChanged(); void opacityChanged(); void enabledChanged(); @@ -454,7 +453,6 @@ private: Q_DECLARE_PRIVATE(QQuickItem) }; -// XXX todo Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickItem::Flags) #ifndef QT_NO_DEBUG_STREAM -- cgit v1.2.3 From c7de423a8cf033524dfe5a9d2b69412abbb03cbb Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 1 Jul 2016 15:25:22 +0200 Subject: Minor touch event function renaming Rename reallyDeliverTouchEvent back to deliverTouchEvent. To have one entry point for the whole touch handling, have a single function which already didn't call itself recursively: handleTouchEvent. That function optionally compresses and calls deliverTouchEvent. Change-Id: Id3624d3a2ee041cf07b47da36bb3fbb80886a676 Reviewed-by: Robin Burchell --- src/quick/items/qquickwindow.cpp | 21 ++++++++++++--------- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index c3c886de35..aa69257e52 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1478,8 +1478,7 @@ bool QQuickWindow::event(QEvent *e) case QEvent::TouchUpdate: case QEvent::TouchEnd: { QTouchEvent *touch = static_cast(e); - d->translateTouchEvent(touch); - d->deliverTouchEvent(touch); + d->handleTouchEvent(touch); if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) { // we consume all touch events ourselves to avoid duplicate // mouse delivery by QtGui mouse synthesis @@ -1972,20 +1971,24 @@ void QQuickWindowPrivate::deliverDelayedTouchEvent() // Set delayedTouch to 0 before delivery to avoid redelivery in case of // event loop recursions (e.g if it the touch starts a dnd session). QScopedPointer e(delayedTouch.take()); - reallyDeliverTouchEvent(e.data()); + deliverTouchEvent(e.data()); } static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION"); -// check what kind of touch we have (begin/update) and -// call deliverTouchPoints to actually dispatch the points -void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) +// entry point for touch event delivery: +// - translate the event to window coordinates +// - compress the event instead of delivering it if applicable +// - call deliverTouchPoints to actually dispatch the points +void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) { + translateTouchEvent(event); + qCDebug(DBG_TOUCH) << event; Q_Q(QQuickWindow); if (qquickwindow_no_touch_compression || touchRecursionGuard) { - reallyDeliverTouchEvent(event); + deliverTouchEvent(event); return; } @@ -2048,7 +2051,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) } else { if (delayedTouch) deliverDelayedTouchEvent(); - reallyDeliverTouchEvent(event); + deliverTouchEvent(event); } } @@ -2076,7 +2079,7 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() } } -void QQuickWindowPrivate::reallyDeliverTouchEvent(QTouchEvent *event) +void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index c452659c5b..1ab2617f0a 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -156,8 +156,8 @@ public: #endif bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList &, QSet *, QHash > *, QSet *filtered); + void handleTouchEvent(QTouchEvent *); void deliverTouchEvent(QTouchEvent *); - void reallyDeliverTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); void deliverDelayedTouchEvent(); void flushFrameSynchronousEvents(); -- cgit v1.2.3 From f1216b9c4a6cc239c6ed730fcf14e6fd4b3cecdb Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 1 Jul 2016 16:56:43 +0200 Subject: add QQuickWindowPrivate::removeGrabber() The goal is to eventually avoid accessing QQuickWindowPrivate's own variables inside QQuickItemPrivate, and to use itemForTouchPointId instead of mouseGrabberItem. PinchArea relies on some strange behavior: when one touch point is released, it ungrabs the mouse, but keeps the touch point grab. This seems somewhat inconsistent, but for now we keep one place (QQuickItem::ungrabMouse) that does the mouse ungrab only. Change-Id: I46389475da2ecb157508e092f9f3f0cf923881e3 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickitem.cpp | 27 ++++----------------------- src/quick/items/qquickwindow.cpp | 18 ++++++++++++++++++ src/quick/items/qquickwindow_p.h | 1 + 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 6fd8875e83..76f72b6bde 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3013,13 +3013,7 @@ void QQuickItemPrivate::derefWindow() QQuickWindowPrivate *c = QQuickWindowPrivate::get(window); if (polishScheduled) c->itemsToPolish.removeOne(q); - QMutableHashIterator itemTouchMapIt(c->itemForTouchPointId); - while (itemTouchMapIt.hasNext()) { - if (itemTouchMapIt.next().value() == q) - itemTouchMapIt.remove(); - } - if (c->mouseGrabberItem == q) - c->mouseGrabberItem = 0; + c->removeGrabber(q); #ifndef QT_NO_CURSOR if (c->cursorItem == q) { c->cursorItem = 0; @@ -5770,10 +5764,7 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) if (window) { QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); - if (windowPriv->mouseGrabberItem == q) - q->ungrabMouse(); - if (!effectiveVisible) - q->ungrabTouchPoints(); + windowPriv->removeGrabber(q); } bool childVisibilityChanged = false; @@ -5820,10 +5811,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec if (window) { QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); - if (windowPriv->mouseGrabberItem == q) - q->ungrabMouse(); - if (!effectiveEnable) - q->ungrabTouchPoints(); + windowPriv->removeGrabber(q); if (scope && !effectiveEnable && activeFocus) { windowPriv->clearFocusInScope( scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem); @@ -7316,14 +7304,7 @@ void QQuickItem::ungrabTouchPoints() if (!d->window) return; QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window); - - QMutableHashIterator i(windowPriv->itemForTouchPointId); - while (i.hasNext()) { - i.next(); - if (i.value() == this) - i.remove(); - } - touchUngrabEvent(); + windowPriv->removeGrabber(this); } /*! diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index aa69257e52..99ac048219 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -771,6 +771,24 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) } } +void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber) +{ + Q_Q(QQuickWindow); + QMutableHashIterator itemTouchMapIt(itemForTouchPointId); + while (itemTouchMapIt.hasNext()) { + if (itemTouchMapIt.next().value() == grabber) { + itemTouchMapIt.remove(); + grabber->touchUngrabEvent(); + } + } + if (mouseGrabberItem == grabber) { + qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << mouseGrabberItem << "-> null"; + mouseGrabberItem = 0; + QEvent ev(QEvent::UngrabMouse); + q->sendEvent(grabber, &ev); + } +} + void QQuickWindowPrivate::transformTouchPoints(QList &touchPoints, const QTransform &transform) { QMatrix4x4 transformMatrix(transform); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 1ab2617f0a..ac430f3e0c 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -143,6 +143,7 @@ public: bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event); void translateTouchEvent(QTouchEvent *touchEvent); void setMouseGrabber(QQuickItem *grabber); + void removeGrabber(QQuickItem *grabber); static void transformTouchPoints(QList &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *); -- cgit v1.2.3 From 672716db6e17b79c86075e4d79afe76bc0c5a23e Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sun, 3 Jul 2016 13:54:30 +0200 Subject: Improve error handling in TestCase::tryCompare Change-Id: I27a6962e404be0b92a9702f9deff8775edb782b1 Reviewed-by: Mitch Curtis --- src/imports/testlib/TestCase.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index 046ce507d4..cff265869d 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -711,6 +711,11 @@ Item { \sa compare(), SignalSpy::wait() */ function tryCompare(obj, prop, value, timeout, msg) { + if (arguments.length == 1 || typeof(prop) != "string") { + qtest_results.fail("A property name as string is required for tryCompare", + util.callerFile(), util.callerLine()) + throw new Error("QtQuickTest::fail") + } if (arguments.length == 2) { qtest_results.fail("A value is required for tryCompare", util.callerFile(), util.callerLine()) -- cgit v1.2.3 From 123481f94e98f340aa1a19cbf0760dc22620889f Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sun, 3 Jul 2016 11:38:22 +0200 Subject: Fix tst_rendernode to work with hidpi Change-Id: Id2a7f20933533f69fa43eefc2338205b82a27ccd Reviewed-by: Laszlo Agocs --- tests/auto/quick/rendernode/tst_rendernode.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/auto/quick/rendernode/tst_rendernode.cpp b/tests/auto/quick/rendernode/tst_rendernode.cpp index 313e5ac196..e15649e62c 100644 --- a/tests/auto/quick/rendernode/tst_rendernode.cpp +++ b/tests/auto/quick/rendernode/tst_rendernode.cpp @@ -215,16 +215,18 @@ void tst_rendernode::renderOrder() QSKIP("This test does not work at display depths < 24"); QImage fb = runTest("RenderOrder.qml"); - QCOMPARE(fb.width(), 200); - QCOMPARE(fb.height(), 200); + QQuickView v; + int devicePixelRatio = static_cast(v.devicePixelRatio()); + QCOMPARE(fb.width(), 200 * devicePixelRatio); + QCOMPARE(fb.height(), 200 * devicePixelRatio); - QCOMPARE(fb.pixel(50, 50), qRgb(0xff, 0xff, 0xff)); - QCOMPARE(fb.pixel(50, 150), qRgb(0xff, 0xff, 0xff)); - QCOMPARE(fb.pixel(150, 50), qRgb(0x00, 0x00, 0xff)); + QCOMPARE(fb.pixel(50 * devicePixelRatio, 50 * devicePixelRatio), qRgb(0xff, 0xff, 0xff)); + QCOMPARE(fb.pixel(50 * devicePixelRatio, 150 * devicePixelRatio), qRgb(0xff, 0xff, 0xff)); + QCOMPARE(fb.pixel(150 * devicePixelRatio, 50 * devicePixelRatio), qRgb(0x00, 0x00, 0xff)); QByteArray errorMessage; - QVERIFY2(fuzzyCompareColor(fb.pixel(150, 150), qRgb(0x7f, 0x7f, 0xff), &errorMessage), - msgColorMismatchAt(errorMessage, 150, 150).constData()); + QVERIFY2(fuzzyCompareColor(fb.pixel(150 * devicePixelRatio, 150 * devicePixelRatio), qRgb(0x7f, 0x7f, 0xff), &errorMessage), + msgColorMismatchAt(errorMessage, 150 * devicePixelRatio, 150 * devicePixelRatio).constData()); } /* The test uses a number of nested rectangles with clipping -- cgit v1.2.3 From a669a2cd49a832baefb945384fa26b3715a94e3e Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sun, 3 Jul 2016 12:46:57 +0200 Subject: Stabilize tst_SceneGraph::createTextureFromImage The test would crash half the time on my laptop due to the scene graph not being initialized and then returning a nullptr for the texture. At that point texture->hasAlphaChannel() crashes. Change-Id: I9220d9bd6abe6381752c26335f2b60fa8d2adccf Reviewed-by: Shawn Rutledge Reviewed-by: Laszlo Agocs Reviewed-by: Friedemann Kleint --- tests/auto/quick/scenegraph/tst_scenegraph.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp index c0d1b53e92..791bcb215a 100644 --- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp +++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp @@ -549,6 +549,7 @@ void tst_SceneGraph::createTextureFromImage() QQuickView view; view.show(); QTest::qWaitForWindowExposed(&view); + QTRY_VERIFY(view.isSceneGraphInitialized()); QScopedPointer texture(view.createTextureFromImage(image, (QQuickWindow::CreateTextureOptions) flags)); QCOMPARE(texture->hasAlphaChannel(), expectedAlpha); -- cgit v1.2.3 From e00b9df034be846013fa83f0923f823cf6e988f0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 1 Jul 2016 16:20:39 +0200 Subject: Add comments to touchEventForItem and touchEventWithPoints Change-Id: I7f491319af87c7b026ecf071d99766ddd1496388 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 99ac048219..3eb4849d24 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2309,6 +2309,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv return touchEventAccepted; } +// create touch event containing only points inside the target item QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds) { const QList &touchPoints = originalEvent.touchPoints(); @@ -2336,6 +2337,7 @@ QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QT return touchEvent; } +// copy a touch event's basic properties but give it new touch points QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList &newPoints) { Qt::TouchPointStates eventStates; -- cgit v1.2.3 From e7d6606e2d58774c98a1eda5bd879c92ffbbd23c Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 1 Jul 2016 16:56:43 +0200 Subject: QQuickWindowPrivate::removeGrabber: take params for mouse and touch QQuickItem::ungrabTouchPoints() wasn't intended to ungrab mouse and QQuickItem::ungrabMouse() wasn't intended to ungrab touch. This seems to make the tests more likely to pass. Change-Id: I2d39028dc8267b7c8b0e1bac721f1a12014f0b25 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickitem.cpp | 13 ++----------- src/quick/items/qquickwindow.cpp | 16 +++++++++------- src/quick/items/qquickwindow_p.h | 2 +- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 76f72b6bde..9f6574f6c7 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -7208,16 +7208,7 @@ void QQuickItem::ungrabMouse() if (!d->window) return; QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window); - if (windowPriv->mouseGrabberItem != this) { - qWarning("QQuickItem::ungrabMouse(): Item is not the mouse grabber."); - return; - } - - qCDebug(DBG_MOUSE_TARGET) << "ungrabMouse" << windowPriv->mouseGrabberItem << "-> null"; - windowPriv->mouseGrabberItem = 0; - - QEvent ev(QEvent::UngrabMouse); - d->window->sendEvent(this, &ev); + windowPriv->removeGrabber(this, true, false); } @@ -7304,7 +7295,7 @@ void QQuickItem::ungrabTouchPoints() if (!d->window) return; QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window); - windowPriv->removeGrabber(this); + windowPriv->removeGrabber(this, false, true); } /*! diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 3eb4849d24..8ad9711fc4 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -771,17 +771,19 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) } } -void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber) +void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch) { Q_Q(QQuickWindow); - QMutableHashIterator itemTouchMapIt(itemForTouchPointId); - while (itemTouchMapIt.hasNext()) { - if (itemTouchMapIt.next().value() == grabber) { - itemTouchMapIt.remove(); - grabber->touchUngrabEvent(); + if (Q_LIKELY(touch)) { + QMutableHashIterator itemTouchMapIt(itemForTouchPointId); + while (itemTouchMapIt.hasNext()) { + if (itemTouchMapIt.next().value() == grabber) { + itemTouchMapIt.remove(); + grabber->touchUngrabEvent(); + } } } - if (mouseGrabberItem == grabber) { + if (Q_LIKELY(mouse) && mouseGrabberItem == grabber) { qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << mouseGrabberItem << "-> null"; mouseGrabberItem = 0; QEvent ev(QEvent::UngrabMouse); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index ac430f3e0c..3737f9efb8 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -143,7 +143,7 @@ public: bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event); void translateTouchEvent(QTouchEvent *touchEvent); void setMouseGrabber(QQuickItem *grabber); - void removeGrabber(QQuickItem *grabber); + void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); static void transformTouchPoints(QList &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *); -- cgit v1.2.3 From 827af028904c8f0c370ea590d565e9a31e168088 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sun, 3 Jul 2016 14:10:15 +0200 Subject: Canvas test: make sure canvas is ready before: Totals: 80 passed, 653 failed, 0 skipped, 0 blacklisted, 35852ms after: Totals: 729 passed, 4 failed, 0 skipped, 0 blacklisted, 29599ms The last four failures are due to high dpi issues. Change-Id: Ie937091558395572dc281ee5a5b90848ea9d2081 Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml | 4 +++- tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml | 1 + tests/auto/quick/qquickcanvasitem/data/tst_image.qml | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml b/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml index e49f0ac462..b0fb7fcf8c 100644 --- a/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml +++ b/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml @@ -31,7 +31,9 @@ TestCase { } function createCanvasObject(data) { - return component.createObject(testCase, data.properties); + var canvas = component.createObject(testCase, data.properties); + waitForRendering(canvas); + return canvas; } function comparePixel(ctx,x,y,r,g,b,a, d) diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml b/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml index 93f85107a7..565f906fb1 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml @@ -590,6 +590,7 @@ CanvasTestCase { verify(canvas); canvas.width = 100; canvas.height = 100; + waitForRendering(canvas); var ctx = canvas.getContext("2d"); diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_image.qml b/tests/auto/quick/qquickcanvasitem/data/tst_image.qml index 46a038a13c..1f695d7080 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_image.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_image.qml @@ -667,6 +667,7 @@ CanvasTestCase { var canvas2 = Qt.createQmlObject("import QtQuick 2.0; Canvas{renderTarget:Canvas.Image; renderStrategy:Canvas.Immediate}", canvas); canvas2.width = 100; canvas2.height = 50; + waitForRendering(canvas2); var ctx2 = canvas2.getContext('2d'); ctx2.fillStyle = '#0f0'; ctx2.fillRect(0, 0, 100, 50); -- cgit v1.2.3 From 603ff80d3e7622b14d666df73a03e29aec874dda Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sat, 2 Jul 2016 11:53:49 +0200 Subject: Fix tst_qquickpincharea::pan with highdpi and document it The test was expanded slightly and documented, so that it's easier to understand the strangeness of the center. On my display the dragThreshold is returned as 16 while the test assued it's always <= 10. Explain the math and the coordinate changes due to everything being in item coordinates. Change-Id: I4bf72271895ecd9e248154c08bf36eab376c6d79 Reviewed-by: Shawn Rutledge --- .../quick/qquickpincharea/data/pinchproperties.qml | 2 +- .../quick/qquickpincharea/tst_qquickpincharea.cpp | 65 ++++++++++++++++++---- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/tests/auto/quick/qquickpincharea/data/pinchproperties.qml b/tests/auto/quick/qquickpincharea/data/pinchproperties.qml index 37d706fc8e..c5daf6cbfe 100644 --- a/tests/auto/quick/qquickpincharea/data/pinchproperties.qml +++ b/tests/auto/quick/qquickpincharea/data/pinchproperties.qml @@ -2,7 +2,7 @@ import QtQuick 2.0 Rectangle { id: whiteRect property variant center - property real scale + property real scale: -1.0 property int pointCount: 0 property bool pinchActive: false width: 240; height: 320 diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp index aee35b4b90..1d7273f6df 100644 --- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp +++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp @@ -286,6 +286,7 @@ void tst_QQuickPinchArea::pan() QPoint p1(80, 80); QPoint p2(100, 100); { + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); pinchSequence.press(0, p1, window).commit(); QQuickTouchUtils::flush(window); @@ -293,23 +294,63 @@ void tst_QQuickPinchArea::pan() // we have to reuse the same pinchSequence object. pinchSequence.stationary(0).press(1, p2, window).commit(); QQuickTouchUtils::flush(window); - p1 += QPoint(10,10); - p2 += QPoint(10,10); - pinchSequence.move(0, p1,window).move(1, p2,window).commit(); + QVERIFY(!root->property("pinchActive").toBool()); + QCOMPARE(root->property("scale").toReal(), -1.0); + + p1 += QPoint(dragThreshold - 1, 0); + p2 += QPoint(dragThreshold - 1, 0); + pinchSequence.move(0, p1, window).move(1, p2, window).commit(); QQuickTouchUtils::flush(window); + // movement < dragThreshold: pinch not yet active + QVERIFY(!root->property("pinchActive").toBool()); + QCOMPARE(root->property("scale").toReal(), -1.0); - QCOMPARE(root->property("scale").toReal(), 1.0); + // exactly the dragThreshold: pinch starts + p1 += QPoint(1, 0); + p2 += QPoint(1, 0); + pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QVERIFY(root->property("pinchActive").toBool()); + QCOMPARE(root->property("scale").toReal(), 1.0); - p1 += QPoint(10,10); - p2 += QPoint(10,10); - pinchSequence.move(0, p1,window).move(1, p2,window).commit(); + // Calculation of the center point is tricky at first: + // center point of the two touch points in item coordinates: + // scene coordinates: (80, 80) + (dragThreshold, 0), (100, 100) + (dragThreshold, 0) + // = ((180+dT)/2, 180/2) = (90+dT, 90) + // item coordinates: (scene) - (50, 50) = (40+dT, 40) + QCOMPARE(root->property("center").toPointF(), QPointF(40 + dragThreshold, 40)); + // pan started, but no actual movement registered yet: + // blackrect starts at 50,50 + QCOMPARE(blackRect->x(), 50.0); + QCOMPARE(blackRect->y(), 50.0); + + p1 += QPoint(10, 0); + p2 += QPoint(10, 0); + pinchSequence.move(0, p1, window).move(1, p2, window).commit(); QQuickTouchUtils::flush(window); - } + QCOMPARE(root->property("center").toPointF(), QPointF(40 + 10 + dragThreshold, 40)); + QCOMPARE(blackRect->x(), 60.0); + QCOMPARE(blackRect->y(), 50.0); - QCOMPARE(root->property("center").toPointF(), QPointF(60, 60)); // blackrect is at 50,50 - QCOMPARE(blackRect->x(), 60.0); - QCOMPARE(blackRect->y(), 60.0); + p1 += QPoint(0, 10); + p2 += QPoint(0, 10); + pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); + // next big surprise: the center is in item local coordinates and the item was just + // moved 10 to the right... which offsets the center point 10 to the left + QCOMPARE(root->property("center").toPointF(), QPointF(40 + 10 - 10 + dragThreshold, 40 + 10)); + QCOMPARE(blackRect->x(), 60.0); + QCOMPARE(blackRect->y(), 60.0); + + p1 += QPoint(10, 10); + p2 += QPoint(10, 10); + pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); + // now the item moved again, thus the center point of the touch is moved in total by (10, 10) + QCOMPARE(root->property("center").toPointF(), QPointF(50 + dragThreshold, 50)); + QCOMPARE(blackRect->x(), 70.0); + QCOMPARE(blackRect->y(), 70.0); + } // pan x beyond bound p1 += QPoint(100,100); @@ -318,7 +359,7 @@ void tst_QQuickPinchArea::pan() QQuickTouchUtils::flush(window); QCOMPARE(blackRect->x(), 140.0); - QCOMPARE(blackRect->y(), 160.0); + QCOMPARE(blackRect->y(), 170.0); QTest::touchEvent(window, device).release(0, p1, window).release(1, p2, window); QQuickTouchUtils::flush(window); -- cgit v1.2.3 From 5b470b6b6ad07975f9f9a56425a09b4827e87650 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 4 Jul 2016 23:36:39 +0200 Subject: Improve tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate Make it more reliable and faster - average run time goes down by ~400 ms while the test passes reliable for me after this change. Most QTRY_* will return instantly since the condition will be good in any case. On my laptop the test tended to fail roughly every 4th run without this patch. The failure was the second click on the new window not working without the QTRY_VERIFY. Removing the extra 200 ms wait time is what makes it faster - I see no good reason for the wait here. Change-Id: I5f45da91ef04a9b807ff0e9362c9d9b3881c227d Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index aa1474df91..6ab7f79ce1 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -791,14 +791,14 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate() QGuiApplication::sendEvent(&window, &pressEvent); - QVERIFY(window.rootObject()->property("pressed").toBool()); + QTRY_VERIFY(window.rootObject()->property("pressed").toBool()); QVERIFY(!window.rootObject()->property("canceled").toBool()); QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease); QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks); if (doubleClick) { QGuiApplication::sendEvent(&window, &releaseEvent); - QVERIFY(!window.rootObject()->property("pressed").toBool()); + QTRY_VERIFY(!window.rootObject()->property("pressed").toBool()); QVERIFY(!window.rootObject()->property("canceled").toBool()); QCOMPARE(window.rootObject()->property("released").toInt(), ++expectedRelease); QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks); @@ -807,7 +807,7 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate() QMouseEvent pressEvent2(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0); QGuiApplication::sendEvent(&window, &pressEvent2); - QVERIFY(window.rootObject()->property("pressed").toBool()); + QTRY_VERIFY(window.rootObject()->property("pressed").toBool()); QVERIFY(!window.rootObject()->property("canceled").toBool()); QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease); QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks); @@ -819,23 +819,21 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate() secondWindow->setProperty("visible", true); QTest::qWaitForWindowExposed(secondWindow); - QVERIFY(!window.rootObject()->property("pressed").toBool()); + QTRY_VERIFY(!window.rootObject()->property("pressed").toBool()); QVERIFY(window.rootObject()->property("canceled").toBool()); QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease); QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks); //press again QGuiApplication::sendEvent(&window, &pressEvent); - QVERIFY(window.rootObject()->property("pressed").toBool()); + QTRY_VERIFY(window.rootObject()->property("pressed").toBool()); QVERIFY(!window.rootObject()->property("canceled").toBool()); QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease); QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks); - QTest::qWait(200); - //release QGuiApplication::sendEvent(&window, &releaseEvent); - QVERIFY(!window.rootObject()->property("pressed").toBool()); + QTRY_VERIFY(!window.rootObject()->property("pressed").toBool()); QVERIFY(!window.rootObject()->property("canceled").toBool()); QCOMPARE(window.rootObject()->property("released").toInt(), ++expectedRelease); QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks); -- cgit v1.2.3 From 8c745d808527684836d04da9014ee33c7cf8b6f1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 23 Jun 2016 10:37:03 +0200 Subject: QtQuick: clean up shader effect property connections - Don't use a signal mapper, but handle the mapping using a custom slot object and a lambda to do the dispatching ourselves. - Don't do meta-calls by property name, but by index. - Cache the meta-object. - Resolve the property indices by using the QML property cache. For a shader with 6 property connections, the time spent goes from 320k instructions to 80k instructions (valgrind on x86_64). Task-number: QTBUG-53901 Change-Id: I2809198cf62f9716b3683798222203fc3e97fbb3 Reviewed-by: Robin Burchell --- src/particles/qquickcustomparticle.cpp | 17 +-- src/particles/qquickcustomparticle_p.h | 4 +- src/qml/qml/ftw/qhashedstring_p.h | 4 +- src/qml/qml/qqmldata_p.h | 2 +- src/qml/qml/qqmlengine.cpp | 12 +- src/quick/items/qquickopenglshadereffect.cpp | 126 ++++++++++++++------- src/quick/items/qquickopenglshadereffect_p.h | 26 +++-- src/quick/items/qquickopenglshadereffectnode_p.h | 10 ++ .../qquickshadereffect/tst_qquickshadereffect.cpp | 57 +++++----- 9 files changed, 170 insertions(+), 88 deletions(-) diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp index a89b0d6a3b..8ee431aeb2 100644 --- a/src/particles/qquickcustomparticle.cpp +++ b/src/particles/qquickcustomparticle.cpp @@ -94,7 +94,8 @@ struct PlainVertices { QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent) : QQuickParticlePainter(parent) - , m_common(this) + , m_common(this, [this](int mappedId){this->propertyChanged(mappedId);}) + , m_myMetaObject(nullptr) , m_dirtyUniforms(true) , m_dirtyUniformValues(true) , m_dirtyTextureProviders(true) @@ -114,7 +115,10 @@ QQuickCustomParticle::~QQuickCustomParticle() void QQuickCustomParticle::componentComplete() { - m_common.updateShader(this, Key::FragmentShader); + if (!m_myMetaObject) + m_myMetaObject = metaObject(); + + m_common.updateShader(this, m_myMetaObject, Key::FragmentShader); updateVertexShader(); reset(); QQuickParticlePainter::componentComplete(); @@ -138,7 +142,7 @@ void QQuickCustomParticle::setFragmentShader(const QByteArray &code) m_common.source.sourceCode[Key::FragmentShader] = code; m_dirtyProgram = true; if (isComponentComplete()) { - m_common.updateShader(this, Key::FragmentShader); + m_common.updateShader(this, m_myMetaObject, Key::FragmentShader); reset(); } emit fragmentShaderChanged(); @@ -202,7 +206,6 @@ void QQuickCustomParticle::setVertexShader(const QByteArray &code) void QQuickCustomParticle::updateVertexShader() { m_common.disconnectPropertySignals(this, Key::VertexShader); - qDeleteAll(m_common.signalMappers[Key::VertexShader]); m_common.uniformData[Key::VertexShader].clear(); m_common.signalMappers[Key::VertexShader].clear(); m_common.attributes.clear(); @@ -225,9 +228,9 @@ void QQuickCustomParticle::updateVertexShader() const QByteArray &code = m_common.source.sourceCode[Key::VertexShader]; if (!code.isEmpty()) - m_common.lookThroughShaderCode(this, Key::VertexShader, code); + m_common.lookThroughShaderCode(this, m_myMetaObject, Key::VertexShader, code); - m_common.connectPropertySignals(this, Key::VertexShader); + m_common.connectPropertySignals(this, m_myMetaObject, Key::VertexShader); } void QQuickCustomParticle::reset() @@ -392,7 +395,7 @@ void QQuickCustomParticle::sourceDestroyed(QObject *object) void QQuickCustomParticle::propertyChanged(int mappedId) { bool textureProviderChanged; - m_common.propertyChanged(this, mappedId, &textureProviderChanged); + m_common.propertyChanged(this, m_myMetaObject, mappedId, &textureProviderChanged); m_dirtyTextureProviders |= textureProviderChanged; m_dirtyUniformValues = true; update(); diff --git a/src/particles/qquickcustomparticle_p.h b/src/particles/qquickcustomparticle_p.h index d9690aa96a..e9d68cbe5c 100644 --- a/src/particles/qquickcustomparticle_p.h +++ b/src/particles/qquickcustomparticle_p.h @@ -99,9 +99,10 @@ protected: private Q_SLOTS: void sourceDestroyed(QObject *object); - void propertyChanged(int mappedId); private: + void propertyChanged(int mappedId); + typedef QQuickOpenGLShaderEffectMaterialKey Key; typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData; @@ -109,6 +110,7 @@ private: void updateVertexShader(); QQuickOpenGLShaderEffectCommon m_common; + const QMetaObject *m_myMetaObject; QHash m_nodes; qreal m_lastTime; diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h index 6ff3e4a11b..03361f47f7 100644 --- a/src/qml/qml/ftw/qhashedstring_p.h +++ b/src/qml/qml/ftw/qhashedstring_p.h @@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE // #define QSTRINGHASH_LINK_DEBUG class QHashedStringRef; -class Q_AUTOTEST_EXPORT QHashedString : public QString +class Q_QML_PRIVATE_EXPORT QHashedString : public QString { public: inline QHashedString(); @@ -94,7 +94,7 @@ private: }; class QHashedCStringRef; -class Q_AUTOTEST_EXPORT QHashedStringRef +class Q_QML_PRIVATE_EXPORT QHashedStringRef { public: inline QHashedStringRef(); diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 94a3b0f198..a9f2acdb06 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -229,7 +229,7 @@ public: static inline void flushPendingBinding(QObject *, int coreIndex); - static void ensurePropertyCache(QJSEngine *engine, QObject *object); + static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object); private: // For attachedProperties diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 6cd32852e1..f7191feec5 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1805,14 +1805,16 @@ void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) QQmlData_setBit(this, obj, coreIndex * 2 + 1); } -void QQmlData::ensurePropertyCache(QJSEngine *engine, QObject *object) +QQmlPropertyCache *QQmlData::ensurePropertyCache(QJSEngine *engine, QObject *object) { Q_ASSERT(engine); QQmlData *ddata = QQmlData::get(object, /*create*/true); - if (ddata->propertyCache) - return; - ddata->propertyCache = QJSEnginePrivate::get(engine)->cache(object); - if (ddata->propertyCache) ddata->propertyCache->addref(); + if (!ddata->propertyCache) { + ddata->propertyCache = QJSEnginePrivate::get(engine)->cache(object); + if (ddata->propertyCache) + ddata->propertyCache->addref(); + } + return ddata->propertyCache; } void QQmlEnginePrivate::sendQuit() diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index b77a7830ff..3f057ecd64 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -179,12 +179,40 @@ namespace { } } +namespace QtPrivate { +class MappedSlotObject: public QtPrivate::QSlotObjectBase +{ +public: + typedef std::function PropChangedFunc; + explicit MappedSlotObject(PropChangedFunc func) + : QSlotObjectBase(&impl), _signalIndex(-1), func(func) + {} -QQuickOpenGLShaderEffectCommon::~QQuickOpenGLShaderEffectCommon() -{ - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) - qDeleteAll(signalMappers[shaderType]); + void setSignalIndex(int idx) { _signalIndex = idx; } + int signalIndex() const { return _signalIndex; } + +private: + int _signalIndex; + PropChangedFunc func; + + static void impl(int which, QSlotObjectBase *this_, QObject *, void **a, bool *ret) + { + auto thiz = static_cast(this_); + switch (which) { + case Destroy: + delete thiz; + break; + case Call: + thiz->func(); + break; + case Compare: + *ret = thiz == reinterpret_cast(a[0]); + break; + case NumOperations: ; + } + } +}; } void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) @@ -193,9 +221,9 @@ void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, if (signalMappers[shaderType].at(i) == 0) continue; const UniformData &d = uniformData[shaderType].at(i); - QSignalMapper *mapper = signalMappers[shaderType].at(i); - QObject::disconnect(item, 0, mapper, SLOT(map())); - QObject::disconnect(mapper, SIGNAL(mapped(int)), host, SLOT(propertyChanged(int))); + auto mapper = signalMappers[shaderType].at(i); + void *a = mapper; + QObjectPrivate::disconnect(item, mapper->signalIndex(), &a); if (d.specialType == UniformData::Sampler) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) { @@ -207,25 +235,30 @@ void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, } } -void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) +void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item, + const QMetaObject *itemMetaObject, + Key::ShaderType shaderType) { + QQmlPropertyCache *propCache = QQmlData::ensurePropertyCache(qmlEngine(item), item); for (int i = 0; i < uniformData[shaderType].size(); ++i) { if (signalMappers[shaderType].at(i) == 0) continue; const UniformData &d = uniformData[shaderType].at(i); - int pi = item->metaObject()->indexOfProperty(d.name.constData()); - if (pi >= 0) { - QMetaProperty mp = item->metaObject()->property(pi); - if (!mp.hasNotifySignal()) + QQmlPropertyData *pd = propCache->property(QString::fromUtf8(d.name), nullptr, nullptr); + if (pd && !pd->isFunction()) { + if (pd->notifyIndex == -1) { qWarning("QQuickOpenGLShaderEffect: property '%s' does not have notification method!", d.name.constData()); - const QByteArray signalName = '2' + mp.notifySignal().methodSignature(); - QSignalMapper *mapper = signalMappers[shaderType].at(i); - QObject::connect(item, signalName, mapper, SLOT(map())); - QObject::connect(mapper, SIGNAL(mapped(int)), host, SLOT(propertyChanged(int))); + } else { + auto *mapper = signalMappers[shaderType].at(i); + mapper->setSignalIndex(pd->notifyIndex); + Q_ASSERT(item->metaObject() == itemMetaObject); + QObjectPrivate::connectImpl(item, mapper->signalIndex(), item, nullptr, mapper, + Qt::AutoConnection, nullptr, itemMetaObject); + } } else { // If the source is set via a dynamic property, like the layer is, then we need this // check to disable the warning. - if (!item->property(d.name.constData()).isValid()) + if (!item->property(d.name).isValid()) qWarning("QQuickOpenGLShaderEffect: '%s' does not have a matching property!", d.name.constData()); } @@ -269,8 +302,12 @@ void QQuickOpenGLShaderEffectCommon::updateParseLog(bool ignoreAttributes) parseLog += QLatin1String("Warning: Shaders are missing reference to \'qt_Opacity\'.\n"); } -void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code) +void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, + const QMetaObject *itemMetaObject, + Key::ShaderType shaderType, + const QByteArray &code) { + QQmlPropertyCache *propCache = QQmlData::ensurePropertyCache(qmlEngine(item), item); int index = 0; int typeIndex = -1; int typeLength = 0; @@ -293,7 +330,7 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key const int srLen = sizeof("qt_SubRect_") - 1; UniformData d; - QSignalMapper *mapper = 0; + QtPrivate::MappedSlotObject *mapper = nullptr; d.name = QByteArray(s + nameIndex, nameLength); if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) { d.specialType = UniformData::Opacity; @@ -302,11 +339,17 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key } else if (nameLength > srLen && qstrncmp("qt_SubRect_", s + nameIndex, srLen) == 0) { d.specialType = UniformData::SubRect; } else { - mapper = new QSignalMapper; - mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16)); - d.value = item->property(d.name.constData()); + if (QQmlPropertyData *pd = propCache->property(QString::fromUtf8(d.name), nullptr, nullptr)) { + if (!pd->isFunction()) + d.propertyIndex = pd->coreIndex; + } + const int mappedId = uniformData[shaderType].size() | (shaderType << 16); + mapper = new QtPrivate::MappedSlotObject([this, mappedId](){ + this->mappedPropertyChanged(mappedId); + }); bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0; d.specialType = sampler ? UniformData::Sampler : UniformData::None; + d.setValueFromProperty(item, itemMetaObject); } uniformData[shaderType].append(d); signalMappers[shaderType].append(mapper); @@ -314,10 +357,11 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key } } -void QQuickOpenGLShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType) +void QQuickOpenGLShaderEffectCommon::updateShader(QQuickItem *item, + const QMetaObject *itemMetaObject, + Key::ShaderType shaderType) { disconnectPropertySignals(item, shaderType); - qDeleteAll(signalMappers[shaderType]); uniformData[shaderType].clear(); signalMappers[shaderType].clear(); if (shaderType == Key::VertexShader) @@ -363,20 +407,20 @@ void QQuickOpenGLShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderT d.specialType = UniformData::Opacity; uniformData[Key::FragmentShader].append(d); signalMappers[Key::FragmentShader].append(0); - QSignalMapper *mapper = new QSignalMapper; - mapper->setMapping(item, 1 | (Key::FragmentShader << 16)); + const int mappedId = 1 | (Key::FragmentShader << 16); + auto mapper = new QtPrivate::MappedSlotObject([this, mappedId](){mappedPropertyChanged(mappedId);}); const char *sourceName = "source"; d.name = sourceName; - d.value = item->property(sourceName); + d.setValueFromProperty(item, itemMetaObject); d.specialType = UniformData::Sampler; uniformData[Key::FragmentShader].append(d); signalMappers[Key::FragmentShader].append(mapper); } } else { - lookThroughShaderCode(item, shaderType, code); + lookThroughShaderCode(item, itemMetaObject, shaderType, code); } - connectPropertySignals(item, shaderType); + connectPropertySignals(item, itemMetaObject, shaderType); } void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode *node, @@ -507,8 +551,9 @@ static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector> 16); int index = mappedId & 0xffff; @@ -527,7 +572,7 @@ void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item, int mappe QObject::disconnect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*))); } - d.value = item->property(d.name.constData()); + d.setValueFromProperty(item, itemMetaObject); source = qobject_cast(qvariant_cast(d.value)); if (source) { @@ -542,7 +587,7 @@ void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item, int mappe if (textureProviderChanged) *textureProviderChanged = true; } else { - d.value = item->property(d.name.constData()); + d.setValueFromProperty(item, itemMetaObject); if (textureProviderChanged) *textureProviderChanged = false; } @@ -551,11 +596,12 @@ void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item, int mappe QQuickOpenGLShaderEffect::QQuickOpenGLShaderEffect(QQuickShaderEffect *item, QObject *parent) : QObject(parent) , m_item(item) + , m_itemMetaObject(nullptr) , m_meshResolution(1, 1) , m_mesh(0) , m_cullMode(QQuickShaderEffect::NoCulling) , m_status(QQuickShaderEffect::Uncompiled) - , m_common(this) + , m_common(this, [this](int mappedId){this->propertyChanged(mappedId);}) , m_blending(true) , m_dirtyUniforms(true) , m_dirtyUniformValues(true) @@ -708,7 +754,8 @@ void QQuickOpenGLShaderEffect::handleEvent(QEvent *event) for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) { if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) { bool textureProviderChanged; - m_common.propertyChanged(m_item, (shaderType << 16) | i, &textureProviderChanged); + m_common.propertyChanged(m_item, m_itemMetaObject, + (shaderType << 16) | i, &textureProviderChanged); m_dirtyTextureProviders |= textureProviderChanged; m_dirtyUniformValues = true; m_item->update(); @@ -746,7 +793,7 @@ void QQuickOpenGLShaderEffect::sourceDestroyed(QObject *object) void QQuickOpenGLShaderEffect::propertyChanged(int mappedId) { bool textureProviderChanged; - m_common.propertyChanged(m_item, mappedId, &textureProviderChanged); + m_common.propertyChanged(m_item, m_itemMetaObject, mappedId, &textureProviderChanged); m_dirtyTextureProviders |= textureProviderChanged; m_dirtyUniformValues = true; m_item->update(); @@ -880,6 +927,9 @@ QSGNode *QQuickOpenGLShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQuic void QQuickOpenGLShaderEffect::maybeUpdateShaders(bool force) { + if (!m_itemMetaObject) + m_itemMetaObject = m_item->metaObject(); + // Defer processing if a window is not yet associated with the item. This // is because the actual scenegraph backend is not known so conditions // based on GraphicsInfo.shaderType and similar evaluate to wrong results. @@ -890,12 +940,12 @@ void QQuickOpenGLShaderEffect::maybeUpdateShaders(bool force) if (m_vertNeedsUpdate) { m_vertNeedsUpdate = false; - m_common.updateShader(m_item, Key::VertexShader); + m_common.updateShader(m_item, m_itemMetaObject, Key::VertexShader); } if (m_fragNeedsUpdate) { m_fragNeedsUpdate = false; - m_common.updateShader(m_item, Key::FragmentShader); + m_common.updateShader(m_item, m_itemMetaObject, Key::FragmentShader); } } diff --git a/src/quick/items/qquickopenglshadereffect_p.h b/src/quick/items/qquickopenglshadereffect_p.h index 062eedd744..44b60c97d9 100644 --- a/src/quick/items/qquickopenglshadereffect_p.h +++ b/src/quick/items/qquickopenglshadereffect_p.h @@ -61,6 +61,7 @@ #include "qquickshadereffectmesh_p.h" #include +#include QT_BEGIN_NAMESPACE @@ -69,32 +70,39 @@ class QSignalMapper; class QFileSelector; class QQuickOpenGLCustomMaterialShader; +namespace QtPrivate { +class MappedSlotObject; +} + // Common class for QQuickOpenGLShaderEffect and QQuickCustomParticle. struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon { typedef QQuickOpenGLShaderEffectMaterialKey Key; typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData; - QQuickOpenGLShaderEffectCommon(QObject *host) : host(host), fileSelector(nullptr) { } - ~QQuickOpenGLShaderEffectCommon(); + QQuickOpenGLShaderEffectCommon(QObject *host, std::function mappedPropertyChanged) + : host(host), mappedPropertyChanged(mappedPropertyChanged), fileSelector(nullptr) + { } + void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); - void connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); + void connectPropertySignals(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType); void updateParseLog(bool ignoreAttributes); - void lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code); - void updateShader(QQuickItem *item, Key::ShaderType shaderType); + void lookThroughShaderCode(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType, const QByteArray &code); + void updateShader(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType); void updateMaterial(QQuickOpenGLShaderEffectNode *node, QQuickOpenGLShaderEffectMaterial *material, bool updateUniforms, bool updateUniformValues, bool updateTextureProviders); void updateWindow(QQuickWindow *window); // Called by slots in QQuickOpenGLShaderEffect: void sourceDestroyed(QObject *object); - void propertyChanged(QQuickItem *item, int mappedId, bool *textureProviderChanged); + void propertyChanged(QQuickItem *item, const QMetaObject *itemMetaObject, int mappedId, bool *textureProviderChanged); QObject *host; + std::function mappedPropertyChanged; Key source; QVector attributes; QVector uniformData[Key::ShaderTypeCount]; - QVector signalMappers[Key::ShaderTypeCount]; + QVector signalMappers[Key::ShaderTypeCount]; QString parseLog; QFileSelector *fileSelector; }; @@ -142,9 +150,10 @@ private Q_SLOTS: void updateGeometryIfAtlased(); void updateLogAndStatus(const QString &log, int status); void sourceDestroyed(QObject *object); - void propertyChanged(int mappedId); private: + void propertyChanged(int mappedId); + friend class QQuickCustomMaterialShader; friend class QQuickOpenGLShaderEffectNode; @@ -152,6 +161,7 @@ private: typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData; QQuickShaderEffect *m_item; + const QMetaObject *m_itemMetaObject; QSize m_meshResolution; QQuickShaderEffectMesh *m_mesh; QQuickGridMesh m_defaultMesh; diff --git a/src/quick/items/qquickopenglshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h index adf5ef730b..5abe3ae3d0 100644 --- a/src/quick/items/qquickopenglshadereffectnode_p.h +++ b/src/quick/items/qquickopenglshadereffectnode_p.h @@ -90,9 +90,19 @@ public: QByteArray name; QVariant value; + int propertyIndex = -1; SpecialType specialType; bool operator == (const UniformData &other) const; + + void setValueFromProperty(QObject *item, const QMetaObject *itemMetaObject) + { + if (propertyIndex == -1) { + value = item->property(name); + } else { + value = itemMetaObject->property(propertyIndex).read(item); + } + } }; explicit QQuickOpenGLShaderEffectMaterial(QQuickOpenGLShaderEffectNode *node = 0); diff --git a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp index 171e0800e1..54b64fdee9 100644 --- a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp +++ b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp @@ -33,12 +33,13 @@ #include #include #include +#include #include "../../shared/util.h" class TestShaderEffect : public QQuickShaderEffect { Q_OBJECT - Q_PROPERTY(QVariant source READ dummyRead NOTIFY dummyChanged) + Q_PROPERTY(QVariant source READ dummyRead NOTIFY sourceChanged) Q_PROPERTY(QVariant _0aA9zZ READ dummyRead NOTIFY dummyChanged) Q_PROPERTY(QVariant x86 READ dummyRead NOTIFY dummyChanged) Q_PROPERTY(QVariant X READ dummyRead NOTIFY dummyChanged) @@ -47,17 +48,18 @@ class TestShaderEffect : public QQuickShaderEffect public: QMatrix4x4 mat4x4Read() const { return QMatrix4x4(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1); } QVariant dummyRead() const { return QVariant(); } - bool isConnected(const QMetaMethod &signal) const { return m_signals.contains(signal); } + + int signalsConnected = 0; protected: - void connectNotify(const QMetaMethod &signal) { m_signals.append(signal); } - void disconnectNotify(const QMetaMethod &signal) { m_signals.removeOne(signal); } + void connectNotify(const QMetaMethod &) { ++signalsConnected; } + void disconnectNotify(const QMetaMethod &) { --signalsConnected; } signals: void dummyChanged(); + void sourceChanged(); private: - QList m_signals; }; class tst_qquickshadereffect : public QQmlDataTest @@ -82,12 +84,13 @@ private: TexCoordPresent = 0x02, MatrixPresent = 0x04, OpacityPresent = 0x08, - PropertyPresent = 0x10 + SourcePresent = 0x10 }; }; tst_qquickshadereffect::tst_qquickshadereffect() { + qmlRegisterType("ShaderEffectTest", 1, 0, "TestShaderEffect"); } void tst_qquickshadereffect::initTestCase() @@ -120,7 +123,7 @@ void tst_qquickshadereffect::lookThroughShaderCode_data() "void main() { \n" " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n" "}") - << (VertexPresent | TexCoordPresent | MatrixPresent | OpacityPresent | PropertyPresent); + << (VertexPresent | TexCoordPresent | MatrixPresent | OpacityPresent | SourcePresent); QTest::newRow("empty") << QByteArray(" ") // one space -- if completely empty, default will be used instead. @@ -133,14 +136,14 @@ void tst_qquickshadereffect::lookThroughShaderCode_data() "attribute highp vec4 qt_Vertex;\n" "// attribute highp vec2 qt_MultiTexCoord0;") << QByteArray("uniform int source; // uniform lowp float qt_Opacity;") - << (VertexPresent | PropertyPresent); + << (VertexPresent | SourcePresent); QTest::newRow("inside block comments") << QByteArray("/*uniform highp mat4 qt_Matrix;\n" "*/attribute highp vec4 qt_Vertex;\n" "/*/attribute highp vec2 qt_MultiTexCoord0;//**/") << QByteArray("/**/uniform int source; /* uniform lowp float qt_Opacity; */") - << (VertexPresent | PropertyPresent); + << (VertexPresent | SourcePresent); QTest::newRow("inside preprocessor directive") << QByteArray("#define uniform\nhighp mat4 qt_Matrix;\n" @@ -148,7 +151,7 @@ void tst_qquickshadereffect::lookThroughShaderCode_data() "#if\\\nattribute highp vec2 qt_MultiTexCoord0;") << QByteArray("uniform int source;\n" " # undef uniform lowp float qt_Opacity;") - << (VertexPresent | PropertyPresent); + << (VertexPresent | SourcePresent); QTest::newRow("line comments between") @@ -156,21 +159,21 @@ void tst_qquickshadereffect::lookThroughShaderCode_data() "attribute//\nhighp//\nvec4//\nqt_Vertex;\n" " //*/ uniform \n attribute //\\ \n highp //// \n vec2 //* \n qt_MultiTexCoord0;") << QByteArray("uniform// lowp float qt_Opacity;\nsampler2D source;") - << (VertexPresent | TexCoordPresent | MatrixPresent | PropertyPresent); + << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent); QTest::newRow("block comments between") << QByteArray("uniform/*foo*/highp/*/bar/*/mat4/**//**/qt_Matrix;\n" "attribute/**/highp/**/vec4/**/qt_Vertex;\n" " /* * */ attribute /*///*/ highp /****/ vec2 /**/ qt_MultiTexCoord0;") << QByteArray("uniform/*/ uniform//lowp/*float qt_Opacity;*/sampler2D source;") - << (VertexPresent | TexCoordPresent | MatrixPresent | PropertyPresent); + << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent); QTest::newRow("preprocessor directive between") << QByteArray("uniform\n#foo\nhighp\n#bar\nmat4\n#baz\\\nblimey\nqt_Matrix;\n" "attribute\n#\nhighp\n#\nvec4\n#\nqt_Vertex;\n" " #uniform \n attribute \n # foo \n highp \n # bar \n vec2 \n#baz \n qt_MultiTexCoord0;") << QByteArray("uniform\n#if lowp float qt_Opacity;\nsampler2D source;") - << (VertexPresent | TexCoordPresent | MatrixPresent | PropertyPresent); + << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent); QTest::newRow("newline between") << QByteArray("uniform\nhighp\nmat4\nqt_Matrix\n;\n" @@ -178,7 +181,7 @@ void tst_qquickshadereffect::lookThroughShaderCode_data() " \n attribute \n highp \n vec2 \n qt_Multi\nTexCoord0 \n ;") << QByteArray("uniform\nsampler2D\nsource;" "uniform lowp float qt_Opacity;") - << (VertexPresent | MatrixPresent | OpacityPresent | PropertyPresent); + << (VertexPresent | MatrixPresent | OpacityPresent | SourcePresent); QTest::newRow("extra characters #1") @@ -219,28 +222,28 @@ void tst_qquickshadereffect::lookThroughShaderCode_data() "attribute highp qt_MultiTexCoord0;\n") << QByteArray("uniform lowp float qt_Opacity;\n" "uniform mediump float source;\n") - << (MatrixPresent | OpacityPresent | PropertyPresent); + << (MatrixPresent | OpacityPresent | SourcePresent); QTest::newRow("property name #1") << QByteArray("uniform highp vec3 _0aA9zZ;") << QByteArray(" ") - << int(PropertyPresent); + << int(SourcePresent); QTest::newRow("property name #2") << QByteArray("uniform mediump vec2 x86;") << QByteArray(" ") - << int(PropertyPresent); + << int(SourcePresent); QTest::newRow("property name #3") << QByteArray("uniform lowp float X;") << QByteArray(" ") - << int(PropertyPresent); + << int(SourcePresent); QTest::newRow("property name #4") << QByteArray("uniform highp mat4 mat4x4;") << QByteArray(" ") - << int(PropertyPresent); + << int(SourcePresent); } void tst_qquickshadereffect::lookThroughShaderCode() @@ -249,9 +252,11 @@ void tst_qquickshadereffect::lookThroughShaderCode() QFETCH(QByteArray, fragmentShader); QFETCH(int, presenceFlags); - TestShaderEffect item; - QMetaMethod dummyChangedSignal = QMetaMethod::fromSignal(&TestShaderEffect::dummyChanged); - QVERIFY(!item.isConnected(dummyChangedSignal)); // Nothing connected yet. + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\nimport ShaderEffectTest 1.0\nTestShaderEffect {}", QUrl()); + QScopedPointer item(qobject_cast(component.create())); + QCOMPARE(item->signalsConnected, 1); QString expected; if ((presenceFlags & VertexPresent) == 0) @@ -263,12 +268,12 @@ void tst_qquickshadereffect::lookThroughShaderCode() if ((presenceFlags & OpacityPresent) == 0) expected += "Warning: Shaders are missing reference to \'qt_Opacity\'.\n"; - item.setVertexShader(vertexShader); - item.setFragmentShader(fragmentShader); - QCOMPARE(item.parseLog(), expected); + item->setVertexShader(vertexShader); + item->setFragmentShader(fragmentShader); + QCOMPARE(item->parseLog(), expected); // If the uniform was successfully parsed, the notify signal has been connected to an update slot. - QCOMPARE(item.isConnected(dummyChangedSignal), (presenceFlags & PropertyPresent) != 0); + QCOMPARE(item->signalsConnected, (presenceFlags & SourcePresent) ? 2 : 1); } void tst_qquickshadereffect::deleteSourceItem() -- cgit v1.2.3 From dcafea9111eab7dd16760c8cf91b3872c06008d7 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 4 Jul 2016 11:39:16 +0200 Subject: Export the OpenGL context and rendercontext Because traditional context plugins (see scenegraph-playground) may want to subclass these instead of the generic base interfaces with no ready-made OpenGL functionality. Change-Id: Idfd66e132f386aecdcd6e980605332abd45411ce Reviewed-by: Robin Burchell --- src/quick/scenegraph/qsgdefaultcontext_p.h | 2 +- src/quick/scenegraph/qsgdefaultrendercontext_p.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/scenegraph/qsgdefaultcontext_p.h b/src/quick/scenegraph/qsgdefaultcontext_p.h index 63c817cc7b..908934d28c 100644 --- a/src/quick/scenegraph/qsgdefaultcontext_p.h +++ b/src/quick/scenegraph/qsgdefaultcontext_p.h @@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE -class QSGDefaultContext : public QSGContext, public QSGRendererInterface +class Q_QUICK_PRIVATE_EXPORT QSGDefaultContext : public QSGContext, public QSGRendererInterface { public: QSGDefaultContext(QObject *parent = 0); diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h index c5e08c07f0..bc653565cf 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h +++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h @@ -64,7 +64,7 @@ namespace QSGAtlasTexture { class Manager; } -class QSGDefaultRenderContext : public QSGRenderContext +class Q_QUICK_PRIVATE_EXPORT QSGDefaultRenderContext : public QSGRenderContext { Q_OBJECT public: -- cgit v1.2.3 From edfaf6c497aea24f74fca63beb3fe4fd95dece92 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Thu, 30 Jun 2016 00:13:20 +0200 Subject: qsgsoftwarerenderloop: Don't lie about which render loop is running I forsee this as a good way to get confused in the future otherwise. Change-Id: I85a76bc4666db206b694b6fca6bdcc410e159126 Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index 0b111c7b19..19a963b403 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -183,7 +183,7 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window) if (QSG_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled()) { static QTime lastFrameTime = QTime::currentTime(); qCDebug(QSG_RASTER_LOG_TIME_RENDERLOOP, - "Frame rendered with 'basic' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d", + "Frame rendered with 'software' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d", int(swapTime / 1000000), int(polishTime / 1000000), int((syncTime - polishTime) / 1000000), -- cgit v1.2.3 From baead140713f927c132290da0d493cea14358ab8 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sat, 2 Jul 2016 13:23:25 +0200 Subject: Fix tst_QQuickMouseArea::dragging for big startDragDistance On macOS the mouse move events go through qcursor's setPos. That in turn uses a native event, so the waits are there for a good reason. Change-Id: Icb616d303c5280f2a9ba5f78cf07032c298b84a7 Reviewed-by: Shawn Rutledge --- .../quick/qquickmousearea/tst_qquickmousearea.cpp | 29 ++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 6ab7f79ce1..709f32beb2 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -320,7 +320,8 @@ void tst_QQuickMouseArea::dragging() QVERIFY(!drag->active()); - QTest::mousePress(&window, button, 0, QPoint(100,100)); + QPoint p = QPoint(100,100); + QTest::mousePress(&window, button, 0, p); QVERIFY(!drag->active()); QCOMPARE(blackRect->x(), 50.0); @@ -331,18 +332,32 @@ void tst_QQuickMouseArea::dragging() // The item is moved relative to the position of the mouse when the drag // was triggered, this prevents a sudden change in position when the drag // threshold is exceeded. - QTest::mouseMove(&window, QPoint(111,111), 50); - QTest::mouseMove(&window, QPoint(116,116), 50); - QTest::mouseMove(&window, QPoint(122,122), 50); + int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + + // move the minimum distance to activate drag + p += QPoint(dragThreshold + 1, dragThreshold + 1); + QTest::mouseMove(&window, p); + QVERIFY(!drag->active()); + + // from here on move the item + p += QPoint(1, 1); + QTest::mouseMove(&window, p); + QTRY_VERIFY(drag->active()); + // on macOS the cursor movement is going through a native event which + // means that it can actually take some time to show + QTRY_COMPARE(blackRect->x(), 50.0 + 1); + QCOMPARE(blackRect->y(), 50.0 + 1); + + p += QPoint(10, 10); + QTest::mouseMove(&window, p); QTRY_VERIFY(drag->active()); QTRY_COMPARE(blackRect->x(), 61.0); QCOMPARE(blackRect->y(), 61.0); - QTest::mouseRelease(&window, button, 0, QPoint(122,122)); - + QTest::mouseRelease(&window, button, 0, p); QTRY_VERIFY(!drag->active()); - QCOMPARE(blackRect->x(), 61.0); + QTRY_COMPARE(blackRect->x(), 61.0); QCOMPARE(blackRect->y(), 61.0); } -- cgit v1.2.3 From 36d23889bee2d21c02d309dbc81efb20acb0d77f Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sat, 2 Jul 2016 13:33:34 +0200 Subject: Fix tst_QQuickMouseArea::cancelDragging for big startDragDistance Change-Id: Ia39947f7b6f42c48738af81966deb9c98f708da5 Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 709f32beb2..13b1b2016c 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -125,6 +125,9 @@ private slots: void ignoreBySource(); private: + int startDragDistance() const { + return QGuiApplication::styleHints()->startDragDistance(); + } void acceptedButton_data(); void rejectedButton_data(); QTouchDevice *device; @@ -546,15 +549,18 @@ void tst_QQuickMouseArea::cancelDragging() QVERIFY(!drag->active()); - QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100)); + QPoint p = QPoint(100,100); + QTest::mousePress(&window, Qt::LeftButton, 0, p); QVERIFY(!drag->active()); QCOMPARE(blackRect->x(), 50.0); QCOMPARE(blackRect->y(), 50.0); - QTest::mouseMove(&window, QPoint(111,111), 50); - QTest::mouseMove(&window, QPoint(116,116), 50); - QTest::mouseMove(&window, QPoint(122,122), 50); + p += QPoint(startDragDistance() + 1, 0); + QTest::mouseMove(&window, p); + + p += QPoint(11, 11); + QTest::mouseMove(&window, p); QTRY_VERIFY(drag->active()); QTRY_COMPARE(blackRect->x(), 61.0); -- cgit v1.2.3 From 18d81de30df0d9e027acb87f8ed16136aac36a8e Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sat, 2 Jul 2016 13:39:12 +0200 Subject: Fix tst_QQuickMouseArea::setDragOnPressed for big startDragDistance Change-Id: I9f2795bcaaec2b126a41c6a6a12a2d5e55d0beee Reviewed-by: Shawn Rutledge --- .../quick/qquickmousearea/tst_qquickmousearea.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 13b1b2016c..7668a0427a 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -595,7 +595,8 @@ void tst_QQuickMouseArea::setDragOnPressed() QQuickItem *target = mouseArea->findChild("target"); QVERIFY(target); - QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100)); + QPoint p = QPoint(100, 100); + QTest::mousePress(&window, Qt::LeftButton, 0, p); QQuickDrag *drag = mouseArea->drag(); QVERIFY(drag); @@ -607,19 +608,17 @@ void tst_QQuickMouseArea::setDragOnPressed() // First move event triggers drag, second is acted upon. // This is due to possibility of higher stacked area taking precedence. - QTest::mouseMove(&window, QPoint(111,102)); - QTest::qWait(50); - QTest::mouseMove(&window, QPoint(122,122)); - QTest::qWait(50); + p += QPoint(startDragDistance() + 1, 0); + QTest::mouseMove(&window, p); - QVERIFY(drag->active()); - QCOMPARE(target->x(), 61.0); + p += QPoint(11, 0); + QTest::mouseMove(&window, p); + QTRY_VERIFY(drag->active()); + QTRY_COMPARE(target->x(), 61.0); QCOMPARE(target->y(), 50.0); - QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(122,122)); - QTest::qWait(50); - - QVERIFY(!drag->active()); + QTest::mouseRelease(&window, Qt::LeftButton, 0, p); + QTRY_VERIFY(!drag->active()); QCOMPARE(target->x(), 61.0); QCOMPARE(target->y(), 50.0); } -- cgit v1.2.3 From ea5b7477a809b8a210911d62931ccc9b7f347f88 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sat, 2 Jul 2016 13:53:39 +0200 Subject: Fix tst_QQuickMouseArea::preventStealing for big startDragDistance Change-Id: I38c3a663409633bd7020c2630bf11c2e46082435 Reviewed-by: Shawn Rutledge --- .../quick/qquickmousearea/tst_qquickmousearea.cpp | 46 ++++++++++++++-------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 7668a0427a..e53593d18a 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -1011,48 +1011,60 @@ void tst_QQuickMouseArea::preventStealing() QSignalSpy mousePositionSpy(mouseArea, SIGNAL(positionChanged(QQuickMouseEvent*))); - QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(80, 80)); + QPoint p = QPoint(80, 80); + QTest::mousePress(&window, Qt::LeftButton, 0, p); // Without preventStealing, mouse movement over MouseArea would // cause the Flickable to steal mouse and trigger content movement. - QTest::mouseMove(&window,QPoint(69,69)); - QTest::mouseMove(&window,QPoint(58,58)); - QTest::mouseMove(&window,QPoint(47,47)); + p += QPoint(-startDragDistance() * 2, -startDragDistance() * 2); + QTest::mouseMove(&window, p); + p += QPoint(-10, -10); + QTest::mouseMove(&window, p); + p += QPoint(-10, -10); + QTest::mouseMove(&window, p); + p += QPoint(-10, -10); + QTest::mouseMove(&window, p); - // We should have received all three move events - QCOMPARE(mousePositionSpy.count(), 3); + // We should have received all four move events + QTRY_COMPARE(mousePositionSpy.count(), 4); + mousePositionSpy.clear(); QVERIFY(mouseArea->pressed()); // Flickable content should not have moved. QCOMPARE(flickable->contentX(), 0.); QCOMPARE(flickable->contentY(), 0.); - QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(47, 47)); + QTest::mouseRelease(&window, Qt::LeftButton, 0, p); // Now allow stealing and confirm Flickable does its thing. window.rootObject()->setProperty("stealing", false); - QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(80, 80)); + p = QPoint(80, 80); + QTest::mousePress(&window, Qt::LeftButton, 0, p); // Without preventStealing, mouse movement over MouseArea would // cause the Flickable to steal mouse and trigger content movement. - QTest::mouseMove(&window,QPoint(69,69)); - QTest::mouseMove(&window,QPoint(58,58)); - QTest::mouseMove(&window,QPoint(47,47)); + p += QPoint(-startDragDistance() * 2, -startDragDistance() * 2); + QTest::mouseMove(&window, p); + p += QPoint(-10, -10); + QTest::mouseMove(&window, p); + p += QPoint(-10, -10); + QTest::mouseMove(&window, p); + p += QPoint(-10, -10); + QTest::mouseMove(&window, p); // We should only have received the first move event - QCOMPARE(mousePositionSpy.count(), 4); + QTRY_COMPARE(mousePositionSpy.count(), 1); // Our press should be taken away QVERIFY(!mouseArea->pressed()); - // Flickable content should have moved. - - QCOMPARE(flickable->contentX(), 11.); - QCOMPARE(flickable->contentY(), 11.); + // Flickable swallows the first move, then moves 2*10 px + QTRY_COMPARE(flickable->contentX(), 20.); + QCOMPARE(flickable->contentY(), 20.); - QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(50, 50)); + QTest::mouseRelease(&window, Qt::LeftButton, 0, p); } void tst_QQuickMouseArea::clickThrough() -- cgit v1.2.3 From 183eb088de2118f3b47bf95dbca11854e7381dde Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 4 Jul 2016 13:35:22 +0200 Subject: D3D12: Print what goes wrong in rootsig serialization This is not something the debug layer handles so we have to warn ourselves if we want to know what is going on. Change-Id: If412234923a4ce977618d240f4e9dd8890182fd2 Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 844e3bb7cf..001e74b396 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -1671,7 +1671,8 @@ void QSGD3D12EnginePrivate::finalizePipeline(const QSGD3D12PipelineState &pipeli ComPtr signature; ComPtr error; if (FAILED(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error))) { - qWarning("Failed to serialize root signature"); + QByteArray msg(static_cast(error->GetBufferPointer()), error->GetBufferSize()); + qWarning("Failed to serialize root signature: %s", qPrintable(msg)); return; } if (FAILED(device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), -- cgit v1.2.3 From 3e35ea9dcf648482415a6b0c7e968e65502bdd94 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 4 Jul 2016 13:52:08 +0200 Subject: D3D12: Fix descriptor range reference Prevent it from going out of scope. Change-Id: I230937cdb59545e04a7da64a6a68970a2e1f5522 Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 001e74b396..640c2f0d3c 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -1626,17 +1626,17 @@ void QSGD3D12EnginePrivate::finalizePipeline(const QSGD3D12PipelineState &pipeli rootParams[0].Descriptor.RegisterSpace = 0; ++rootParamCount; + D3D12_DESCRIPTOR_RANGE tvDescRange; if (pipelineState.shaders.rootSig.textureViewCount > 0) { rootParams[rootParamCount].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; rootParams[rootParamCount].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; rootParams[rootParamCount].DescriptorTable.NumDescriptorRanges = 1; - D3D12_DESCRIPTOR_RANGE descRange; - descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - descRange.NumDescriptors = pipelineState.shaders.rootSig.textureViewCount; - descRange.BaseShaderRegister = 0; // t0, t1, ... - descRange.RegisterSpace = 0; - descRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - rootParams[rootParamCount].DescriptorTable.pDescriptorRanges = &descRange; + tvDescRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + tvDescRange.NumDescriptors = pipelineState.shaders.rootSig.textureViewCount; + tvDescRange.BaseShaderRegister = 0; // t0, t1, ... + tvDescRange.RegisterSpace = 0; + tvDescRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + rootParams[rootParamCount].DescriptorTable.pDescriptorRanges = &tvDescRange; ++rootParamCount; } -- cgit v1.2.3 From 506ae2dba8270341cbe3ee6435baf472b0d1abed Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Thu, 30 Jun 2016 16:54:13 +0200 Subject: Fix crash in QQuickShaderEffect::updatePaintNode When having a ShaderEffect and an Image sharing the texture via supportsAtlasTextures This used to work fine in 5.4 but 38cab579a0c5398b7621221fd8609bc43cf1f3c5 removed the check for the provider not being null Change-Id: I18cb969dbf8011ea01543cc079214e8ecbb66623 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickshadereffect.cpp | 2 +- .../auto/quick/qquickshadereffect/data/MyIcon.qml | 76 ++++++++++++++++++++++ .../data/twoImagesOneShaderEffect.qml | 61 +++++++++++++++++ .../qquickshadereffect/tst_qquickshadereffect.cpp | 14 ++++ 4 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 tests/auto/quick/qquickshadereffect/data/MyIcon.qml create mode 100644 tests/auto/quick/qquickshadereffect/data/twoImagesOneShaderEffect.qml diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index 94fa7e92e3..690433af4b 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -1054,7 +1054,7 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa bool geometryUsesTextureSubRect = false; if (m_supportsAtlasTextures && material->textureProviders.size() == 1) { QSGTextureProvider *provider = material->textureProviders.at(0); - if (provider->texture()) { + if (provider && provider->texture()) { srcRect = provider->texture()->normalizedTextureSubRect(); geometryUsesTextureSubRect = true; } diff --git a/tests/auto/quick/qquickshadereffect/data/MyIcon.qml b/tests/auto/quick/qquickshadereffect/data/MyIcon.qml new file mode 100644 index 0000000000..b83da321f2 --- /dev/null +++ b/tests/auto/quick/qquickshadereffect/data/MyIcon.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.4 + +Item { + id: root + + property alias source: image.source + property bool shaderActive: false + + implicitWidth: image.width + + Image { + id: image + objectName: "image" + anchors { top: parent.top; bottom: parent.bottom } + sourceSize.height: height + + visible: !shaderActive + } + + ShaderEffect { + id: colorizedImage + + anchors.fill: parent + visible: shaderActive && image.status == Image.Ready + supportsAtlasTextures: true + + property Image source: visible ? image : null + + fragmentShader: " + varying highp vec2 qt_TexCoord0; + uniform sampler2D source; + void main() { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + }" + } +} diff --git a/tests/auto/quick/qquickshadereffect/data/twoImagesOneShaderEffect.qml b/tests/auto/quick/qquickshadereffect/data/twoImagesOneShaderEffect.qml new file mode 100644 index 0000000000..d1292f74b8 --- /dev/null +++ b/tests/auto/quick/qquickshadereffect/data/twoImagesOneShaderEffect.qml @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.4 + +Item { + width: 400 + height: 700 + + MyIcon { + id: icon + + height: 24 + source: "star.png" + shaderActive: true + } + + MyIcon { + anchors.top: icon.bottom + + height: 24 + source: "star.png" + } +} diff --git a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp index c95fef9ef5..2f1d7679c8 100644 --- a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp +++ b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp @@ -81,6 +81,7 @@ private slots: void deleteSourceItem(); void deleteShaderEffectSource(); + void twoImagesOneShaderEffect(); private: enum PresenceFlags { @@ -307,6 +308,19 @@ void tst_qquickshadereffect::deleteShaderEffectSource() delete view; } +void tst_qquickshadereffect::twoImagesOneShaderEffect() +{ + // purely to ensure that deleting the sourceItem of a shader doesn't cause a crash + QQuickView *view = new QQuickView(0); + view->setSource(QUrl::fromLocalFile(testFile("twoImagesOneShaderEffect.qml"))); + view->show(); + QVERIFY(QTest::qWaitForWindowExposed(view)); + QVERIFY(view); + QObject *obj = view->rootObject(); + QVERIFY(obj); + delete view; +} + QTEST_MAIN(tst_qquickshadereffect) #include "tst_qquickshadereffect.moc" -- cgit v1.2.3 From fe4868f03ed498d604f8a17a5643b111fe5358c8 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 5 Jul 2016 10:27:19 +0200 Subject: Fix "inconsistent use of override" warnings If one overriding virtual method is marked as such, all of them should be marked. Change-Id: I29a19d2196a7f1afbb31fba8a4a6f2b345489e3c Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 8 ++++---- src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 9501b3bb3e..e4f9417574 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -2764,10 +2764,10 @@ void Renderer::render() struct RenderNodeState : public QSGRenderNode::RenderState { const QMatrix4x4 *projectionMatrix() const override { return m_projectionMatrix; } - QRect scissorRect() const { return m_scissorRect; } - bool scissorEnabled() const { return m_scissorEnabled; } - int stencilValue() const { return m_stencilValue; } - bool stencilEnabled() const { return m_stencilEnabled; } + QRect scissorRect() const override { return m_scissorRect; } + bool scissorEnabled() const override { return m_scissorEnabled; } + int stencilValue() const override { return m_stencilValue; } + bool stencilEnabled() const override { return m_stencilEnabled; } const QMatrix4x4 *m_projectionMatrix; QRect m_scissorRect; diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h index 941d870c8b..a3f734a7b3 100644 --- a/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h +++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h @@ -65,7 +65,7 @@ class Q_QUICK_PRIVATE_EXPORT QSGSmoothColorMaterial : public QSGMaterial public: QSGSmoothColorMaterial(); - int compare(const QSGMaterial *other) const; + int compare(const QSGMaterial *other) const override; protected: QSGMaterialType *type() const override; -- cgit v1.2.3 From 60a42a266a093e8091b091883ae64f82c375d68b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 5 Jul 2016 10:30:01 +0200 Subject: QSG: prevent in-struct padding This also shrinks the class by a tiny bit, because the second bool fits inside the padding that was added to the first bool. Change-Id: Ifb3f3f165c5e1666e2787118de5b5fb579a573b9 Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index e4f9417574..d45a0ea75d 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -2771,8 +2771,8 @@ struct RenderNodeState : public QSGRenderNode::RenderState const QMatrix4x4 *m_projectionMatrix; QRect m_scissorRect; - bool m_scissorEnabled; int m_stencilValue; + bool m_scissorEnabled; bool m_stencilEnabled; }; -- cgit v1.2.3 From c03d9c49d3367f01491f297e606083d63b1b36ce Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 7 Jun 2016 17:15:07 +0300 Subject: Qml: replace QStringLiteral with QL1S ... or with QL1C in such cases: - if there is overloaded function - in QStringBuilder expressions Saves ~1.5 KB in text size. Build config: ubuntu 16.04 x64, gcc 5.3 Change-Id: Icc0789f1c244ce20a3182494b0c7f35c9d77e41d Reviewed-by: Shawn Rutledge Reviewed-by: Ulf Hermann --- src/qml/compiler/qv4jsir.cpp | 10 +++++----- src/qml/jsruntime/qv4dateobject.cpp | 2 +- src/qml/jsruntime/qv4engine.cpp | 4 ++-- src/qml/jsruntime/qv4globalobject.cpp | 2 +- src/qml/jsruntime/qv4jsonobject.cpp | 35 +++++++++++++++++++---------------- src/qml/jsruntime/qv4object.cpp | 2 +- src/qml/jsruntime/qv4vme_moth.cpp | 14 +++++++------- src/qml/qml/qqmlboundsignal.cpp | 4 ++-- src/qml/qml/qqmlengine.cpp | 2 +- src/qml/qml/qqmlimport.cpp | 6 +++--- src/qml/qml/qqmlpropertycache.cpp | 2 +- src/qml/types/qqmldelegatemodel.cpp | 8 ++++---- 12 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 74c4f91f2b..c6a6c95cd8 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -929,15 +929,15 @@ QString IRPrinter::escape(const QString &s) for (int i = 0; i < s.length(); ++i) { const QChar ch = s.at(i); if (ch == QLatin1Char('\n')) - r += QStringLiteral("\\n"); + r += QLatin1String("\\n"); else if (ch == QLatin1Char('\r')) - r += QStringLiteral("\\r"); + r += QLatin1String("\\r"); else if (ch == QLatin1Char('\\')) - r += QStringLiteral("\\\\"); + r += QLatin1String("\\\\"); else if (ch == QLatin1Char('"')) - r += QStringLiteral("\\\""); + r += QLatin1String("\\\""); else if (ch == QLatin1Char('\'')) - r += QStringLiteral("\\'"); + r += QLatin1String("\\'"); else r += ch; } diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 6acc76cdd8..df648ba9ee 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -565,7 +565,7 @@ static inline QString ToString(double t) { if (std::isnan(t)) return QStringLiteral("Invalid Date"); - QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); + QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT"); double tzoffset = LocalTZA + DaylightSavingTA(t); if (tzoffset) { int hours = static_cast(::fabs(tzoffset) / 1000 / 60 / 60); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 4a26b8b662..fe2d4e6575 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -969,7 +969,7 @@ ReturnedValue ExecutionEngine::throwReferenceError(const Value &value) { Scope scope(this); ScopedString s(scope, value.toString(this)); - QString msg = s->toQString() + QStringLiteral(" is not defined"); + QString msg = s->toQString() + QLatin1String(" is not defined"); ScopedObject error(scope, newReferenceErrorObject(msg)); return throwError(error); } @@ -993,7 +993,7 @@ ReturnedValue ExecutionEngine::throwRangeError(const Value &value) { Scope scope(this); ScopedString s(scope, value.toString(this)); - QString msg = s->toQString() + QStringLiteral(" out of range"); + QString msg = s->toQString() + QLatin1String(" out of range"); ScopedObject error(scope, newRangeErrorObject(msg)); return throwError(error); } diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index d8130c1cac..b88e271a26 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -520,7 +520,7 @@ ReturnedValue GlobalFunctions::method_parseFloat(CallContext *ctx) if (trimmed.startsWith(QLatin1String("Infinity")) || trimmed.startsWith(QLatin1String("+Infinity"))) return Encode(Q_INFINITY); - if (trimmed.startsWith(QStringLiteral("-Infinity"))) + if (trimmed.startsWith(QLatin1String("-Infinity"))) return Encode(-Q_INFINITY); QByteArray ba = trimmed.toLatin1(); bool ok; diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 7661dd1903..5b665d8836 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -643,34 +643,37 @@ struct Stringify static QString quote(const QString &str) { - QString product = QStringLiteral("\""); - for (int i = 0; i < str.length(); ++i) { + QString product; + const int length = str.length(); + product.reserve(length + 2); + product += QLatin1Char('"'); + for (int i = 0; i < length; ++i) { QChar c = str.at(i); switch (c.unicode()) { case '"': - product += QStringLiteral("\\\""); + product += QLatin1String("\\\""); break; case '\\': - product += QStringLiteral("\\\\"); + product += QLatin1String("\\\\"); break; case '\b': - product += QStringLiteral("\\b"); + product += QLatin1String("\\b"); break; case '\f': - product += QStringLiteral("\\f"); + product += QLatin1String("\\f"); break; case '\n': - product += QStringLiteral("\\n"); + product += QLatin1String("\\n"); break; case '\r': - product += QStringLiteral("\\r"); + product += QLatin1String("\\r"); break; case '\t': - product += QStringLiteral("\\t"); + product += QLatin1String("\\t"); break; default: if (c.unicode() <= 0x1f) { - product += QStringLiteral("\\u00"); + product += QLatin1String("\\u00"); product += (c.unicode() > 0xf ? QLatin1Char('1') : QLatin1Char('0')) + QLatin1Char("0123456789abcdef"[c.unicode() & 0xf]); } else { @@ -806,10 +809,10 @@ QString Stringify::JO(Object *o) if (partial.isEmpty()) { result = QStringLiteral("{}"); } else if (gap.isEmpty()) { - result = QStringLiteral("{") + partial.join(QLatin1Char(',')) + QLatin1Char('}'); + result = QLatin1Char('{') + partial.join(QLatin1Char(',')) + QLatin1Char('}'); } else { - QString separator = QStringLiteral(",\n") + indent; - result = QStringLiteral("{\n") + indent + partial.join(separator) + QLatin1Char('\n') + QString separator = QLatin1String(",\n") + indent; + result = QLatin1String("{\n") + indent + partial.join(separator) + QLatin1Char('\n') + stepback + QLatin1Char('}'); } @@ -852,10 +855,10 @@ QString Stringify::JA(ArrayObject *a) if (partial.isEmpty()) { result = QStringLiteral("[]"); } else if (gap.isEmpty()) { - result = QStringLiteral("[") + partial.join(QLatin1Char(',')) + QStringLiteral("]"); + result = QLatin1Char('[') + partial.join(QLatin1Char(',')) + QLatin1Char(']'); } else { - QString separator = QStringLiteral(",\n") + indent; - result = QStringLiteral("[\n") + indent + partial.join(separator) + QStringLiteral("\n") + stepback + QStringLiteral("]"); + QString separator = QLatin1String(",\n") + indent; + result = QLatin1String("[\n") + indent + partial.join(separator) + QLatin1Char('\n') + stepback + QLatin1Char(']'); } indent = stepback; diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 0fc5f95dad..aeb185049f 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -754,7 +754,7 @@ void Object::internalPut(String *name, const Value &value) reject: if (engine()->current->strictMode) { - QString message = QStringLiteral("Cannot assign to read-only property \"") + + QString message = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); engine()->throwTypeError(message); } diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 9ad21305f7..99aa0ef652 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -203,7 +203,7 @@ int qt_v4DebuggerHook(const char *json) QJsonDocument doc = QJsonDocument::fromJson(json); QJsonObject ob = doc.object(); - QByteArray command = ob.value(QStringLiteral("command")).toString().toUtf8(); + QByteArray command = ob.value(QLatin1String("command")).toString().toUtf8(); if (command == "protocolVersion") { return ProtocolVersion; // Version number. @@ -217,17 +217,17 @@ int qt_v4DebuggerHook(const char *json) if (command == "insertBreakpoint") { Breakpoint bp; bp.bpNumber = ++qt_v4BreakpointCount; - bp.lineNumber = ob.value(QStringLiteral("lineNumber")).toString().toInt(); - bp.engineName = ob.value(QStringLiteral("engineName")).toString(); - bp.fullName = ob.value(QStringLiteral("fullName")).toString(); - bp.condition = ob.value(QStringLiteral("condition")).toString(); + bp.lineNumber = ob.value(QLatin1String("lineNumber")).toString().toInt(); + bp.engineName = ob.value(QLatin1String("engineName")).toString(); + bp.fullName = ob.value(QLatin1String("fullName")).toString(); + bp.condition = ob.value(QLatin1String("condition")).toString(); qt_v4Breakpoints.append(bp); return bp.bpNumber; } if (command == "removeBreakpoint") { - int lineNumber = ob.value(QStringLiteral("lineNumber")).toString().toInt(); - QString fullName = ob.value(QStringLiteral("fullName")).toString(); + int lineNumber = ob.value(QLatin1String("lineNumber")).toString().toInt(); + QString fullName = ob.value(QLatin1String("fullName")).toString(); if (qt_v4Breakpoints.last().matches(fullName, lineNumber)) { qt_v4Breakpoints.removeLast(); return Success; diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index ab968065a4..266a5e5366 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -81,7 +81,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, // Add some leading whitespace to account for the binding's column offset. // It's 2 off because a, we start counting at 1 and b, the '(' below is not counted. function.fill(QChar(QChar::Space), qMax(column, (quint16)2) - 2); - function += QStringLiteral("(function ") + handlerName + QLatin1Char('('); + function += QLatin1String("(function ") + handlerName + QLatin1Char('('); if (parameterString.isEmpty()) { QString error; @@ -97,7 +97,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, } else function += parameterString; - function += QStringLiteral(") { ") + expression + QStringLiteral(" })"); + function += QLatin1String(") { ") + expression + QLatin1String(" })"); m_function.set(v4, evalFunction(context(), scopeObject(), function, fileName, line)); if (m_function.isNullOrUndefined()) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index f7191feec5..d391f38b61 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1669,7 +1669,7 @@ void QQmlData::destroyed(QObject *object) QString source = expr->expression(); if (source.size() > 100) { source.truncate(96); - source.append(QStringLiteral(" ...")); + source.append(QLatin1String(" ...")); } locationString.append(source); } else { diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 01f1042de4..9393f74a8d 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -913,7 +913,7 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector &res // To avoid traversing all static plugins for all imports, we cut down // the list the first time called to only contain QML plugins: foreach (const QStaticPlugin &plugin, QPluginLoader::staticPlugins()) { - if (plugin.metaData().value(QStringLiteral("IID")).toString() == QLatin1String(QQmlExtensionInterface_iid)) + if (plugin.metaData().value(QLatin1String("IID")).toString() == QLatin1String(QQmlExtensionInterface_iid)) plugins.append(plugin); } } @@ -921,7 +921,7 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector &res foreach (const QStaticPlugin &plugin, plugins) { // Since a module can list more than one plugin, we keep iterating even after we found a match. if (QQmlExtensionPlugin *instance = qobject_cast(plugin.instance())) { - const QJsonArray metaTagsUriList = plugin.metaData().value(QStringLiteral("uri")).toArray(); + const QJsonArray metaTagsUriList = plugin.metaData().value(QLatin1String("uri")).toArray(); if (metaTagsUriList.isEmpty()) { if (errors) { QQmlError error; @@ -1817,7 +1817,7 @@ void QQmlImportDatabase::addImportPath(const QString& path) } else if (path.startsWith(QLatin1Char(':'))) { // qrc directory, e.g. :/foo // need to convert to a qrc url, e.g. qrc:/foo - cPath = QStringLiteral("qrc") + path; + cPath = QLatin1String("qrc") + path; cPath.replace(Backslash, Slash); } else if (url.isRelative() || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 562e7d1746..313eec8f69 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -622,7 +622,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, setNamedProperty(methodName, ii, data, (old != 0)); if (data->isSignal()) { - QHashedString on(QStringLiteral("on") % methodName.at(0).toUpper() % methodName.midRef(1)); + QHashedString on(QLatin1String("on") % methodName.at(0).toUpper() % methodName.midRef(1)); setNamedProperty(on, ii, sigdata, (old != 0)); ++signalHandlerIndex; } diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 9a1d9e50d1..d6d4553946 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -1654,7 +1654,7 @@ void QQmlDelegateModelItemMetaType::initializeMetaObject() int notifierId = 0; for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); + QString propertyName = QLatin1String("in") + groupNames.at(i); propertyName.replace(2, 1, propertyName.at(2).toUpper()); builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); QMetaPropertyBuilder propertyBuilder = builder.addProperty( @@ -1662,7 +1662,7 @@ void QQmlDelegateModelItemMetaType::initializeMetaObject() propertyBuilder.setWritable(true); } for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + const QString propertyName = groupNames.at(i) + QLatin1String("Index"); builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); QMetaPropertyBuilder propertyBuilder = builder.addProperty( propertyName.toUtf8(), "int", notifierId); @@ -1710,7 +1710,7 @@ void QQmlDelegateModelItemMetaType::initializePrototype() proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); for (int i = 2; i < groupNames.count(); ++i) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); + QString propertyName = QLatin1String("in") + groupNames.at(i); propertyName.replace(2, 1, propertyName.at(2).toUpper()); s = v4->newString(propertyName); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_member))); @@ -1718,7 +1718,7 @@ void QQmlDelegateModelItemMetaType::initializePrototype() proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } for (int i = 2; i < groupNames.count(); ++i) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + const QString propertyName = groupNames.at(i) + QLatin1String("Index"); s = v4->newString(propertyName); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_index))); p->setSetter(0); -- cgit v1.2.3 From dffe4ab249ced63255e83942be447df4183bf2f2 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Tue, 5 Jul 2016 17:16:44 +0200 Subject: demos: Fix installation of photoviewer example and add to build deployment.pri seems suspicious (and qmake errors about it actually), so just use the example install path and trust that it will be okay. Change-Id: Id3b6abd80f84485e006473130873ab89ee11c0d4 Reviewed-by: Oswald Buddenhagen --- examples/quick/demos/demos.pro | 3 +-- examples/quick/demos/photoviewer/deployment.pri | 23 ----------------------- examples/quick/demos/photoviewer/photoviewer.pro | 7 ++----- 3 files changed, 3 insertions(+), 30 deletions(-) delete mode 100644 examples/quick/demos/photoviewer/deployment.pri diff --git a/examples/quick/demos/demos.pro b/examples/quick/demos/demos.pro index 9aac7bf6f7..e6937683ab 100644 --- a/examples/quick/demos/demos.pro +++ b/examples/quick/demos/demos.pro @@ -5,9 +5,8 @@ SUBDIRS = samegame \ tweetsearch \ maroon \ photosurface \ + photoviewer \ stocqt qtHaveModule(xmlpatterns): SUBDIRS += rssnews -EXAMPLE_FILES = \ - photoviewer diff --git a/examples/quick/demos/photoviewer/deployment.pri b/examples/quick/demos/photoviewer/deployment.pri deleted file mode 100644 index 839b3b5cac..0000000000 --- a/examples/quick/demos/photoviewer/deployment.pri +++ /dev/null @@ -1,23 +0,0 @@ -android-no-sdk { - target.path = /data/user/qt - export(target.path) - INSTALLS += target -} else:android { - x86 { - target.path = /libs/x86 - } else: armeabi-v7a { - target.path = /libs/armeabi-v7a - } else { - target.path = /libs/armeabi - } - export(target.path) - INSTALLS += target -} else:unix { - isEmpty(target.path) { - target.path = /opt/$${TARGET}/bin - export(target.path) - } - INSTALLS += target -} - -export(INSTALLS) diff --git a/examples/quick/demos/photoviewer/photoviewer.pro b/examples/quick/demos/photoviewer/photoviewer.pro index 704e2ce003..4bfdb86f31 100644 --- a/examples/quick/demos/photoviewer/photoviewer.pro +++ b/examples/quick/demos/photoviewer/photoviewer.pro @@ -15,8 +15,5 @@ TRANSLATIONS += i18n/qml_fr.ts \ RESOURCES += qml.qrc -# Additional import path used to resolve QML modules in Qt Creator's code model -QML_IMPORT_PATH = - -# Default rules for deployment. -include(deployment.pri) +target.path = $$[QT_INSTALL_EXAMPLES]/quick/demos/photoviewer +INSTALLS += target -- cgit v1.2.3 From fb6c9b83e8a025d12224fc8c847a08c425ae1e9b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 5 Jul 2016 11:25:21 +0200 Subject: QQuickMouseEvent and QQuickWheelEvent: default init in constructor c9389dbd7facaa424dc175e5f5dac514630eef removed the constructor params but also removed the initialization, which rightly resulted in warnings from Coverity. Change-Id: Ife72d9e6075368bdf39b945551a460214a2fd036 Reviewed-by: Mitch Curtis --- src/quick/items/qquickevents_p_p.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 6d4b49b3cd..39c5a060b5 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -121,7 +121,10 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMouseEvent : public QObject Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) public: - QQuickMouseEvent() {} + QQuickMouseEvent() + : _x(0), _y(0), _button(Qt::NoButton), _buttons(Qt::NoButton), _modifiers(Qt::NoModifier) + , _source(Qt::MouseEventNotSynthesized), _wasHeld(false), _isClick(false), _accepted(false) + {} void reset(qreal x, qreal y, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, bool isClick = false, bool wasHeld = false) @@ -180,7 +183,10 @@ class QQuickWheelEvent : public QObject Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) public: - QQuickWheelEvent() {} + QQuickWheelEvent() + : _x(0), _y(0), _buttons(Qt::NoButton), _modifiers(Qt::NoModifier) + , _inverted(false), _accepted(false) + {} void reset(qreal x, qreal y, const QPoint &angleDelta, const QPoint &pixelDelta, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, bool inverted) -- cgit v1.2.3 From efbeb7d7917fa1db3730fea51a3e83435d96e3f2 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sat, 2 Jul 2016 14:16:34 +0200 Subject: Fix tests in tst_QQuickMouseArea for big startDragDistance Change-Id: I8610887e7d1d3b0c6bd9ab60e55ce1e9209bcae7 Reviewed-by: Shawn Rutledge --- .../quick/qquickmousearea/tst_qquickmousearea.cpp | 101 ++++++++++++--------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index e53593d18a..bb3c93720c 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -1340,9 +1340,8 @@ void tst_QQuickMouseArea::disableAfterPress() QCOMPARE(blackRect, drag->target()); QVERIFY(!drag->active()); - - QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100)); - + QPoint p = QPoint(100,100); + QTest::mousePress(&window, Qt::LeftButton, 0, p); QTRY_COMPARE(mousePressSpy.count(), 1); QVERIFY(!drag->active()); @@ -1352,22 +1351,24 @@ void tst_QQuickMouseArea::disableAfterPress() // First move event triggers drag, second is acted upon. // This is due to possibility of higher stacked area taking precedence. - QTest::mouseMove(&window, QPoint(111,111)); - QTest::qWait(50); - QTest::mouseMove(&window, QPoint(122,122)); + p += QPoint(startDragDistance() + 1, 0); + QTest::mouseMove(&window, p); + p += QPoint(11, 11); + QTest::mouseMove(&window, p); QTRY_COMPARE(mousePositionSpy.count(), 2); - QVERIFY(drag->active()); - QCOMPARE(blackRect->x(), 61.0); + QTRY_VERIFY(drag->active()); + QTRY_COMPARE(blackRect->x(), 61.0); QCOMPARE(blackRect->y(), 61.0); mouseArea->setEnabled(false); // move should still be acted upon - QTest::mouseMove(&window, QPoint(133,133)); - QTest::qWait(50); - QTest::mouseMove(&window, QPoint(144,144)); + p += QPoint(11, 11); + QTest::mouseMove(&window, p); + p += QPoint(11, 11); + QTest::mouseMove(&window, p); QTRY_COMPARE(mousePositionSpy.count(), 4); @@ -1378,7 +1379,7 @@ void tst_QQuickMouseArea::disableAfterPress() QVERIFY(mouseArea->pressed()); QVERIFY(mouseArea->hovered()); - QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(144,144)); + QTest::mouseRelease(&window, Qt::LeftButton, 0, p); QTRY_COMPARE(mouseReleaseSpy.count(), 1); @@ -1635,36 +1636,39 @@ void tst_QQuickMouseArea::changeAxis() QVERIFY(!drag->active()); // Start a diagonal drag - QTest::mousePress(&view, Qt::LeftButton, 0, QPoint(100, 100)); + QPoint p = QPoint(100, 100); + QTest::mousePress(&view, Qt::LeftButton, 0, p); QVERIFY(!drag->active()); QCOMPARE(blackRect->x(), 50.0); QCOMPARE(blackRect->y(), 50.0); - QTest::mouseMove(&view, QPoint(111, 111)); - QTest::qWait(50); - QTest::mouseMove(&view, QPoint(122, 122)); - + p += QPoint(startDragDistance() + 1, startDragDistance() + 1); + QTest::mouseMove(&view, p); + p += QPoint(11, 11); + QTest::mouseMove(&view, p); QTRY_VERIFY(drag->active()); - QCOMPARE(blackRect->x(), 61.0); + QTRY_COMPARE(blackRect->x(), 61.0); QCOMPARE(blackRect->y(), 61.0); QCOMPARE(drag->axis(), QQuickDrag::XAndYAxis); /* When blackRect.x becomes bigger than 75, the drag axis is changed to * Drag.YAxis by the QML code. Verify that this happens, and that the drag * movement is effectively constrained to the Y axis. */ - QTest::mouseMove(&view, QPoint(144, 144)); + p += QPoint(22, 22); + QTest::mouseMove(&view, p); QTRY_COMPARE(blackRect->x(), 83.0); QTRY_COMPARE(blackRect->y(), 83.0); QTRY_COMPARE(drag->axis(), QQuickDrag::YAxis); - QTest::mouseMove(&view, QPoint(155, 155)); + p += QPoint(11, 11); + QTest::mouseMove(&view, p); QTRY_COMPARE(blackRect->y(), 94.0); QCOMPARE(blackRect->x(), 83.0); - QTest::mouseRelease(&view, Qt::LeftButton, 0, QPoint(155, 155)); + QTest::mouseRelease(&view, Qt::LeftButton, 0, p); QTRY_VERIFY(!drag->active()); QCOMPARE(blackRect->x(), 83.0); @@ -1870,33 +1874,41 @@ void tst_QQuickMouseArea::ignoreBySource() QVERIFY(flickable); // MouseArea should grab the press because it's interested in non-synthesized mouse events - QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(80, 80)); + QPoint p = QPoint(80, 80); + QTest::mousePress(&window, Qt::LeftButton, 0, p); QVERIFY(window.mouseGrabberItem() == mouseArea); // That was a real mouse event QVERIFY(root->property("lastEventSource").toInt() == Qt::MouseEventNotSynthesized); // Flickable content should not move - QTest::mouseMove(&window,QPoint(69,69)); - QTest::mouseMove(&window,QPoint(58,58)); - QTest::mouseMove(&window,QPoint(47,47)); + p -= QPoint(startDragDistance() + 1, startDragDistance() + 1); + QTest::mouseMove(&window, p); + p -= QPoint(11, 11); + QTest::mouseMove(&window, p); + p -= QPoint(11, 11); + QTest::mouseMove(&window, p); QCOMPARE(flickable->contentX(), 0.); QCOMPARE(flickable->contentY(), 0.); - QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(47, 47)); + QTest::mouseRelease(&window, Qt::LeftButton, 0, p); // Now try touch events and confirm that MouseArea ignores them, while Flickable does its thing - - QTest::touchEvent(&window, device).press(0, QPoint(80, 80), &window); + p = QPoint(80, 80); + QTest::touchEvent(&window, device).press(0, p, &window); QQuickTouchUtils::flush(&window); QVERIFY(window.mouseGrabberItem() != mouseArea); // That was a fake mouse event QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventSynthesizedByQt)); - QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window); - QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window); - QTest::touchEvent(&window, device).move(0, QPoint(47,47), &window); + p -= QPoint(startDragDistance() + 1, startDragDistance() + 1); + QTest::touchEvent(&window, device).move(0, p, &window); + p -= QPoint(11, 11); + QTest::touchEvent(&window, device).move(0, p, &window); + p -= QPoint(11, 11); + QTest::touchEvent(&window, device).move(0, p, &window); + QQuickTouchUtils::flush(&window); QCOMPARE(window.mouseGrabberItem(), flickable); - QTest::touchEvent(&window, device).release(0, QPoint(47,47), &window); + QTest::touchEvent(&window, device).release(0, p, &window); QQuickTouchUtils::flush(&window); // Flickable content should have moved @@ -1911,15 +1923,19 @@ void tst_QQuickMouseArea::ignoreBySource() // MouseArea should ignore the press because it's interested in synthesized mouse events - QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(80, 80)); + p = QPoint(80, 80); + QTest::mousePress(&window, Qt::LeftButton, 0, p); QVERIFY(window.mouseGrabberItem() != mouseArea); // That was a real mouse event QVERIFY(root->property("lastEventSource").toInt() == Qt::MouseEventNotSynthesized); // Flickable content should move - QTest::mouseMove(&window,QPoint(69,69)); - QTest::mouseMove(&window,QPoint(58,58)); - QTest::mouseMove(&window,QPoint(47,47)); + p -= QPoint(startDragDistance() + 1, startDragDistance() + 1); + QTest::mouseMove(&window, p); + p -= QPoint(11, 11); + QTest::mouseMove(&window, p); + p -= QPoint(11, 11); + QTest::mouseMove(&window, p); QTRY_VERIFY(flickable->contentX() > 1); QVERIFY(flickable->contentY() > 1); @@ -1928,13 +1944,16 @@ void tst_QQuickMouseArea::ignoreBySource() flickable->setContentY(0); // Now try touch events and confirm that MouseArea gets them, while Flickable doesn't - - QTest::touchEvent(&window, device).press(0, QPoint(80, 80), &window); + p = QPoint(80, 80); + QTest::touchEvent(&window, device).press(0, p, &window); QQuickTouchUtils::flush(&window); QCOMPARE(window.mouseGrabberItem(), mouseArea); - QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window); - QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window); - QTest::touchEvent(&window, device).move(0, QPoint(47,47), &window); + p -= QPoint(startDragDistance() + 1, startDragDistance() + 1); + QTest::touchEvent(&window, device).move(0, p, &window); + p -= QPoint(11, 11); + QTest::touchEvent(&window, device).move(0, p, &window); + p -= QPoint(11, 11); + QTest::touchEvent(&window, device).move(0, p, &window); QQuickTouchUtils::flush(&window); QCOMPARE(window.mouseGrabberItem(), mouseArea); QTest::touchEvent(&window, device).release(0, QPoint(47,47), &window); -- cgit v1.2.3 From e98251991249f2ad6654a0c63eea30069ac84aed Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 4 Jul 2016 21:26:11 +0200 Subject: Move QQuickItem::UpdatePaintNodeData ctor to qquickitem.cpp The struct lives in Qt Quick Item. Change-Id: Ieeff6bdac1875f9cb844788db494d149e502070b Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 5 +++++ src/quick/items/qquickwindow.cpp | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 9f6574f6c7..7f7ba2222b 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3816,6 +3816,11 @@ QSGNode *QQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *upda return 0; } +QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData() +: transformNode(0) +{ +} + /*! This function is called when an item should release graphics resources which are not already managed by the nodes returend from diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 8ad9711fc4..a34dd488a8 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -203,11 +203,6 @@ thus the first item that has focus will get it (assuming the scope doesn't alrea have a scope focused item), and the other items will have their focus cleared. */ -QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData() -: transformNode(0) -{ -} - QQuickRootItem::QQuickRootItem() { } -- cgit v1.2.3 From 249eec90ad109ca7f81f7858ffc593d0ed27fde7 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 4 Jul 2016 22:48:03 +0200 Subject: Merge mouse handling functions In order to clean up the pointer input handling in Qt Quick, handle all mouse events in one place. Keep the functions in QQuickWindow to allow overriding them. Change-Id: Id413d440a5ea1a77cc7b9b135790e776a7c554bf Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 201 +++++++++++++++++++-------------------- src/quick/items/qquickwindow_p.h | 1 + 2 files changed, 99 insertions(+), 103 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index a34dd488a8..68fe3c1e97 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1669,71 +1669,6 @@ bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) return false; } -/*! \reimp */ -void QQuickWindow::mousePressEvent(QMouseEvent *event) -{ - Q_D(QQuickWindow); - Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(), - event->buttons()); - - if (event->source() == Qt::MouseEventSynthesizedBySystem) { - event->accept(); - return; - } - - qCDebug(DBG_MOUSE) << "QQuickWindow::mousePressEvent()" << event->localPos() << event->button() << event->buttons(); - d->deliverMouseEvent(event); -} - -/*! \reimp */ -void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QQuickWindow); - Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(), - event->buttons()); - - if (event->source() == Qt::MouseEventSynthesizedBySystem) { - event->accept(); - return; - } - - qCDebug(DBG_MOUSE) << "QQuickWindow::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons() << "grabber:" << d->mouseGrabberItem; - - if (!d->mouseGrabberItem) { - QWindow::mouseReleaseEvent(event); - return; - } - - d->deliverMouseEvent(event); - if (d->mouseGrabberItem && !event->buttons()) - d->mouseGrabberItem->ungrabMouse(); -} - -/*! \reimp */ -void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event) -{ - Q_D(QQuickWindow); - Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, - event->button(), event->buttons()); - - if (event->source() == Qt::MouseEventSynthesizedBySystem) { - event->accept(); - return; - } - - qCDebug(DBG_MOUSE) << "QQuickWindow::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons(); - - if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) { - if (d->deliverInitialMousePressEvent(d->contentItem, event)) - event->accept(); - else - event->ignore(); - return; - } - - d->deliverMouseEvent(event); -} - bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool accepted) @@ -1755,44 +1690,6 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, return hoverEvent.isAccepted(); } -/*! \reimp */ -void QQuickWindow::mouseMoveEvent(QMouseEvent *event) -{ - Q_D(QQuickWindow); - Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, - event->localPos().x(), event->localPos().y()); - - if (event->source() == Qt::MouseEventSynthesizedBySystem) { - event->accept(); - return; - } - - qCDebug(DBG_MOUSE) << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons(); - qCDebug(DBG_HOVER_TRACE) << this; - -#ifndef QT_NO_CURSOR - d->updateCursor(event->windowPos()); -#endif - - if (!d->mouseGrabberItem) { - if (d->lastMousePosition.isNull()) - d->lastMousePosition = event->windowPos(); - QPointF last = d->lastMousePosition; - d->lastMousePosition = event->windowPos(); - - bool accepted = event->isAccepted(); - bool delivered = d->deliverHoverEvent(d->contentItem, event->windowPos(), last, event->modifiers(), accepted); - if (!delivered) { - //take care of any exits - accepted = d->clearHover(); - } - event->setAccepted(accepted); - return; - } - - d->deliverMouseEvent(event); -} - bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted) { @@ -2070,6 +1967,104 @@ void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) } } +/*! \reimp */ +void QQuickWindow::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickWindow); + d->handleMouseEvent(event); +} +/*! \reimp */ +void QQuickWindow::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickWindow); + d->handleMouseEvent(event); +} +/*! \reimp */ +void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QQuickWindow); + d->handleMouseEvent(event); +} +/*! \reimp */ +void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickWindow); + d->handleMouseEvent(event); +} + +void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) +{ + if (event->source() == Qt::MouseEventSynthesizedBySystem) { + event->accept(); + return; + } + qCDebug(DBG_MOUSE) << "QQuickWindow::handleMouseEvent()" << event->type() << event->localPos() << event->button() << event->buttons(); + + switch (event->type()) { + case QEvent::MouseButtonPress: + Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(), + event->buttons()); + deliverMouseEvent(event); + break; + case QEvent::MouseButtonRelease: + Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(), + event->buttons()); + if (!mouseGrabberItem) { + event->ignore(); + return; + } + + deliverMouseEvent(event); + if (mouseGrabberItem && !event->buttons()) + mouseGrabberItem->ungrabMouse(); + break; + case QEvent::MouseButtonDblClick: + Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, + event->button(), event->buttons()); + + if (!mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) { + if (deliverInitialMousePressEvent(contentItem, event)) + event->accept(); + else + event->ignore(); + return; + } + + deliverMouseEvent(event); + break; + case QEvent::MouseMove: + Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, + event->localPos().x(), event->localPos().y()); + + qCDebug(DBG_HOVER_TRACE) << this; + + #ifndef QT_NO_CURSOR + updateCursor(event->windowPos()); + #endif + + if (!mouseGrabberItem) { + if (lastMousePosition.isNull()) + lastMousePosition = event->windowPos(); + QPointF last = lastMousePosition; + lastMousePosition = event->windowPos(); + + bool accepted = event->isAccepted(); + bool delivered = deliverHoverEvent(contentItem, event->windowPos(), last, event->modifiers(), accepted); + if (!delivered) { + //take care of any exits + accepted = clearHover(); + } + event->setAccepted(accepted); + return; + } + deliverMouseEvent(event); + break; + default: + Q_ASSERT(false); + break; + } +} + void QQuickWindowPrivate::flushFrameSynchronousEvents() { if (delayedTouch) { diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 3737f9efb8..9a5a13d142 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -158,6 +158,7 @@ public: bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList &, QSet *, QHash > *, QSet *filtered); void handleTouchEvent(QTouchEvent *); + void handleMouseEvent(QMouseEvent *); void deliverTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); void deliverDelayedTouchEvent(); -- cgit v1.2.3 From e5892dcb7b6f2419ba73268de7e080770ddc9b18 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 5 Jul 2016 15:30:33 +0200 Subject: Remove invalid TouchPoint screen coordinate setting The points would get their screen position set to the scene position which doesn't make any sense. It has been there for a long time and probably was never found since on phones this is a valid assumption. Found by Shawn Rutledge. Change-Id: Ie5bd33953e099af9d96359ae817a0f98212bb126 Reviewed-by: Robin Burchell --- src/quick/items/qquickwindow.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 68fe3c1e97..a6bed82571 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -809,10 +809,6 @@ void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent) for (int i = 0; i < touchPoints.count(); ++i) { QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; - touchPoint.setScreenRect(touchPoint.sceneRect()); - touchPoint.setStartScreenPos(touchPoint.startScenePos()); - touchPoint.setLastScreenPos(touchPoint.lastScenePos()); - touchPoint.setSceneRect(touchPoint.rect()); touchPoint.setStartScenePos(touchPoint.startPos()); touchPoint.setLastScenePos(touchPoint.lastPos()); -- cgit v1.2.3 From bd8cf4e04062c2d844628f6b5167888b8f342f8a Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 24 May 2016 16:22:33 +0200 Subject: QmlProfiler: Extend test to check for timestamp ordering Timestamps should always increase monotonically. Change-Id: Idfd44e160193aab7bce37b58bc42a94ff087d15e Reviewed-by: Simon Hausmann --- .../tst_qqmlprofilerservice.cpp | 23 +++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index c1de5ff594..52c9953970 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -64,7 +64,8 @@ class QQmlProfilerTestClient : public QQmlProfilerClient Q_OBJECT public: - QQmlProfilerTestClient(QQmlDebugConnection *connection) : QQmlProfilerClient(connection) {} + QQmlProfilerTestClient(QQmlDebugConnection *connection) : QQmlProfilerClient(connection), + lastTimestamp(-1) {} QVector qmlMessages; QVector javascriptMessages; @@ -72,6 +73,8 @@ public: QVector asynchronousMessages; QVector pixmapMessages; + qint64 lastTimestamp; + signals: void recordingFinished(); @@ -114,6 +117,8 @@ void QQmlProfilerTestClient::traceFinished(qint64 time, int engineId) void QQmlProfilerTestClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime) { QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); + QVERIFY(lastTimestamp <= startTime); + lastTimestamp = startTime; QQmlProfilerData data(startTime, QQmlProfilerDefinitions::RangeStart, type); if (type == QQmlProfilerDefinitions::Javascript) javascriptMessages.append(data); @@ -125,6 +130,8 @@ void QQmlProfilerTestClient::rangeData(QQmlProfilerDefinitions::RangeType type, const QString &string) { QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeData, type, string); if (type == QQmlProfilerDefinitions::Javascript) javascriptMessages.append(data); @@ -137,6 +144,8 @@ void QQmlProfilerTestClient::rangeLocation(QQmlProfilerDefinitions::RangeType ty { QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); QVERIFY(location.line >= -2); + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeLocation, type, location.filename); data.line = location.line; data.column = location.column; @@ -149,6 +158,8 @@ void QQmlProfilerTestClient::rangeLocation(QQmlProfilerDefinitions::RangeType ty void QQmlProfilerTestClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime) { QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); + QVERIFY(lastTimestamp <= endTime); + lastTimestamp = endTime; QQmlProfilerData data(endTime, QQmlProfilerDefinitions::RangeEnd, type); if (type == QQmlProfilerDefinitions::Javascript) javascriptMessages.append(data); @@ -161,6 +172,8 @@ void QQmlProfilerTestClient::animationFrame(qint64 time, int frameRate, int anim QVERIFY(threadId >= 0); QVERIFY(frameRate != -1); QVERIFY(animationCount != -1); + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; QQmlProfilerData data(time, QQmlProfilerDefinitions::Event, QQmlProfilerDefinitions::AnimationFrame); data.framerate = frameRate; @@ -178,6 +191,8 @@ void QQmlProfilerTestClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraph Q_UNUSED(numericData3); Q_UNUSED(numericData4); Q_UNUSED(numericData5); + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::SceneGraphFrame, type)); } @@ -186,6 +201,8 @@ void QQmlProfilerTestClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEve qint64 time, const QString &url, int numericData1, int numericData2) { + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; QQmlProfilerData data(time, QQmlProfilerDefinitions::PixmapCacheEvent, type, url); switch (type) { case QQmlProfilerDefinitions::PixmapSizeKnown: @@ -205,6 +222,8 @@ void QQmlProfilerTestClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEve void QQmlProfilerTestClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 amount) { + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; QQmlProfilerData data(time, QQmlProfilerDefinitions::MemoryAllocation, type); data.amount = amount; jsHeapMessages.append(data); @@ -213,6 +232,8 @@ void QQmlProfilerTestClient::memoryAllocation(QQmlProfilerDefinitions::MemoryTyp void QQmlProfilerTestClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, int b) { + QVERIFY(lastTimestamp <= time); + lastTimestamp = time; qmlMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, type, QString::number(a) + QLatin1Char('x') + QString::number(b))); -- cgit v1.2.3 From 0d7dd44d781a73c4bd065c0660d4a2f824a86f05 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 5 Jul 2016 10:35:56 +0200 Subject: QML: Clarify a parameter's usage in QQmlPropertyCache Give the template type a fitting name, use a parameter name that is slightly more descriptive than "dummy", and pass it around by pointer like we do in other places. Change-Id: Ie7051a6053fc8d2f9c3ea92ee3ce18e57dc5e62a Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4qobjectwrapper.cpp | 19 ++++++++++--------- src/qml/qml/qqmlboundsignal.cpp | 4 ++-- src/qml/qml/qqmlpropertycache.cpp | 23 +++++++++++++++-------- src/qml/qml/qqmlpropertycache_p.h | 10 +++++++--- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index cf99b425a2..a2da7d6d89 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -797,8 +797,8 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase if (!v4) break; - QVarLengthArray dummy; - int *argsTypes = QQmlMetaObject(r).methodParameterTypes(This->signalIndex, dummy, 0); + QQmlMetaObject::ArgTypeStorage storage; + int *argsTypes = QQmlMetaObject(r).methodParameterTypes(This->signalIndex, &storage, 0); int argCount = argsTypes ? argsTypes[0]:0; @@ -1372,12 +1372,13 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ if (data.hasArguments()) { int *args = 0; - QVarLengthArray dummy; + QQmlMetaObject::ArgTypeStorage storage; if (data.isConstructor()) - args = static_cast(object).constructorParameterTypes(data.coreIndex, dummy, &unknownTypeError); + args = static_cast(object).constructorParameterTypes( + data.coreIndex, &storage, &unknownTypeError); else - args = object.methodParameterTypes(data.coreIndex, dummy, &unknownTypeError); + args = object.methodParameterTypes(data.coreIndex, &storage, &unknownTypeError); if (!args) { QString typeName = QString::fromLatin1(unknownTypeError); @@ -1429,11 +1430,11 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QV4::ScopedValue v(scope); do { - QVarLengthArray dummy; + QQmlMetaObject::ArgTypeStorage storage; int methodArgumentCount = 0; int *methodArgTypes = 0; if (attempt->hasArguments()) { - int *args = object.methodParameterTypes(attempt->coreIndex, dummy, 0); + int *args = object.methodParameterTypes(attempt->coreIndex, &storage, 0); if (!args) // Must be an unknown argument continue; @@ -2001,11 +2002,11 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine for (int i = 0; i < numberOfConstructors; i++) { const QQmlPropertyData & attempt = d()->constructors.at(i); - QVarLengthArray dummy; int methodArgumentCount = 0; int *methodArgTypes = 0; if (attempt.hasArguments()) { - int *args = object.constructorParameterTypes(attempt.coreIndex, dummy, 0); + QQmlMetaObject::ArgTypeStorage storage; + int *args = object.constructorParameterTypes(attempt.coreIndex, &storage, 0); if (!args) // Must be an unknown argument continue; diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 266a5e5366..ef837183db 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -206,10 +206,10 @@ void QQmlBoundSignalExpression::evaluate(void **a) ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. - QVarLengthArray dummy; + QQmlMetaObject::ArgTypeStorage storage; //TODO: lookup via signal index rather than method index as an optimization int methodIndex = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).methodIndex(); - int *argsTypes = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, dummy, 0); + int *argsTypes = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, &storage, 0); int argCount = argsTypes ? *argsTypes : 0; QV4::ScopedCallData callData(scope, argCount); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 313eec8f69..84081e707d 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -1463,7 +1463,8 @@ int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *u return type; } -int *QQmlMetaObject::methodParameterTypes(int index, QVarLengthArray &dummy, QByteArray *unknownTypeError) const +int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const { Q_ASSERT(!_m.isNull() && index >= 0); @@ -1518,15 +1519,19 @@ int *QQmlMetaObject::methodParameterTypes(int index, QVarLengthArray &du } else { QMetaMethod m = _m.asT2()->method(index); - return methodParameterTypes(m, dummy, unknownTypeError); + return methodParameterTypes(m, argStorage, unknownTypeError); } } -int* QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, QVarLengthArray &dummy, QByteArray *unknownTypeError) const { +int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const +{ + Q_ASSERT(argStorage); + int argc = m.parameterCount(); - dummy.resize(argc + 1); - dummy[0] = argc; + argStorage->resize(argc + 1); + argStorage->operator[](0) = argc; QList argTypeNames; // Only loaded if needed for (int ii = 0; ii < argc; ++ii) { @@ -1546,10 +1551,10 @@ int* QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, QVarLengthArray< if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); return 0; } - dummy[ii + 1] = type; + argStorage->operator[](ii + 1) = type; } - return dummy.data(); + return argStorage->data(); } void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const @@ -1568,7 +1573,9 @@ void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv } } -int* QQmlStaticMetaObject::constructorParameterTypes(int index, QVarLengthArray &dummy, QByteArray *unknownTypeError) const { +int *QQmlStaticMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy, + QByteArray *unknownTypeError) const +{ QMetaMethod m = _m.asT2()->constructor(index); return methodParameterTypes(m, dummy, unknownTypeError); } diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 6b2e0d6d9b..26fa533103 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -410,6 +410,8 @@ class QQmlEnginePrivate; class Q_QML_EXPORT QQmlMetaObject { public: + typedef QVarLengthArray ArgTypeStorage; + inline QQmlMetaObject(); inline QQmlMetaObject(QObject *); inline QQmlMetaObject(const QMetaObject *); @@ -429,7 +431,8 @@ public: QQmlPropertyCache *propertyCache(QQmlEnginePrivate *) const; int methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const; - int *methodParameterTypes(int index, QVarLengthArray &dummy, QByteArray *unknownTypeError) const; + int *methodParameterTypes(int index, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const; static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to); @@ -439,7 +442,8 @@ public: protected: QBiPointer _m; - int *methodParameterTypes(const QMetaMethod &method, QVarLengthArray &dummy, QByteArray *unknownTypeError) const; + int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const; }; @@ -472,7 +476,7 @@ public: QQmlStaticMetaObject(const QMetaObject* metaObject) : QQmlObjectOrGadget(metaObject) {} - int *constructorParameterTypes(int index, QVarLengthArray &dummy, QByteArray *unknownTypeError) const; + int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const; }; QQmlPropertyData::QQmlPropertyData() -- cgit v1.2.3 From ae87e1034d186798c092e24ee13930a3ce245363 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 5 Jul 2016 21:14:55 +0200 Subject: Micro optimize QQuickItem::contains Constructing a QRectF in order to check the conditions is overkill and the comparison inside QRectF is much more complicated than needed, it takes for example negative width into account. Change-Id: I4754ec1fb8c9fe3e730358427233ebfe3e53bea6 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 7f7ba2222b..27acf38faa 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -7359,7 +7359,9 @@ void QQuickItem::setKeepTouchGrab(bool keep) bool QQuickItem::contains(const QPointF &point) const { Q_D(const QQuickItem); - return QRectF(0, 0, d->width, d->height).contains(point); + qreal x = point.x(); + qreal y = point.y(); + return x >= 0 && y >= 0 && x <= d->width && y <= d->height; } /*! -- cgit v1.2.3 From 00a85fb5c9eebe8458d0f9c9be7745c5e26fa9b4 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 5 Jul 2016 16:35:39 +0200 Subject: Move touch event compression into a function This makes it easier to understand what's going on. Change-Id: I7846178d791b12fc3b78a5790419fe7fb32601f2 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 124 ++++++++++++++++++++------------------- src/quick/items/qquickwindow_p.h | 1 + 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index a6bed82571..cf23aa1367 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1884,6 +1884,70 @@ void QQuickWindowPrivate::deliverDelayedTouchEvent() static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION"); +bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event) +{ + Q_Q(QQuickWindow); + Qt::TouchPointStates states = event->touchPointStates(); + if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) == 0) + || ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) != 0)) { + // we can only compress something that isn't a press or release + return false; + } + + if (!delayedTouch) { + delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); + delayedTouch->setTimestamp(event->timestamp()); + if (renderControl) + QQuickRenderControlPrivate::get(renderControl)->maybeUpdate(); + else if (windowManager) + windowManager->maybeUpdate(q); + return true; + } + + // check if this looks like the last touch event + if (delayedTouch->type() == event->type() && + delayedTouch->device() == event->device() && + delayedTouch->modifiers() == event->modifiers() && + delayedTouch->touchPoints().count() == event->touchPoints().count()) + { + // possible match.. is it really the same? + bool mismatch = false; + + QList tpts = event->touchPoints(); + Qt::TouchPointStates states; + for (int i = 0; i < event->touchPoints().count(); ++i) { + const QTouchEvent::TouchPoint &tp = tpts.at(i); + const QTouchEvent::TouchPoint &tpDelayed = delayedTouch->touchPoints().at(i); + if (tp.id() != tpDelayed.id()) { + mismatch = true; + break; + } + + if (tpDelayed.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary) + tpts[i].setState(Qt::TouchPointMoved); + tpts[i].setLastPos(tpDelayed.lastPos()); + tpts[i].setLastScenePos(tpDelayed.lastScenePos()); + tpts[i].setLastScreenPos(tpDelayed.lastScreenPos()); + tpts[i].setLastNormalizedPos(tpDelayed.lastNormalizedPos()); + + states |= tpts.at(i).state(); + } + + // matching touch event? then merge the new event into the old one + if (!mismatch) { + delayedTouch->setTouchPoints(tpts); + delayedTouch->setTimestamp(event->timestamp()); + return true; + } + } + + // merging wasn't possible, so deliver the delayed event first, and then delay this one + deliverDelayedTouchEvent(); + delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); + delayedTouch->setTimestamp(event->timestamp()); + return true; +} + // entry point for touch event delivery: // - translate the event to window coordinates // - compress the event instead of delivering it if applicable @@ -1891,72 +1955,14 @@ static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_N void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) { translateTouchEvent(event); - qCDebug(DBG_TOUCH) << event; - Q_Q(QQuickWindow); if (qquickwindow_no_touch_compression || touchRecursionGuard) { deliverTouchEvent(event); return; } - Qt::TouchPointStates states = event->touchPointStates(); - if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) != 0) - && ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) == 0)) { - // we can only compress something that isn't a press or release - if (!delayedTouch) { - delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); - delayedTouch->setTimestamp(event->timestamp()); - if (renderControl) - QQuickRenderControlPrivate::get(renderControl)->maybeUpdate(); - else if (windowManager) - windowManager->maybeUpdate(q); - return; - } else { - // check if this looks like the last touch event - if (delayedTouch->type() == event->type() && - delayedTouch->device() == event->device() && - delayedTouch->modifiers() == event->modifiers() && - delayedTouch->touchPoints().count() == event->touchPoints().count()) - { - // possible match.. is it really the same? - bool mismatch = false; - - QList tpts = event->touchPoints(); - Qt::TouchPointStates states; - for (int i = 0; i < event->touchPoints().count(); ++i) { - const QTouchEvent::TouchPoint &tp = tpts.at(i); - const QTouchEvent::TouchPoint &tpDelayed = delayedTouch->touchPoints().at(i); - if (tp.id() != tpDelayed.id()) { - mismatch = true; - break; - } - - if (tpDelayed.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary) - tpts[i].setState(Qt::TouchPointMoved); - tpts[i].setLastPos(tpDelayed.lastPos()); - tpts[i].setLastScenePos(tpDelayed.lastScenePos()); - tpts[i].setLastScreenPos(tpDelayed.lastScreenPos()); - tpts[i].setLastNormalizedPos(tpDelayed.lastNormalizedPos()); - - states |= tpts.at(i).state(); - } - - // matching touch event? then merge the new event into the old one - if (!mismatch) { - delayedTouch->setTouchPoints(tpts); - delayedTouch->setTimestamp(event->timestamp()); - return; - } - } - - // merging wasn't possible, so deliver the delayed event first, and then delay this one - deliverDelayedTouchEvent(); - delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); - delayedTouch->setTimestamp(event->timestamp()); - return; - } - } else { + if (!compressTouchEvent(event)) { if (delayedTouch) deliverDelayedTouchEvent(); deliverTouchEvent(event); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 9a5a13d142..1b4b87e001 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -160,6 +160,7 @@ public: void handleTouchEvent(QTouchEvent *); void handleMouseEvent(QMouseEvent *); void deliverTouchEvent(QTouchEvent *); + bool compressTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); void deliverDelayedTouchEvent(); void flushFrameSynchronousEvents(); -- cgit v1.2.3 From abdc94b268fed68f068b99291ab81d85e3c84d6c Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Wed, 6 Jul 2016 14:35:24 +0300 Subject: Replace QString::trimmed() with QStringRef::trimmed() ... where it's possible. Reduce allocations. Change-Id: I503507f8f29e2455113c48e50389a6fdf80e1d16 Reviewed-by: Shawn Rutledge Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4runtime.cpp | 2 +- src/qml/parser/qqmljsengine_p.cpp | 2 +- src/qml/qml/qqmlxmlhttprequest.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index c2dfdb1497..50896305d8 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -387,7 +387,7 @@ QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left double RuntimeHelpers::stringToNumber(const QString &string) { - QString s = string.trimmed(); + const QStringRef s = QStringRef(&string).trimmed(); if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) return s.toLong(0, 16); bool ok; diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp index 07064a4889..7a6d9c3826 100644 --- a/src/qml/parser/qqmljsengine_p.cpp +++ b/src/qml/parser/qqmljsengine_p.cpp @@ -114,7 +114,7 @@ double integerFromString(const char *buf, int size, int radix) double integerFromString(const QString &str, int radix) { - QByteArray ba = str.trimmed().toLatin1(); + QByteArray ba = QStringRef(&str).trimmed().toLatin1(); return integerFromString(ba.constData(), ba.size(), radix); } diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 74de30c96c..201d634411 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -715,7 +715,7 @@ ReturnedValue Text::method_isElementContentWhitespace(CallContext *ctx) Scoped r(scope, ctx->thisObject().as()); if (!r) return Encode::undefined(); - return Encode(r->d()->d->data.trimmed().isEmpty()); + return Encode(QStringRef(&r->d()->d->data).trimmed().isEmpty()); } ReturnedValue Text::method_wholeText(CallContext *ctx) -- cgit v1.2.3 From 3de5955e2ff3c5c14093a0aa135d461d0c7664c4 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Wed, 6 Jul 2016 14:43:22 +0300 Subject: Proper construction of QString with known size and known char ... by corresponding ctor. Don't use fill() for this case. Change-Id: Ife5711a946524c0bbac6c5431ef7052275fc3d33 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4runtime.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 50896305d8..66ea6b33d3 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -252,11 +252,11 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->append(QLatin1Char('+')); result->append(QString::number(decpt - 1)); } else if (decpt <= 0) { - result->prepend(QString::fromLatin1("0.%1").arg(QString().fill(zero, -decpt))); + result->prepend(QLatin1String("0.") + QString(-decpt, zero)); } else if (decpt < result->length()) { result->insert(decpt, dot); } else { - result->append(QString().fill(zero, decpt - result->length())); + result->append(QString(decpt - result->length(), zero)); } if (sign) -- cgit v1.2.3 From a858ce0039cb63a6a0afdfabab80ad4adc98ce17 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 1 Jul 2016 14:48:06 +0200 Subject: localstorage: disable warning about tautological comparison When building with gcc 6.1.1, there is a warning like this: plugin.cpp:152:126: error: self-comparison always evaluates to true [-Werror=tautological-compare] DEFINE_OBJECT_VTABLE(QV4::QQmlSqlDatabaseWrapper); In the definition of DEFINE_OBJECT_VTABLE, the comparison &classname::SuperClass::static_vtbl == &Object::static_vtbl ? 0 : &classname::SuperClass::static_vtbl.vTable will be done at runtime. Maybe the macro could somehow do that at compile time (or maybe the compiler will optimize it away after detecting that the result is constant); but for now, this warning breaks the build. Task-number: QTBUG-53373 Change-Id: I5f8e429a3f1446d3ef931ff8b0c41fcf652f7a02 Reviewed-by: Thiago Macieira --- src/imports/localstorage/plugin.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index a043af6b46..fbcb6ddea5 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -149,7 +149,12 @@ public: using namespace QV4; +QT_WARNING_PUSH +#if (Q_CC_GNU >= 600) +QT_WARNING_DISABLE_GCC("-Wtautological-compare") +#endif DEFINE_OBJECT_VTABLE(QV4::QQmlSqlDatabaseWrapper); +QT_WARNING_POP -- cgit v1.2.3 From 2b171025bfedcbe5ac1c296a1f57400cd227ce2f Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Thu, 7 Jul 2016 12:35:00 +0200 Subject: Remove old rename-qtdeclarative-symbols script This rename happened a long time ago, there is probably not much sense in keeping this now. Change-Id: I9f567e39fd9e63c402bb3be69038ffec42df2865 Reviewed-by: Simon Hausmann --- bin/rename-qtdeclarative-symbols.sh | 642 ------------------------------------ 1 file changed, 642 deletions(-) delete mode 100755 bin/rename-qtdeclarative-symbols.sh diff --git a/bin/rename-qtdeclarative-symbols.sh b/bin/rename-qtdeclarative-symbols.sh deleted file mode 100755 index 3b82989fa5..0000000000 --- a/bin/rename-qtdeclarative-symbols.sh +++ /dev/null @@ -1,642 +0,0 @@ -#!/bin/sh -############################################################################# -## -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of the QtQml module of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:LGPL$ -## 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 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$ -## -############################################################################# - -# Replaces deprecated QDeclarative symbol names with their replacements -# -# Changes instances in all regular files under the specified directory; -# use on a clean source tree! - -if [ "$#" -lt "1" ] -then - echo " Usage: $0 " - exit 1; -fi - -MODIFY_DIR="$1" - -QML_SYMBOLS="\ - QDeclarativeAbstractBinding - QDeclarativeAbstractBoundSignal - QDeclarativeAbstractExpression - QDeclarativeAccessible - QDeclarativeAccessors - QDeclarativeAccessorProperties - QDeclarativeAnimationTimer - QDeclarativeAssociationList - QDeclarativeAttachedPropertiesFunc - QDeclarativeBinding - QDeclarativeBindingPrivate - QDeclarativeBindingProfiler - QDeclarativeBoundSignal - QDeclarativeBoundSignalParameters - QDeclarativeBoundSignalProxy - QDeclarativeBuiltinFunctions - QDeclarativeCleanup - QDeclarativeColorValueType - QDeclarativeCompiledData - QDeclarativeCompiler - QDeclarativeCompilerTypes - QDeclarativeCompilingProfiler - QDeclarativeComponent - QDeclarativeComponentAttached - QDeclarativeComponentExtension - QDeclarativeComponentPrivate - QDeclarativeComponent_setQmlParent - QDeclarativeCompositeTypeData - QDeclarativeConnectionsParser - QDeclarativeContext - QDeclarativeContextData - QDeclarativeContextPrivate - QDeclarativeCustomParser - QDeclarativeCustomParserNode - QDeclarativeCustomParserNodePrivate - QDeclarativeCustomParserProperty - QDeclarativeCustomParserPropertyPrivate - QDeclarativeData - QDeclarativeDataBlob - QDeclarativeDataExtended - QDeclarativeDataLoader - QDeclarativeDataLoaderNetworkReplyProxy - QDeclarativeDataLoaderThread - QDeclarativeDateExtension - QDeclarativeDataTest - QDeclarativeDebug - QDeclarativeDebugClient - QDeclarativeDebugClientPrivate - QDeclarativeDebugConnection - QDeclarativeDebugConnectionPrivate - QDeclarativeDebugContextReference - QDeclarativeDebugData - QDeclarativeDebugEngineReference - QDeclarativeDebugEnginesQuery - QDeclarativeDebugExpressionQuery - QDeclarativeDebugFileReference - QDeclarativeDebugger - QDeclarativeDebuggingEnabler - QDeclarativeDebugHelper - QDeclarativeDebugObjectExpressionWatch - QDeclarativeDebugObjectQuery - QDeclarativeDebugObjectReference - QDeclarativeDebugPropertyReference - QDeclarativeDebugPropertyWatch - QDeclarativeDebugQuery - QDeclarativeDebugRootContextQuery - QDeclarativeDebugServer - QDeclarativeDebugServerConnection - QDeclarativeDebugServerPrivate - QDeclarativeDebugServerThread - QDeclarativeDebugService - QDeclarativeDebugServicePrivate - QDeclarativeDebugStatesDelegate - QDeclarativeDebugTrace - QDeclarativeDebugWatch - QDeclarativeDelayedError - QDeclarativeDirComponents - QDeclarativeDirParser - QDeclarativeDirScripts - QDeclarativeDOMNodeResource - QDeclarativeEasingValueType - QDeclarativeElement - QDeclarativeEngine - QDeclarativeEngineDebug - QDeclarativeEngineDebugClient - QDeclarativeEngineDebugService - QDeclarativeEngineDebugPrivate - QDeclarativeEnginePrivate - QDeclarativeError - QDeclarativeErrorPrivate - QDeclarativeExpression - QDeclarativeExpressionPrivate - QDeclarativeExtensionInterface - QDeclarativeExtensionPlugin - QDeclarativeFontValueType - QDeclarativeGraphics_DerivedObject - QDeclarativeGuard - QDeclarativeGuardedContextData - QDeclarativeGuardImpl - QDeclarativeHandlingSignalProfiler - QDeclarativeImportDatabase - QDeclarativeImportedNamespace - QDeclarativeImports - QDeclarativeImportsPrivate - QDeclarativeIncubationController - QDeclarativeIncubator - QDeclarativeIncubatorController - QDeclarativeIncubatorPrivate - QDeclarativeIncubators - QDeclarativeInfo - QDeclarativeInfoPrivate - QDeclarativeInspector - QDeclarativeInspectorInterface - QDeclarativeInspectorService - QDeclarativeInstruction - QDeclarativeInstructionData - QDeclarativeInstructionMeta - QDeclarativeIntegerCache - QDeclarativeJavaScriptExpression - QDeclarativeJavaScriptExpressionGuard - QDeclarativeJS - QDeclarativeJSGrammar - QDeclarativeListProperty - QDeclarativeListReference - QDeclarativeListReferencePrivate - QDeclarativeLocale - QDeclarativeLocalStoragePlugin - QDeclarativeMatrix4x4ValueType - QDeclarativeMetaType - QDeclarativeMetaTypeData - QDeclarativeNetworkAccessManagerFactory - QDeclarativeNotifier - QDeclarativeNotifierEndpoint - QDeclarativeNullableValue - QDeclarativeNumberExtension - QDeclarativeObjectCreatingProfiler - QDeclarativeObjectData - QDeclarativeObjectProperty - QDeclarativeObserverMode - QDeclarativeOpenMetaObject - QDeclarativeOpenMetaObjectPrivate - QDeclarativeOpenMetaObjectType - QDeclarativeOpenMetaObjectTypePrivate - QDeclarativeParser - QDeclarativeParserStatus - QDeclarativePointFValueType - QDeclarativePointValueType - QDeclarativePool - QDeclarativePrivate - QDeclarativeProfilerData - QDeclarativeProfilerService - QDeclarativeProperties - QDeclarativeProperty - QDeclarativePropertyCache - QDeclarativePropertyCacheMethodArguments - QDeclarativePropertyData - QDeclarativePropertyMap - QDeclarativePropertyMapMetaObject - QDeclarativePropertyMapPrivate - QDeclarativePropertyPrivate - QDeclarativePropertyRawData - QDeclarativePropertyValueInterceptor - QDeclarativePropertyValueSource - QDeclarativeProxyMetaObject - QDeclarativeQmldirData - QDeclarativeQtQuick1Module - QDeclarativeQtQuick2Module - QDeclarativeQtQuick2DebugStatesDelegate - QDeclarativeQuaternionValueType - QDeclarativeRectFValueType - QDeclarativeRectValueType - QDeclarativeRefCount - QDeclarativeRefPointer - QDeclarativeRegisterType - QDeclarativeRewrite - QDeclarativeScript - QDeclarativeScriptBlob - QDeclarativeScriptData - QDeclarativeScriptPrivate - QDeclarativeScriptString - QDeclarativeScriptStringPrivate - QDeclarativeSizeFValueType - QDeclarativeSizeValueType - QDeclarativeSqlDatabaseData - QDeclarativeStringConverters - QDeclarativeThread - QDeclarativeThreadPrivate - QDeclarativeTrace - QDeclarativeType - QDeclarativeTypeData - QDeclarativeTypeInfo - QDeclarativeTypeLoader - QDeclarativeTypeModule - QDeclarativeTypeModulePrivate - QDeclarativeTypeModuleVersion - QDeclarativeTypeNameCache - QDeclarativeTypeNotAvailable - QDeclarativeTypePrivate - QDeclarativeTypesExtensionInterface - QDeclarativeV8Function - QDeclarativeV8Handle - QDeclarativeValueType - QDeclarativeValueTypeProxyBinding - QDeclarativeValueTypeFactory - QDeclarativeVector2DValueType - QDeclarativeVector3DValueType - QDeclarativeVector4DValueType - QDeclarativeVME - QDeclarativeVMEGuard - QDeclarativeVMEMetaData - QDeclarativeVMEMetaObject - QDeclarativeVMEMetaObjectEndpoint - QDeclarativeVMEVariant - QDeclarativeVMETypes - QDeclarativeWatcher - QDeclarativeWatchProxy - QDeclarativeXMLHttpRequest - QDeclarativeXMLHttpRequestData - QDeclarative_isFileCaseCorrect - QDeclarative_setParent_noEvent - QQuickProperties - QQuickPropertyCacheMethodArguments - QQuickPropertyData -" - -QUICK_SYMBOLS="\ - QDeclarativeAbstractAnimation - QDeclarativeAbstractAnimationAction - QDeclarativeAbstractAnimationPrivate - QDeclarativeAction - QDeclarativeActionEvent - QDeclarativeAnchors - QDeclarativeAnimationController - QDeclarativeAnimationControllerPrivate - QDeclarativeAnimationGroup - QDeclarativeAnimationGroupPrivate - QDeclarativeAnimationPropertyUpdater - QDeclarativeApplication - QDeclarativeApplicationPrivate - QDeclarativeBehavior - QDeclarativeBehaviorPrivate - QDeclarativeBind - QDeclarativeBindPrivate - QDeclarativeBulkValueAnimator - QDeclarativeBulkValueUpdater - QDeclarativeCachedBezier - QDeclarativeChangeSet - QDeclarativeColorAnimation - QDeclarativeConnections - QDeclarativeConnectionsPrivate - QDeclarativeCurve - QDeclarativeDefaultTextureFactory - QDeclarativeFlick - QDeclarativeFocusPanel - QDeclarativeFolderListModel - QDeclarativeFolderListModelPrivate - QDeclarativeFontLoader - QDeclarativeFontLoaderPrivate - QDeclarativeFontObject - QDeclarativeGestureArea - QDeclarativeGestureAreaParser - QDeclarativeGestureAreaPrivate - QDeclarativeGraphics - QDeclarativeImageProvider - QDeclarativeImageProviderPrivate - QDeclarativeItem - QDeclarativeItemAccessor - QDeclarativeItemChangeListener - QDeclarativeItemKeyFilter - QDeclarativeItemPrivate - QDeclarativeListAccessor - QDeclarativeListCompositor - QDeclarativeListElement - QDeclarativeListModel - QDeclarativeListModelParser - QDeclarativeListModelWorkerAgent - QDeclarativeListView - QDeclarativeNumberAnimation - QDeclarativePackage - QDeclarativePackageAttached - QDeclarativePackagePrivate - QDeclarativeParallelAnimation - QDeclarativeParticle - QDeclarativeParticleMotion - QDeclarativeParticleMotionGravity - QDeclarativeParticleMotionLinear - QDeclarativeParticleMotionWander - QDeclarativeParticles - QDeclarativeParticlesPainter - QDeclarativeParticlesPrivate - QDeclarativePath - QDeclarativePathArc - QDeclarativePathAttribute - QDeclarativePathCatmullRomCurve - QDeclarativePathCubic - QDeclarativePathCurve - QDeclarativePathData - QDeclarativePathElement - QDeclarativePathInterpolator - QDeclarativePathLine - QDeclarativePathPercent - QDeclarativePathPrivate - QDeclarativePathQuad - QDeclarativePathSvg - QDeclarativePauseAnimation - QDeclarativePauseAnimationPrivate - QDeclarativePixmap - QDeclarativePixmapData - QDeclarativePixmapKey - QDeclarativePixmapNull - QDeclarativePixmapReader - QDeclarativePixmapReaderThreadObject - QDeclarativePixmapReply - QDeclarativePixmapStore - QDeclarativePropertyAction - QDeclarativePropertyActionPrivate - QDeclarativePropertyAnimation - QDeclarativePropertyAnimationPrivate - QDeclarativePropertyChanges - QDeclarativePropertyChangesParser - QDeclarativePropertyChangesPrivate - QDeclarativeReplaceSignalHandler - QDeclarativeRevertAction - QDeclarativeRotationAnimation - QDeclarativeRotationAnimationPrivate - QDeclarativeSequentialAnimation - QDeclarativeScriptAction - QDeclarativeScriptActionPrivate - QDeclarativeSetPropertyAnimationAction - QDeclarativeSimpleAction - QDeclarativeSmoothedAnimation - QDeclarativeSmoothedAnimationPrivate - QDeclarativeSpringAnimation - QDeclarativeSpringAnimationPrivate - QDeclarativeState - QDeclarativeStateActions - QDeclarativeStateChange - QDeclarativeStateChangeScript - QDeclarativeStateChangeScriptPrivate - QDeclarativeStateGroup - QDeclarativeStateGroupPrivate - QDeclarativeStateOperation - QDeclarativeStateOperationPrivate - QDeclarativeStatePrivate - QDeclarativeStyledText - QDeclarativeStyledTextImgTag - QDeclarativeStyledTextPrivate - QDeclarativeSystemPalette - QDeclarativeSystemPalettePrivate - QDeclarativeTextureFactory - QDeclarativeTimeLine - QDeclarativeTimeLineCallback - QDeclarativeTimeLineObject - QDeclarativeTimeLinePrivate - QDeclarativeTimeLineValue - QDeclarativeTimeLineValueProxy - QDeclarativeTimeLineValues - QDeclarativeTimer - QDeclarativeTimerPrivate - QDeclarativeTransition - QDeclarativeTransitionInstance - QDeclarativeTransitionManager - QDeclarativeTransitionManagerPrivate - QDeclarativeTransitionPrivate - QDeclarativeUtilModule - QDeclarativeVector3dAnimation - QDeclarativeView - QDeclarativeViewInspector - QDeclarativeViewInspectorPrivate - QDeclarativeViewPrivate - QDeclarativeWebView - QDeclarativeXmlListModel - QDeclarativeXmlListModelPrivate - QDeclarativeXmlListModelRole - QDeclarativeXmlListRange - QDeclarativeXmlQueryEngine - QDeclarativeXmlQueryResult - QDeclarativeXmlQueryThreadObject - QDeclarativeXmlRoleList - QDeclarativeSvgParser - QDeclarativeWorkerScript - QDeclarativeWorkerScriptEngine - QDeclarativeWorkerScriptEnginePrivate -" - -QML_INCLUDE_FILES="\ - qdeclarativeaccessible.h - qdeclarativeaccessors_p.h - qdeclarativebinding_p.h - qdeclarativebinding_p_p.h - qdeclarativeboundsignal_p.h - qdeclarativebuiltinfunctions_p.h - qdeclarativecleanup_p.h - qdeclarativecompiler_p.h - qdeclarativecomponentattached_p.h - qdeclarativecomponent.h - qdeclarativecomponent_p.h - qdeclarativecontext.h - qdeclarativecontext_p.h - qdeclarativecustomparser_p.h - qdeclarativecustomparser_p_p.h - qdeclarativedata_p.h - qdeclarativedebugclient_p.h - qdeclarativedebug.h - qdeclarativedebughelper_p.h - qdeclarativedebugserverconnection_p.h - qdeclarativedebugserver_p.h - qdeclarativedebugservice_p.h - qdeclarativedebugservice_p_p.h - qdeclarativedebugstatesdelegate_p.h - qdeclarativedebugtrace_p.h - qdeclarativedirparser_p.h - qdeclarativeenginedebug_p.h - qdeclarativeenginedebugservice_p.h - qdeclarativeengine.h - qdeclarativeengine_p.h - qdeclarativeerror.h - qdeclarativeexpression.h - qdeclarativeexpression_p.h - qdeclarativeextensioninterface.h - qdeclarativeextensionplugin.h - qdeclarativeglobal_p.h - qdeclarativeguard_p.h - qdeclarative.h - qdeclarativeimageprovider.h - qdeclarativeimport_p.h - qdeclarativeincubator.h - qdeclarativeincubator_p.h - qdeclarativeinfo.h - qdeclarativeinspectorinterface_p.h - qdeclarativeinspectorprotocol.h - qdeclarativeinspectorservice_p.h - qdeclarativeinstruction_p.h - qdeclarativeintegercache_p.h - qdeclarativejsastfwd_p.h - qdeclarativejsast_p.h - qdeclarativejsastvisitor_p.h - qdeclarativejsengine_p.h - qdeclarativejsglobal_p.h - qdeclarativejsgrammar_p.h - qdeclarativejskeywords_p.h - qdeclarativejslexer_p.h - qdeclarativejsmemorypool_p.h - qdeclarativejsparser_p.h - qdeclarativelist.h - qdeclarativelist_p.h - qdeclarativelocale_p.h - qdeclarativemetatype_p.h - qdeclarativenetworkaccessmanagerfactory.h - qdeclarativenotifier_p.h - qdeclarativenullablevalue_p_p.h - qdeclarativeopenmetaobject_p.h - qdeclarativeparserstatus.h - qdeclarativepool_p.h - qdeclarativeprivate.h - qdeclarativeprofilerservice_p.h - qdeclarativepropertycache_p.h - qdeclarativeproperty.h - qdeclarativepropertymap.h - qdeclarativeproperty_p.h - qdeclarativepropertyvalueinterceptor_p.h - qdeclarativepropertyvaluesource.h - qdeclarativeproxymetaobject_p.h - qdeclarativerefcount_p.h - qdeclarativerewrite_p.h - qdeclarativescript_p.h - qdeclarativescriptstring.h - qdeclarativescriptstring_p.h - qdeclarativesqldatabase_p.h - qdeclarativestringconverters_p.h - qdeclarativethread_p.h - qdeclarativetrace_p.h - qdeclarativetypeloader_p.h - qdeclarativetypenamecache_p.h - qdeclarativetypenotavailable_p.h - qdeclarativevaluetype_p.h - qdeclarativevmemetaobject_p.h - qdeclarativevme_p.h - qdeclarativewatcher_p.h - qdeclarativexmlhttprequest_p.h - qdeclarativexmllistmodel_p.h -" - -QUICK_INCLUDE_FILES="\ - qdeclarativeanimation_p.h - qdeclarativeanimation_p_p.h - qdeclarativeanimationcontroller_p.h - qdeclarativeapplication_p.h - qdeclarativebehavior_p.h - qdeclarativebind_p.h - qdeclarativechangeset_p.h - qdeclarativeconnections_p.h - qdeclarativefolderlistmodel.h - qdeclarativefontloader_p.h - qdeclarativelistaccessor_p.h - qdeclarativelistcompositor_p.h - qdeclarativelistmodel_p.h - qdeclarativelistmodel_p_p.h - qdeclarativelistmodelworkeragent_p.h - qdeclarativepackage_p.h - qdeclarativepathinterpolator_p.h - qdeclarativepath_p.h - qdeclarativepath_p_p.h - qdeclarativepixmapcache_p.h - qdeclarativepropertychanges_p.h - qdeclarativesmoothedanimation_p.h - qdeclarativesmoothedanimation_p_p.h - qdeclarativespringanimation_p.h - qdeclarativestategroup_p.h - qdeclarativestateoperations_p.h - qdeclarativestate_p.h - qdeclarativestate_p_p.h - qdeclarativestyledtext_p.h - qdeclarativesvgparser_p.h - qdeclarativesystempalette_p.h - qdeclarativetimeline_p_p.h - qdeclarativetimer_p.h - qdeclarativetransitionmanager_p_p.h - qdeclarativetransition_p.h - qdeclarativeutilmodule_p.h - qdeclarativeworkerscript_p.h -" - -replaceMatch() -{ - SYMBOL="$1" - REPLACEMENT="$2" - echo "Replacing $SYMBOL with $REPLACEMENT:" - - CONTAINERS=$(find "$MODIFY_DIR" ! -path ".git" -type f -print0 | xargs -0 grep -l -I "$SYMBOL") - for CONTAINER in $CONTAINERS - do - echo " $CONTAINER" - TMP_FILE="$CONTAINER.tmp" - - sed 's|'"$SYMBOL"'|'"$REPLACEMENT"'|g' <"$CONTAINER" >"$TMP_FILE" - mv "$TMP_FILE" "$CONTAINER" - done - echo -} - -for QML_SYMBOL in $QML_SYMBOLS -do - QML_REPLACEMENT="QQml${QML_SYMBOL#QDeclarative}" - replaceMatch "\bQtDeclarative/$QML_SYMBOL\b" "QtQml/$QML_REPLACEMENT" - replaceMatch "\b$QML_SYMBOL\b" "$QML_REPLACEMENT" -done - -for QUICK_SYMBOL in $QUICK_SYMBOLS -do - QUICK_REPLACEMENT="QQuick${QUICK_SYMBOL#QDeclarative}" - replaceMatch "\bQtDeclarative/$QUICK_SYMBOL\b" "QtQuick/$QUICK_REPLACEMENT" - replaceMatch "\b$QUICK_SYMBOL\b" "$QUICK_REPLACEMENT" -done - -for QML_INCLUDE_FILE in $QML_INCLUDE_FILES -do - QML_INCLUDE_REPLACEMENT="qqml${QML_INCLUDE_FILE#qdeclarative}" - replaceMatch "\b$QML_INCLUDE_FILE\b" "$QML_INCLUDE_REPLACEMENT" -done - -for QUICK_INCLUDE_FILE in $QUICK_INCLUDE_FILES -do - QUICK_INCLUDE_REPLACEMENT="qquick${QUICK_INCLUDE_FILE#qdeclarative}" - replaceMatch "\b$QUICK_INCLUDE_FILE\b" "$QUICK_INCLUDE_REPLACEMENT" -done - -# Various one-off replacements -replaceMatch "\bQtDeclarative\b" "QtQml" -replaceMatch "\basQDeclarativeContext\b" "asQQmlContext" -replaceMatch "\basQDeclarativeContextPrivate\b" "asQQmlContextPrivate" - -# Replace any references to the 'declarative' module with 'qml' -echo "Replacing module declarative with qml:" -CONTAINERS=$(find "$MODIFY_DIR" \( -name \*\.pro -o -name \*\.pri \) -print0 | xargs -0 grep -l -I "\bdeclarative\b") -for CONTAINER in $CONTAINERS -do - echo " $CONTAINER" - TMP_FILE="$CONTAINER.tmp" - - # We only want to replace standalone 'declarative' and 'declarative-private' tokens - sed 's|\([[:space:]]\+\)declarative\([[:space:]]\+\)|\1qml\2|g' <"$CONTAINER" | sed 's|\([[:space:]]\+\)declarative$|\1qml|g' | sed 's|\([[:space:]]\+\)declarative-private\([[:space:]]\+\)|\1qml-private\2|g' | sed 's|\([[:space:]]\+\)declarative-private$|\1qml-private|g' >"$TMP_FILE" - mv "$TMP_FILE" "$CONTAINER" -done -echo - -echo "Replacements complete" -exit 0 -- cgit v1.2.3 From 649b309ea9ed7d03aa74565d51edb416c23460d9 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 3 Jun 2015 18:13:28 +0200 Subject: add QQuickPointerEvent and QQuickPointerDevice QQuickPointerEvent is first of all a wrapper for mouse and touch events to help unify event delivery. Because the instances are reused between deliveries (as in change 0ac9389dbd7facaa424dc175e5f5dac514630eef), it is allowed to be a QObject. This will facilitate exposing pointer events to QML later on, as with the other event classes in qquickevents_p_p.h. It allows both device-agnostic and device-specific handling of point-based interaction using mice and touchscreens (tablet support will come later). The interface of QQuickPointerDevice is a superset of QTouchDevice plus the relevant mouse and tablet device info. The instances are long-lived, and included by pointer in each QQuickPointerEvent. Applications and device-specific event handlers can use the device info to filter out irrelevant events. Change-Id: Iacb42d41889a9a8634d57331ec6cc65c6b36f71f Reviewed-by: Shawn Rutledge Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents_p_p.h | 258 +++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 39c5a060b5..979d6e441a 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -222,10 +222,268 @@ private: bool _accepted; }; +// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent +class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject +{ + Q_OBJECT + Q_PROPERTY(DeviceType type READ type CONSTANT) + Q_PROPERTY(PointerType pointerType READ pointerType CONSTANT) + Q_PROPERTY(Capabilities capabilities READ capabilities CONSTANT) + Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints CONSTANT) + Q_PROPERTY(int buttonCount READ buttonCount CONSTANT) + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(qint64 uniqueId READ uniqueId CONSTANT) + +public: + enum DeviceType { + UnknownDevice = 0x0000, + Mouse = 0x0001, + TouchScreen = 0x0002, + TouchPad = 0x0004, + Puck = 0x0008, + Stylus = 0x0010, + Airbrush = 0x0020, + AllDevices = 0x003F + }; + Q_DECLARE_FLAGS(DeviceTypes, DeviceType) + Q_ENUM(DeviceType) + Q_FLAG(DeviceTypes) + + enum PointerType { + GenericPointer = 0x0001, + Finger = 0x0002, + Pen = 0x0004, + Eraser = 0x0008, + Cursor = 0x0010, + AllPointerTypes = 0x001F + }; + Q_DECLARE_FLAGS(PointerTypes, PointerType) + Q_ENUM(PointerType) + Q_FLAG(PointerTypes) + + enum CapabilityFlag { + Position = 0x0001, + Area = 0x0002, + Pressure = 0x0004, + Velocity = 0x0008, + // some bits reserved in case we need more of QTouchDevice::Capabilities + Scroll = 0x0100, // mouse has a wheel, or there is OS-level scroll gesture recognition (dubious?) + Hover = 0x0200, + Rotation = 0x0400, + XTilt = 0x0800, + YTilt = 0x1000 + }; + Q_DECLARE_FLAGS(Capabilities, CapabilityFlag) + Q_ENUM(CapabilityFlag) + Q_FLAG(Capabilities) + + QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) + : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) + , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId) + {} + + ~QQuickPointerDevice() { } + DeviceType type() const { return m_deviceType; } + PointerType pointerType() const { return m_pointerType; } + Capabilities capabilities() const { return m_capabilities; } + bool hasCapability(CapabilityFlag cap) { return m_capabilities & cap; } + int maximumTouchPoints() const { return m_maximumTouchPoints; } + int buttonCount() const { return m_buttonCount; } + QString name() const { return m_name; } + qint64 uniqueId() const { return m_uniqueId; } + +private: + DeviceType m_deviceType; + PointerType m_pointerType; + Capabilities m_capabilities; + int m_maximumTouchPoints; + int m_buttonCount; + QString m_name; + qint64 m_uniqueId; + + Q_DISABLE_COPY(QQuickPointerDevice) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::PointerTypes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::Capabilities) + +class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject +{ + Q_OBJECT + Q_PROPERTY(QPointF scenePos READ scenePos) + Q_PROPERTY(Qt::TouchPointState state READ state) + Q_PROPERTY(quint64 pointId READ pointId) + Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + +public: + QQuickEventPoint() : QObject(), m_pointId(0), m_valid(false), m_accept(false), m_state(Qt::TouchPointReleased) + { + Q_UNUSED(m_reserved); + } + + void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId) + { + m_scenePos = scenePos; + m_pointId = pointId; + m_valid = true; + m_accept = false; + m_state = state; + } + + void invalidate() { m_valid = false; } + + QPointF scenePos() const { return m_scenePos; } + Qt::TouchPointState state() const { return m_state; } + quint64 pointId() const { return m_pointId; } + bool isValid() const { return m_valid; } + bool isAccepted() const { return m_accept; } + void setAccepted(bool accepted) { m_accept = accepted; } + +private: + QPointF m_scenePos; + quint64 m_pointId; + bool m_valid : 1; + bool m_accept : 1; + Qt::TouchPointState m_state : 4; + int m_reserved : 26; +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint +{ + Q_OBJECT + Q_PROPERTY(qreal rotation READ rotation) + Q_PROPERTY(qreal pressure READ pressure) + Q_PROPERTY(QPointerUniqueId uniqueId READ uniqueId) + +public: + QQuickEventTouchPoint() : QQuickEventPoint(), m_rotation(0), m_pressure(0) { } + + void reset(const QTouchEvent::TouchPoint &tp) + { + QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id()); + // TODO times and velocity + m_rotation = tp.rotation(); + m_pressure = tp.pressure(); + m_uniqueId = tp.uniqueId(); + } + + qreal rotation() const { return m_rotation; } + qreal pressure() const { return m_pressure; } + QPointerUniqueId uniqueId() const { return m_uniqueId; } + +private: + qreal m_rotation; + qreal m_pressure; + QPointerUniqueId m_uniqueId; +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(const QQuickPointerDevice *device READ device) + Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers) + Q_PROPERTY(Qt::MouseButtons button READ button) + Q_PROPERTY(Qt::MouseButtons buttons READ buttons) + +public: + QQuickPointerEvent(QObject *parent = nullptr) + : QObject(parent) + , m_device(nullptr) + , m_event(nullptr) + , m_button(Qt::NoButton) + , m_pressedButtons(Qt::NoButton) + , m_mousePoint(nullptr) { } + + void reset(const QQuickPointerDevice* dev, const QMouseEvent *ev) { + m_device = dev; + m_event = ev; + m_button = ev->button(); + m_pressedButtons = ev->buttons(); + Qt::TouchPointState state = Qt::TouchPointStationary; + switch (ev->type()) { + case QEvent::MouseButtonPress: + state = Qt::TouchPointPressed; + break; + case QEvent::MouseButtonRelease: + state = Qt::TouchPointReleased; + break; + case QEvent::MouseMove: + state = Qt::TouchPointMoved; + break; + default: + break; + } + + if (!m_mousePoint) + m_mousePoint = new QQuickEventPoint; + m_mousePoint->reset(state, ev->windowPos(), 0); // mouse is 0 + } + + void reset(const QQuickPointerDevice* dev, const QTouchEvent *ev) { + m_device = dev; + m_event = ev; + m_button = Qt::NoButton; + m_pressedButtons = Qt::NoButton; + + const QList &tps = ev->touchPoints(); + const int pointCount = tps.count(); + while (pointCount > m_touchPoints.count()) + m_touchPoints.append(new QQuickEventTouchPoint); + + for (int i = 0; i < pointCount; ++i) + m_touchPoints.at(i)->reset(tps.at(i)); + } + + const QQuickPointerDevice *device() const { return m_device; } + Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; } + Qt::MouseButton button() const { return m_button; } + Qt::MouseButtons buttons() const { return m_pressedButtons; } + + const QTouchEvent *asTouchEvent() const { + if (!m_event) + return nullptr; + switch (m_event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchCancel: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + return static_cast(m_event); + default: + break; + } + return nullptr; + } + + // helpers for C++ event delivery, not for QML properties + int pointCount() const { return asTouchEvent() ? m_touchPoints.count() : 1; } + const QQuickEventPoint *point(int i) const { + if (asTouchEvent()) + return m_touchPoints.at(i); + return i == 0 ? m_mousePoint : nullptr; + } + +protected: + bool isValid() const { return m_event != nullptr; } + +protected: + const QQuickPointerDevice *m_device; + const QInputEvent *m_event; // original event as received by QQuickWindow + Qt::MouseButton m_button; + Qt::MouseButtons m_pressedButtons; + QVector m_touchPoints; + QQuickEventPoint *m_mousePoint; + + Q_DISABLE_COPY(QQuickPointerEvent) +}; + QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickKeyEvent) QML_DECLARE_TYPE(QQuickMouseEvent) QML_DECLARE_TYPE(QQuickWheelEvent) +QML_DECLARE_TYPE(QQuickPointerDevice) +QML_DECLARE_TYPE(QPointerUniqueId) +QML_DECLARE_TYPE(QQuickPointerEvent) #endif // QQUICKEVENTS_P_P_H -- cgit v1.2.3 From 28ed215281bb4ee687d2a2bfb7d6fdc1001c082b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 22 Jun 2016 09:30:10 +0200 Subject: Added two unit tests for alias nesting The first test covers a use-case currently only otherwise see in QtQuickControls, and so having it here allows for catching regressions in that area before hitting the next qt5 build. Having an alias refer to another alias property within the same file only works if the second alias is resolved/processed before the first one. This works by chance when relying on the order of processing reverse to the declaration, but that's a bug. It should work regardless of the order. That is what the second test-case demonstrates and that is why it is marked as failure. Change-Id: Iba386437d21efa868d9814221092df5f7ef6f1f6 Reviewed-by: Lars Knoll --- tests/auto/qml/qqmllanguage/data/alias.12.qml | 15 ++++++++++++ tests/auto/qml/qqmllanguage/data/alias.13.qml | 16 +++++++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 30 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 tests/auto/qml/qqmllanguage/data/alias.12.qml create mode 100644 tests/auto/qml/qqmllanguage/data/alias.13.qml diff --git a/tests/auto/qml/qqmllanguage/data/alias.12.qml b/tests/auto/qml/qqmllanguage/data/alias.12.qml new file mode 100644 index 0000000000..cc17318092 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.12.qml @@ -0,0 +1,15 @@ +import QtQml 2.0 + +QtObject { + id: root + property alias topLevelAlias: subObject.targetProperty + + property QtObject referencingSubObject: QtObject { + property alias success: root.topLevelAlias + } + + property QtObject foo: QtObject { + id: subObject + property bool targetProperty: true + } +} diff --git a/tests/auto/qml/qqmllanguage/data/alias.13.qml b/tests/auto/qml/qqmllanguage/data/alias.13.qml new file mode 100644 index 0000000000..cff8a72d9a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.13.qml @@ -0,0 +1,16 @@ +import QtQml 2.0 + +QtObject { + id: root + property bool targetProperty: true + + property QtObject foo: QtObject { + id: otherSubObject + property alias theAlias: root.targetProperty + } + + property QtObject referencingSubObject: QtObject { + property alias success: otherSubObject.theAlias + } + +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 70db9aecd4..1e8e92217c 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -1803,6 +1803,36 @@ void tst_qqmllanguage::aliasProperties() delete object; } + + // Nested aliases with a qml file + { + QQmlComponent component(&engine, testFileUrl("alias.12.qml")); + VERIFY_ERRORS(0); + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QPointer subObject = qvariant_cast(object->property("referencingSubObject")); + QVERIFY(!subObject.isNull()); + + QVERIFY(subObject->property("success").toBool()); + } + + // Nested aliases with a qml file with reverse ordering + { + // This is known to fail at the moment. + QQmlComponent component(&engine, testFileUrl("alias.13.qml")); + QVERIFY(!component.errors().isEmpty()); +#if 0 + VERIFY_ERRORS(0); + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QPointer subObject = qvariant_cast(object->property("referencingSubObject")); + QVERIFY(!subObject.isNull()); + + QVERIFY(subObject->property("success").toBool()); +#endif + } } // QTBUG-13374 Test that alias properties and signals can coexist -- cgit v1.2.3 From 733b20af44d4400faa61e02dfb2feae8badaf003 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 20 Jun 2016 11:57:43 +0200 Subject: MouseArea: add autotest for subtreeHoverEnabled Verify the fix in f7e462b. Task-number: QTBUG-54019 Change-Id: Ia9846f9b79473ea4f72e895320cf01d5d85cc1a3 Reviewed-by: Robin Burchell --- .../auto/quick/qquickmousearea/data/qtbug54019.qml | 21 +++++++++++++++++++++ .../quick/qquickmousearea/tst_qquickmousearea.cpp | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/auto/quick/qquickmousearea/data/qtbug54019.qml diff --git a/tests/auto/quick/qquickmousearea/data/qtbug54019.qml b/tests/auto/quick/qquickmousearea/data/qtbug54019.qml new file mode 100644 index 0000000000..75cca2691a --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/qtbug54019.qml @@ -0,0 +1,21 @@ +import QtQuick 2.7 + +Item { + width: 200 + height: 200 + MouseArea { + id: ma + property string str: "foo!" + width: 150; height: 150 + hoverEnabled: true + + Rectangle { + anchors.fill: parent + color: ma.containsMouse ? "lightsteelblue" : "gray" + } + Text { + text: ma.str + textFormat: Text.PlainText // consequently Text does not care about hover events + } + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index bb3c93720c..46ccb8f80c 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +108,7 @@ private slots: void hoverPropagation(); void hoverVisible(); void hoverAfterPress(); + void subtreeHoverEnabled(); void disableAfterPress(); void onWheel(); void transformedMouseArea_data(); @@ -1316,6 +1318,26 @@ void tst_QQuickMouseArea::hoverAfterPress() QCOMPARE(mouseArea->hovered(), false); } +void tst_QQuickMouseArea::subtreeHoverEnabled() +{ + QQuickView window; + QByteArray errorMessage; + QVERIFY2(initView(window, testFileUrl("qtbug54019.qml"), true, &errorMessage), errorMessage.constData()); + QQuickItem *root = window.rootObject(); + QVERIFY(root != 0); + + QQuickMouseArea *mouseArea = root->findChild(); + QQuickItemPrivate *rootPrivate = QQuickItemPrivate::get(root); + QVERIFY(mouseArea != 0); + QTest::mouseMove(&window, QPoint(10, 160)); + QCOMPARE(mouseArea->hovered(), false); + QVERIFY(rootPrivate->subtreeHoverEnabled); + QTest::mouseMove(&window, QPoint(10, 10)); + QCOMPARE(mouseArea->hovered(), true); + QTest::mouseMove(&window, QPoint(160, 10)); + QCOMPARE(mouseArea->hovered(), false); +} + void tst_QQuickMouseArea::disableAfterPress() { QQuickView window; -- cgit v1.2.3 From f588032c509b01ab4083467afb540d2fdc0685ac Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 5 Jul 2016 13:40:25 +0200 Subject: QQuickWindow: add QQuickPointerDevice instances for pointer events The device objects are long-lived; every QQuickPointerEvent needs a pointer to one of them. Change-Id: I39e4b8ddefd4a62521c7837c4bafb84ee13ce519 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 27 +++++++++++++++++++++++++++ src/quick/items/qquickwindow_p.h | 6 ++++++ 2 files changed, 33 insertions(+) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index cf23aa1367..f263ed23d1 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -93,6 +93,10 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_ bool QQuickWindowPrivate::defaultAlphaBuffer = false; +QQuickPointerDevice *QQuickWindowPrivate::genericMouseDevice(nullptr); +QHash QQuickWindowPrivate::touchDevices; +QHash QQuickWindowPrivate::tabletDevices; + void QQuickWindowPrivate::updateFocusItemTransform() { #ifndef QT_NO_IM @@ -503,6 +507,11 @@ QQuickWindowPrivate::QQuickWindowPrivate() #ifndef QT_NO_DRAGANDDROP dragGrabber = new QQuickDragGrabber; #endif + if (!genericMouseDevice) { + genericMouseDevice = new QQuickPointerDevice(QQuickPointerDevice::Mouse, QQuickPointerDevice::GenericPointer, + QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover, + 1, 3, QStringLiteral("core pointer"), 0); + } } QQuickWindowPrivate::~QQuickWindowPrivate() @@ -1852,6 +1861,24 @@ bool QQuickWindowPrivate::deliverNativeGestureEvent(QQuickItem *item, QNativeGes } #endif // QT_NO_GESTURES +QQuickPointerDevice *QQuickWindowPrivate::touchDevice(QTouchDevice *d) +{ + if (touchDevices.contains(d)) + return touchDevices.value(d); + + QQuickPointerDevice::DeviceType type = QQuickPointerDevice::TouchScreen; + QQuickPointerDevice::Capabilities caps = + static_cast(static_cast(d->capabilities()) & 0x0F); + if (d->type() == QTouchDevice::TouchPad) { + type = QQuickPointerDevice::TouchPad; + caps |= QQuickPointerDevice::Scroll; + } + QQuickPointerDevice *dev = new QQuickPointerDevice(type, QQuickPointerDevice::Finger, + caps, d->maximumTouchPoints(), 0, d->name(), 0); + touchDevices.insert(d, dev); + return dev; +} + bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) { qCDebug(DBG_TOUCH) << event; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 1b4b87e001..f2ec469b21 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -89,6 +89,7 @@ class QQuickItemPrivate; class QQuickWindowPrivate; class QTouchEvent; +class QQuickPointerDevice; class QQuickWindowRenderLoop; class QQuickWindowIncubationController; @@ -155,6 +156,7 @@ public: #ifndef QT_NO_GESTURES bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *); #endif + QQuickPointerDevice *touchDevice(QTouchDevice *d); bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList &, QSet *, QHash > *, QSet *filtered); void handleTouchEvent(QTouchEvent *); @@ -263,6 +265,10 @@ public: mutable QQuickWindowIncubationController *incubationController; + static QQuickPointerDevice *genericMouseDevice; + static QHash touchDevices; + static QHash tabletDevices; + static bool defaultAlphaBuffer; static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1); -- cgit v1.2.3 From 76150dc6b90d8e67ff6a810d8c62fbb59e18e5fc Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Thu, 7 Jul 2016 20:57:33 +0200 Subject: Fix incorrect determination of cursor state in some cases Porting the same fix applied to similar code (but for hover) from Shawn in f7e462ba153ff33a02cad903efc8662f1eacd225. We match the rename to make this easier to understand. Change-Id: I660ae77b1d0e3b735b8c39002109e133d2297f06 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 17 +++++++++-------- src/quick/items/qquickitem_p.h | 2 +- src/quick/items/qquickwindow.cpp | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 27acf38faa..dc0c9df132 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2915,7 +2915,7 @@ void QQuickItemPrivate::addChild(QQuickItem *child) // if the added child has a cursor and we do not currently have any children // with cursors, bubble the notification up - if (childPrivate->hasCursorInChild && !hasCursorInChild) + if (childPrivate->subtreeCursorEnabled && !subtreeCursorEnabled) setHasCursorInChild(true); #endif if (childPrivate->subtreeHoverEnabled && !subtreeHoverEnabled) @@ -2942,7 +2942,7 @@ void QQuickItemPrivate::removeChild(QQuickItem *child) QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); // turn it off, if nothing else is using it - if (childPrivate->hasCursorInChild && hasCursorInChild) + if (childPrivate->subtreeCursorEnabled && subtreeCursorEnabled) setHasCursorInChild(false); #endif if (childPrivate->subtreeHoverEnabled && subtreeHoverEnabled) @@ -3162,7 +3162,7 @@ QQuickItemPrivate::QQuickItemPrivate() , isAccessible(false) , culled(false) , hasCursor(false) - , hasCursorInChild(false) + , subtreeCursorEnabled(false) , subtreeHoverEnabled(false) , activeFocusOnTab(false) , implicitAntialiasing(false) @@ -7049,17 +7049,18 @@ void QQuickItemPrivate::setHasCursorInChild(bool hasCursor) Q_Q(QQuickItem); // if we're asked to turn it off (because of an unsetcursor call, or a node - // removal) then we should check our children and make sure it's really ok - // to turn it off. - if (!hasCursor && hasCursorInChild) { + // removal) then we should make sure it's really ok to turn it off. + if (!hasCursor && subtreeCursorEnabled) { + if (hasCursor) + return; // nope! sorry, I have a cursor myself foreach (QQuickItem *otherChild, childItems) { QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild); - if (otherChildPrivate->hasCursorInChild) + if (otherChildPrivate->subtreeCursorEnabled || otherChildPrivate->hasCursor) return; // nope! sorry, something else wants it kept on. } } - hasCursorInChild = hasCursor; + subtreeCursorEnabled = hasCursor; QQuickItem *parent = q->parentItem(); if (parent) { QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parent); diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 8328dd513b..de4dfc9cd7 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -426,7 +426,7 @@ public: bool isAccessible:1; bool culled:1; bool hasCursor:1; - bool hasCursorInChild:1; + bool subtreeCursorEnabled:1; // Bit 32 bool subtreeHoverEnabled:1; bool activeFocusOnTab:1; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index cf23aa1367..884e6b0d24 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2515,7 +2515,7 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF return 0; } - if (itemPrivate->hasCursorInChild) { + if (itemPrivate->subtreeCursorEnabled) { QList children = itemPrivate->paintOrderChildItems(); for (int ii = children.count() - 1; ii >= 0; --ii) { QQuickItem *child = children.at(ii); -- cgit v1.2.3 From bb8bf92b44aa9fdf5e6b2c2dce1b583a090e8872 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Thu, 7 Jul 2016 13:20:46 +0200 Subject: Layouts: Use qmlobject_{dis}connect This is significantly faster, especially with a large number of items and signals. For a RowLayout with 5 child items, I go from being able to create ~95 per frame to ~145 per frame on a 2011 mbp. Change-Id: I2c09078573a745b6d275931785de548364604a85 Reviewed-by: Simon Hausmann --- src/imports/layouts/qquicklayout.cpp | 12 ++++++------ src/imports/layouts/qquicklinearlayout.cpp | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/imports/layouts/qquicklayout.cpp b/src/imports/layouts/qquicklayout.cpp index abc8f97cec..31e2238feb 100644 --- a/src/imports/layouts/qquicklayout.cpp +++ b/src/imports/layouts/qquicklayout.cpp @@ -764,17 +764,17 @@ void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value) { if (change == ItemChildAddedChange) { QQuickItem *item = value.item; - QObject::connect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem())); - QObject::connect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem())); - QObject::connect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem())); + qmlobject_connect(item, QQuickItem, SIGNAL(implicitWidthChanged()), this, QQuickLayout, SLOT(invalidateSenderItem())); + qmlobject_connect(item, QQuickItem, SIGNAL(implicitHeightChanged()), this, QQuickLayout, SLOT(invalidateSenderItem())); + qmlobject_connect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem())); QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::SiblingOrder); if (isReady()) updateLayoutItems(); } else if (change == ItemChildRemovedChange) { QQuickItem *item = value.item; - QObject::disconnect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem())); - QObject::disconnect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem())); - QObject::disconnect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem())); + qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitWidthChanged()), this, QQuickLayout, SLOT(invalidateSenderItem())); + qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitHeightChanged()), this, QQuickLayout, SLOT(invalidateSenderItem())); + qmlobject_disconnect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem())); QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder); if (isReady()) updateLayoutItems(); diff --git a/src/imports/layouts/qquicklinearlayout.cpp b/src/imports/layouts/qquicklinearlayout.cpp index 0b4a1968d7..fdf8479133 100644 --- a/src/imports/layouts/qquicklinearlayout.cpp +++ b/src/imports/layouts/qquicklinearlayout.cpp @@ -333,10 +333,10 @@ QQuickGridLayoutBase::~QQuickGridLayoutBase() */ for (int i = 0; i < itemCount(); ++i) { QQuickItem *item = itemAt(i); - QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed())); - QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged())); - QObject::disconnect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem())); - QObject::disconnect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem())); + qmlobject_disconnect(item, QQuickItem, SIGNAL(destroyed()), this, QQuickGridLayoutBase, SLOT(onItemDestroyed())); + qmlobject_disconnect(item, QQuickItem, SIGNAL(visibleChanged()), this, QQuickGridLayoutBase, SLOT(onItemVisibleChanged())); + qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitWidthChanged()), this, QQuickGridLayoutBase, SLOT(invalidateSenderItem())); + qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitHeightChanged()), this, QQuickGridLayoutBase, SLOT(invalidateSenderItem())); } delete d->styleInfo; } @@ -465,13 +465,13 @@ void QQuickGridLayoutBase::itemChange(ItemChange change, const ItemChangeData &v if (change == ItemChildAddedChange) { quickLayoutDebug() << "ItemChildAddedChange"; QQuickItem *item = value.item; - QObject::connect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed())); - QObject::connect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged())); + qmlobject_connect(item, QQuickItem, SIGNAL(destroyed()), this, QQuickGridLayoutBase, SLOT(onItemDestroyed())); + qmlobject_connect(item, QQuickItem, SIGNAL(visibleChanged()), this, QQuickGridLayoutBase, SLOT(onItemVisibleChanged())); } else if (change == ItemChildRemovedChange) { quickLayoutDebug() << "ItemChildRemovedChange"; QQuickItem *item = value.item; - QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed())); - QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged())); + qmlobject_disconnect(item, QQuickItem, SIGNAL(destroyed()), this, QQuickGridLayoutBase, SLOT(onItemDestroyed())); + qmlobject_disconnect(item, QQuickItem, SIGNAL(visibleChanged()), this, QQuickGridLayoutBase, SLOT(onItemVisibleChanged())); } QQuickLayout::itemChange(change, value); -- cgit v1.2.3 From 5e9f80976ee082abfcb9fddae901393a4b0b889e Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 7 Jul 2016 21:44:59 +0200 Subject: PointerEvent point: setAccept should default to true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I8212120aa31aaededc0a62e598c512fc29616b1b Reviewed-by: Jan Arve Sæther Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 979d6e441a..b03bf43f20 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -338,7 +338,7 @@ public: quint64 pointId() const { return m_pointId; } bool isValid() const { return m_valid; } bool isAccepted() const { return m_accept; } - void setAccepted(bool accepted) { m_accept = accepted; } + void setAccepted(bool accepted = true) { m_accept = accepted; } private: QPointF m_scenePos; -- cgit v1.2.3 From a0769ccff089f9699579448136a12504fa25cd47 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 7 Jul 2016 21:48:24 +0200 Subject: QQuickPointerEvent::reset return this MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes it possible to use it as: deliverEvent(pointerEvent->reset(qevent)); Change-Id: I653445c23696df384987ec8e948cb37ee312f217 Reviewed-by: Jan Arve Sæther Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index b03bf43f20..c0f2631096 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -395,7 +395,7 @@ public: , m_pressedButtons(Qt::NoButton) , m_mousePoint(nullptr) { } - void reset(const QQuickPointerDevice* dev, const QMouseEvent *ev) { + QQuickPointerEvent *reset(const QQuickPointerDevice* dev, const QMouseEvent *ev) { m_device = dev; m_event = ev; m_button = ev->button(); @@ -418,9 +418,10 @@ public: if (!m_mousePoint) m_mousePoint = new QQuickEventPoint; m_mousePoint->reset(state, ev->windowPos(), 0); // mouse is 0 + return this; } - void reset(const QQuickPointerDevice* dev, const QTouchEvent *ev) { + QQuickPointerEvent *reset(const QQuickPointerDevice* dev, const QTouchEvent *ev) { m_device = dev; m_event = ev; m_button = Qt::NoButton; @@ -433,6 +434,7 @@ public: for (int i = 0; i < pointCount; ++i) m_touchPoints.at(i)->reset(tps.at(i)); + return this; } const QQuickPointerDevice *device() const { return m_device; } -- cgit v1.2.3 From 8185e52960ae96c963b663b94cb0a782e7441ae6 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 7 Jul 2016 21:56:18 +0200 Subject: QQuickPointerEvent::reset move to .cpp file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib84d9fe2a3837f4f37087f7802eeb7570bb2baf7 Reviewed-by: Jan Arve Sæther Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 42 +++++++++++++++++++++++++++++++++++++ src/quick/items/qquickevents_p_p.h | 43 ++------------------------------------ 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 14c0adf393..e62920f77b 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -437,4 +437,46 @@ Item { \l inverted always returns false. */ +QQuickPointerEvent *QQuickPointerEvent::reset(const QQuickPointerDevice *dev, const QMouseEvent *ev) { + m_device = dev; + m_event = ev; + m_button = ev->button(); + m_pressedButtons = ev->buttons(); + Qt::TouchPointState state = Qt::TouchPointStationary; + switch (ev->type()) { + case QEvent::MouseButtonPress: + state = Qt::TouchPointPressed; + break; + case QEvent::MouseButtonRelease: + state = Qt::TouchPointReleased; + break; + case QEvent::MouseMove: + state = Qt::TouchPointMoved; + break; + default: + break; + } + + if (!m_mousePoint) + m_mousePoint = new QQuickEventPoint; + m_mousePoint->reset(state, ev->windowPos(), 0); // mouse is 0 + return this; +} + +QQuickPointerEvent *QQuickPointerEvent::reset(const QQuickPointerDevice *dev, const QTouchEvent *ev) { + m_device = dev; + m_event = ev; + m_button = Qt::NoButton; + m_pressedButtons = Qt::NoButton; + + const QList &tps = ev->touchPoints(); + const int pointCount = tps.count(); + while (pointCount > m_touchPoints.count()) + m_touchPoints.append(new QQuickEventTouchPoint); + + for (int i = 0; i < pointCount; ++i) + m_touchPoints.at(i)->reset(tps.at(i)); + return this; +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index c0f2631096..a1813f368b 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -395,47 +395,8 @@ public: , m_pressedButtons(Qt::NoButton) , m_mousePoint(nullptr) { } - QQuickPointerEvent *reset(const QQuickPointerDevice* dev, const QMouseEvent *ev) { - m_device = dev; - m_event = ev; - m_button = ev->button(); - m_pressedButtons = ev->buttons(); - Qt::TouchPointState state = Qt::TouchPointStationary; - switch (ev->type()) { - case QEvent::MouseButtonPress: - state = Qt::TouchPointPressed; - break; - case QEvent::MouseButtonRelease: - state = Qt::TouchPointReleased; - break; - case QEvent::MouseMove: - state = Qt::TouchPointMoved; - break; - default: - break; - } - - if (!m_mousePoint) - m_mousePoint = new QQuickEventPoint; - m_mousePoint->reset(state, ev->windowPos(), 0); // mouse is 0 - return this; - } - - QQuickPointerEvent *reset(const QQuickPointerDevice* dev, const QTouchEvent *ev) { - m_device = dev; - m_event = ev; - m_button = Qt::NoButton; - m_pressedButtons = Qt::NoButton; - - const QList &tps = ev->touchPoints(); - const int pointCount = tps.count(); - while (pointCount > m_touchPoints.count()) - m_touchPoints.append(new QQuickEventTouchPoint); - - for (int i = 0; i < pointCount; ++i) - m_touchPoints.at(i)->reset(tps.at(i)); - return this; - } + QQuickPointerEvent *reset(const QQuickPointerDevice* dev, const QMouseEvent *ev); + QQuickPointerEvent *reset(const QQuickPointerDevice* dev, const QTouchEvent *ev); const QQuickPointerDevice *device() const { return m_device; } Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; } -- cgit v1.2.3 From e50b4e539f08b9fc6ffcd9876c550138b4d5f173 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 7 Jul 2016 22:01:51 +0200 Subject: Simplify parameters to QQuickPointerEvent::reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need the device passed in, but can infer it. Change-Id: I95479493431f8ca8047bcf124c295081d4895d6c Reviewed-by: Jan Arve Sæther Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 10 ++++++---- src/quick/items/qquickevents_p_p.h | 4 ++-- src/quick/items/qquickwindow_p.h | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index e62920f77b..176b8c9467 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qquickevents_p_p.h" +#include QT_BEGIN_NAMESPACE @@ -437,8 +438,9 @@ Item { \l inverted always returns false. */ -QQuickPointerEvent *QQuickPointerEvent::reset(const QQuickPointerDevice *dev, const QMouseEvent *ev) { - m_device = dev; + +QQuickPointerEvent *QQuickPointerEvent::reset(const QMouseEvent *ev) { + m_device = QQuickWindowPrivate::genericMouseDevice; m_event = ev; m_button = ev->button(); m_pressedButtons = ev->buttons(); @@ -463,8 +465,8 @@ QQuickPointerEvent *QQuickPointerEvent::reset(const QQuickPointerDevice *dev, co return this; } -QQuickPointerEvent *QQuickPointerEvent::reset(const QQuickPointerDevice *dev, const QTouchEvent *ev) { - m_device = dev; +QQuickPointerEvent *QQuickPointerEvent::reset(const QTouchEvent *ev) { + m_device = QQuickWindowPrivate::touchDevice(ev->device()); m_event = ev; m_button = Qt::NoButton; m_pressedButtons = Qt::NoButton; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index a1813f368b..b47f55e353 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -395,8 +395,8 @@ public: , m_pressedButtons(Qt::NoButton) , m_mousePoint(nullptr) { } - QQuickPointerEvent *reset(const QQuickPointerDevice* dev, const QMouseEvent *ev); - QQuickPointerEvent *reset(const QQuickPointerDevice* dev, const QTouchEvent *ev); + QQuickPointerEvent *reset(const QMouseEvent *ev); + QQuickPointerEvent *reset(const QTouchEvent *ev); const QQuickPointerDevice *device() const { return m_device; } Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index f2ec469b21..e06fe4472d 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -156,7 +156,7 @@ public: #ifndef QT_NO_GESTURES bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *); #endif - QQuickPointerDevice *touchDevice(QTouchDevice *d); + static QQuickPointerDevice *touchDevice(QTouchDevice *d); bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList &, QSet *, QHash > *, QSet *filtered); void handleTouchEvent(QTouchEvent *); -- cgit v1.2.3 From 68f7c91f66bd7181d4f0107fa26f2e6e22e08675 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 7 Jul 2016 22:14:43 +0200 Subject: Merge overloads of QQuickPointerEvent::reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I9f76905dfc6ad21394a4ab08cf6684e91213dbde Reviewed-by: Jan Arve Sæther Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 19 +++++++++++++++---- src/quick/items/qquickevents_p_p.h | 11 +++++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 176b8c9467..d261f76d6c 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -439,7 +439,20 @@ Item { */ -QQuickPointerEvent *QQuickPointerEvent::reset(const QMouseEvent *ev) { +QQuickPointerEvent *QQuickPointerEvent::reset(const QEvent *ev) { + if (ev->type() >= QEvent::MouseButtonPress && ev->type() <= QEvent::MouseMove) + { + initFromMouse(static_cast(ev)); + } else if ((ev->type() >= QEvent::TouchBegin && ev->type() <= QEvent::TouchEnd) + || ev->type() == QEvent::TouchCancel) { + initFromTouch(static_cast(ev)); + } else { + Q_ASSERT_X(false, "", "invalid event type"); + } + return this; +} + +void QQuickPointerEvent::initFromMouse(const QMouseEvent *ev) { m_device = QQuickWindowPrivate::genericMouseDevice; m_event = ev; m_button = ev->button(); @@ -462,10 +475,9 @@ QQuickPointerEvent *QQuickPointerEvent::reset(const QMouseEvent *ev) { if (!m_mousePoint) m_mousePoint = new QQuickEventPoint; m_mousePoint->reset(state, ev->windowPos(), 0); // mouse is 0 - return this; } -QQuickPointerEvent *QQuickPointerEvent::reset(const QTouchEvent *ev) { +void QQuickPointerEvent::initFromTouch(const QTouchEvent *ev) { m_device = QQuickWindowPrivate::touchDevice(ev->device()); m_event = ev; m_button = Qt::NoButton; @@ -478,7 +490,6 @@ QQuickPointerEvent *QQuickPointerEvent::reset(const QTouchEvent *ev) { for (int i = 0; i < pointCount; ++i) m_touchPoints.at(i)->reset(tps.at(i)); - return this; } QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index b47f55e353..39f99c18a6 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -395,8 +395,11 @@ public: , m_pressedButtons(Qt::NoButton) , m_mousePoint(nullptr) { } - QQuickPointerEvent *reset(const QMouseEvent *ev); - QQuickPointerEvent *reset(const QTouchEvent *ev); + /** Reset the current event to \a ev. + * + * ev must be a touch, mouse or tablet event. + */ + QQuickPointerEvent *reset(const QEvent *ev); const QQuickPointerDevice *device() const { return m_device; } Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; } @@ -437,6 +440,10 @@ protected: QVector m_touchPoints; QQuickEventPoint *m_mousePoint; +private: + void initFromMouse(const QMouseEvent *ev); + void initFromTouch(const QTouchEvent *ev); + Q_DISABLE_COPY(QQuickPointerEvent) }; -- cgit v1.2.3 From c6d9702bb8694062416fe9ca9789157aaab4cdea Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 8 Jul 2016 01:16:11 +0200 Subject: Prefer public API in test Change-Id: Ib161ffaaea57bdf74e02d0f56c3e02d484a26fcc Reviewed-by: Shawn Rutledge --- tests/auto/quick/touchmouse/tst_touchmouse.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index d831889a1e..338b5d343e 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -251,7 +251,7 @@ void tst_TouchMouse::simpleTouchEvent() QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); - QCOMPARE(windowPriv->mouseGrabberItem, eventItem1); + QCOMPARE(window->mouseGrabberItem(), eventItem1); QPoint localPos = eventItem1->mapFromScene(p1).toPoint(); QPoint globalPos = window->mapToGlobal(p1); @@ -574,7 +574,7 @@ void tst_TouchMouse::buttonOnFlickable() QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); QCOMPARE(windowPriv->touchMouseId, 0); QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1); - QCOMPARE(windowPriv->mouseGrabberItem, eventItem1); + QCOMPARE(window->mouseGrabberItem(), eventItem1); p1 += QPoint(0, -10); QPoint p2 = p1 + QPoint(0, -10); @@ -592,7 +592,7 @@ void tst_TouchMouse::buttonOnFlickable() QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchUpdate); QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove); - QCOMPARE(windowPriv->mouseGrabberItem, flickable); + QCOMPARE(window->mouseGrabberItem(), flickable); QCOMPARE(windowPriv->touchMouseId, 0); QCOMPARE(windowPriv->itemForTouchPointId[0], flickable); QVERIFY(flickable->isMovingVertically()); @@ -659,7 +659,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); QCOMPARE(windowPriv->touchMouseId, 0); QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1); - QCOMPARE(windowPriv->mouseGrabberItem, eventItem1); + QCOMPARE(window->mouseGrabberItem(), eventItem1); p1 += QPoint(0, -10); QPoint p2 = p1 + QPoint(0, -10); @@ -675,7 +675,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() // flickable should have the mouse grab, and have moved the itemForTouchPointId // for the touchMouseId to the new grabber. - QCOMPARE(windowPriv->mouseGrabberItem, flickable); + QCOMPARE(window->mouseGrabberItem(), flickable); QCOMPARE(windowPriv->touchMouseId, 0); QCOMPARE(windowPriv->itemForTouchPointId[0], flickable); @@ -1092,8 +1092,8 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() QQuickTouchUtils::flush(window); QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); - qDebug() << "Mouse Grabber: " << windowPriv->mouseGrabberItem << " itemForTouchPointId: " << windowPriv->itemForTouchPointId; - QCOMPARE(windowPriv->mouseGrabberItem, flickable); + qDebug() << "Mouse Grabber: " << window->mouseGrabberItem() << " itemForTouchPointId: " << windowPriv->itemForTouchPointId; + QCOMPARE(window->mouseGrabberItem(), flickable); // Add a second finger, this should lead to stealing p1 = QPoint(40, 100); -- cgit v1.2.3 From 81867dfbf9c16d4300727a08eed9b5c6c979e0ba Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 6 Apr 2016 15:16:35 +0200 Subject: QML: Introduce write accessors to QQmlAccessors Same idea as the read accessors, but with a slight difference: the property setters do emit signals, so we can't do a direct property write and have to call the setter. However, it does circumvent the meta-calls. There is one gotcha: if a property is intercepted (e.g. by a Behavior), we still have to do the meta-call in order to dispatch the write to the interceptor. According to valgrind, this saves 138 instructions on x86 for every "accessible" property write. Change-Id: I07dbac95613415559ffa1691734a5af7c84721fc Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlabstractbinding_p.h | 9 ++ src/qml/qml/qqmlaccessors_p.h | 7 +- src/qml/qml/qqmlbinding.cpp | 161 +++++++++++++----------------------- src/qml/qml/qqmlbinding_p.h | 4 +- src/qml/qml/qqmlvmemetaobject_p.h | 10 +++ src/quick/items/qquickitem.cpp | 22 ++--- 6 files changed, 94 insertions(+), 119 deletions(-) diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index 45221a4bcd..d25c0c6288 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -91,6 +91,8 @@ public: inline QQmlAbstractBinding *nextBinding() const; + inline bool canUseAccessor() const + { return m_nextBinding.flag2(); } struct RefCount { RefCount() : refCount(0) {} @@ -112,8 +114,15 @@ protected: inline void setNextBinding(QQmlAbstractBinding *); int m_targetIndex; + + // Pointer is the target object to which the binding binds + // flag1 is the updating flag + // flag2 is the enabled flag QFlagPointer m_target; + // Pointer to the next binding in the linked list of bindings. + // flag1 is used for addedToObject + // flag2 indicates if an accessor is can be used (i.e. there is no interceptor on the target) QFlagPointer m_nextBinding; }; diff --git a/src/qml/qml/qqmlaccessors_p.h b/src/qml/qml/qqmlaccessors_p.h index 55562a5307..e98663adfe 100644 --- a/src/qml/qml/qqmlaccessors_p.h +++ b/src/qml/qml/qqmlaccessors_p.h @@ -102,11 +102,15 @@ class QQmlNotifier; } \ } while (false); -#define QML_PRIVATE_ACCESSOR(clazz, cpptype, name, variable) \ +#define QML_PRIVATE_ACCESSOR(clazz, cpptype, name, variable, setter) \ static void clazz ## _ ## name ## Read(QObject *o, void *rv) \ { \ clazz ## Private *d = clazz ## Private::get(static_cast(o)); \ *static_cast(rv) = d->variable; \ + } \ + static void clazz ## _ ## name ## Write(QObject *o, void *rv) \ + { \ + static_cast(o)->setter(*static_cast(rv)); \ } #define QML_PROPERTY_NAME(name) #name, sizeof #name - 1 @@ -115,6 +119,7 @@ class QQmlAccessors { public: void (*read)(QObject *object, void *output); + void (*write)(QObject *object, void *output); void (*notifier)(QObject *object, QQmlNotifier **notifier); }; diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 9f86e448ef..78d0a4386f 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -43,6 +43,7 @@ #include "qqmlcontext.h" #include "qqmlinfo.h" #include "qqmldata_p.h" +#include "qqmlaccessors_p.h" #include #include #include @@ -210,21 +211,10 @@ protected: } }; -#define QUICK_STORE(cpptype, conversion) \ - { \ - cpptype o = (conversion); \ - int status = -1; \ - void *argv[] = { &o, 0, &status, &flags }; \ - QMetaObject::metacall(m_target.data(), QMetaObject::WriteProperty, coreIndex, argv); \ - return true; \ - } \ - - template class GenericBinding: public QQmlBinding { protected: - void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, QQmlPropertyPrivate::WriteFlags flags, QV4::Scope &scope, const QV4::ScopedFunctionObject &f) Q_DECL_OVERRIDE Q_DECL_FINAL @@ -238,13 +228,8 @@ protected: binding->QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); bool error = false; - if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) { - if (StaticPropType == QMetaType::UnknownType) { - error = !write(scope.result, isUndefined, flags); - } else { - error = !fastWrite(scope.result, isUndefined, flags); - } - } + if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) + error = !write(scope.result, isUndefined, flags); if (!watcher.wasDeleted()) { @@ -264,99 +249,72 @@ protected: ep->dereferenceScarceResources(); } -private: // Returns true if successful, false if an error description was set on expression - Q_ALWAYS_INLINE bool fastWrite(const QV4::Value &result, bool isUndefined, - QQmlPropertyPrivate::WriteFlags flags) + Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, + QQmlPropertyPrivate::WriteFlags flags) { - int coreIndex = getPropertyCoreIndex(); - - Q_ASSERT(m_target.data()); - - if (Q_LIKELY(!isUndefined && coreIndex != -1 )) { - switch (StaticPropType) { + QQmlPropertyData pd = getPropertyData(); + int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded. + if (propertyType == QMetaType::UnknownType) + propertyType = pd.propType; + Q_ASSERT(targetObject()); + + if (Q_LIKELY(!isUndefined && !pd.isValueTypeVirtual())) { + switch (propertyType) { + case QMetaType::Bool: + if (result.isBoolean()) + return doStore(result.booleanValue(), pd, flags); + else + return doStore(result.toBoolean(), pd, flags); case QMetaType::Int: if (result.isInteger()) - QUICK_STORE(int, result.integerValue()) + return doStore(result.integerValue(), pd, flags); else if (result.isNumber()) - QUICK_STORE(int, result.doubleValue()) + return doStore(result.doubleValue(), pd, flags); break; case QMetaType::Double: if (result.isNumber()) - QUICK_STORE(double, result.asDouble()) + return doStore(result.asDouble(), pd, flags); break; case QMetaType::Float: if (result.isNumber()) - QUICK_STORE(float, result.asDouble()) + return doStore(result.asDouble(), pd, flags); break; case QMetaType::QString: if (result.isString()) - QUICK_STORE(QString, result.toQStringNoThrow()) + return doStore(result.toQStringNoThrow(), pd, flags); break; default: if (const QV4::QQmlValueTypeWrapper *vtw = result.as()) { - if (vtw->d()->valueType->typeId == StaticPropType) { - return vtw->write(m_target.data(), coreIndex); + if (vtw->d()->valueType->typeId == pd.propType) { + return vtw->write(m_target.data(), pd.coreIndex); } } break; } } - return slowWrite(result, isUndefined, flags); + return slowWrite(pd, result, isUndefined, flags); } -}; -// Returns true if successful, false if an error description was set on expression -Q_ALWAYS_INLINE bool QQmlBinding::write(const QV4::Value &result, bool isUndefined, - QQmlPropertyPrivate::WriteFlags flags) -{ - Q_ASSERT(m_target.data()); - - int coreIndex = getPropertyCoreIndex(); - int propertyType = getPropertyType(); - - Q_ASSERT(m_target.data()); - - if (Q_LIKELY(!isUndefined && coreIndex != -1 )) { - switch (propertyType) { - case QMetaType::Int: - if (result.isInteger()) - QUICK_STORE(int, result.integerValue()) - else if (result.isNumber()) - QUICK_STORE(int, result.doubleValue()) - break; - case QMetaType::Double: - if (result.isNumber()) - QUICK_STORE(double, result.asDouble()) - break; - case QMetaType::Float: - if (result.isNumber()) - QUICK_STORE(float, result.asDouble()) - break; - case QMetaType::QString: - if (result.isString()) - QUICK_STORE(QString, result.toQStringNoThrow()) - break; - default: - if (const QV4::QQmlValueTypeWrapper *vtw = result.as()) { - if (vtw->d()->valueType->typeId == propertyType) { - return vtw->write(m_target.data(), coreIndex); - } - } - break; + template + Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData &pd, QQmlPropertyPrivate::WriteFlags flags) const + { + void *o = &value; + if (pd.hasAccessors() && canUseAccessor()) { + pd.accessors->write(m_target.data(), o); + } else { + int status = -1; + void *argv[] = { o, 0, &status, &flags }; + QMetaObject::metacall(targetObject(), QMetaObject::WriteProperty, pd.coreIndex, argv); } + return true; } +}; - return slowWrite(result, isUndefined, flags); -} -#undef QUICK_STORE - -Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QV4::Value &result, bool isUndefined, - QQmlPropertyPrivate::WriteFlags flags) +Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const QV4::Value &result, + bool isUndefined, QQmlPropertyPrivate::WriteFlags flags) { - QQmlPropertyData core = getPropertyData(); - QQmlEngine *engine = context()->engine; QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); @@ -505,6 +463,13 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) setEnabledFlag(e); setNotifyOnValueChanged(e); + m_nextBinding.clearFlag2(); + if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(targetObject())) { + int coreIndex = getPropertyCoreIndex(); + if (coreIndex != -1 && !interceptorMetaObject->intercepts(coreIndex)) + m_nextBinding.setFlag2(); + } + if (e) update(flags); } @@ -611,38 +576,26 @@ Q_ALWAYS_INLINE int QQmlBinding::getPropertyCoreIndex() const } } -int QQmlBinding::getPropertyType() const -{ - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(m_targetIndex, &coreIndex); - - QQmlData *data = QQmlData::get(*m_target, false); - Q_ASSERT(data && data->propertyCache); - - QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); - Q_ASSERT(propertyData); - - if (valueTypeIndex == -1) - return propertyData->propType; - else - return QMetaType::UnknownType; -} - QQmlBinding *QQmlBinding::newBinding(const QQmlPropertyData *property) { const int type = (property && property->isFullyResolved()) ? property->propType : QMetaType::UnknownType; if (type == qMetaTypeId()) { return new QQmlBindingBinding; - } else if (type == QMetaType::Int) { + } + + switch (type) { + case QMetaType::Bool: + return new GenericBinding; + case QMetaType::Int: return new GenericBinding; - } else if (type == QMetaType::Double) { + case QMetaType::Double: return new GenericBinding; - } else if (type == QMetaType::Float) { + case QMetaType::Float: return new GenericBinding; - } else if (type == QMetaType::QString) { + case QMetaType::QString: return new GenericBinding; - } else { + default: return new GenericBinding; } } diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index ad1b24f340..a7c90603f6 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -110,9 +110,7 @@ protected: int getPropertyCoreIndex() const; int getPropertyType() const; - bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyPrivate::WriteFlags flags); - - bool slowWrite(const QV4::Value &result, bool isUndefined, + bool slowWrite(const QQmlPropertyData &core, const QV4::Value &result, bool isUndefined, QQmlPropertyPrivate::WriteFlags flags); private: diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index 5aa141d026..4a81fc50d2 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -72,6 +72,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -105,6 +106,15 @@ public: // Used by auto-tests for inspection QQmlPropertyCache *propertyCache() const { return cache; } + bool intercepts(int coreIndex) const + { + for (auto it = interceptors; it; it = it->m_next) { + if (it->m_coreIndex == coreIndex) + return true; + } + return false; + } + protected: int metaCall(QObject *o, QMetaObject::Call c, int id, void **a) Q_DECL_OVERRIDE; bool intercept(QMetaObject::Call c, int id, void **a); diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index dc0c9df132..391a785b8f 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -117,17 +117,17 @@ static void QQuickItem_parentNotifier(QObject *o, QQmlNotifier **n) *n = &d->parentNotifier; } -QML_PRIVATE_ACCESSOR(QQuickItem, QQuickItem *, parent, parentItem) -QML_PRIVATE_ACCESSOR(QQuickItem, qreal, x, x) -QML_PRIVATE_ACCESSOR(QQuickItem, qreal, y, y) -QML_PRIVATE_ACCESSOR(QQuickItem, qreal, width, width) -QML_PRIVATE_ACCESSOR(QQuickItem, qreal, height, height) - -static QQmlAccessors QQuickItem_parent = { QQuickItem_parentRead, QQuickItem_parentNotifier }; -static QQmlAccessors QQuickItem_x = { QQuickItem_xRead, 0 }; -static QQmlAccessors QQuickItem_y = { QQuickItem_yRead, 0 }; -static QQmlAccessors QQuickItem_width = { QQuickItem_widthRead, 0 }; -static QQmlAccessors QQuickItem_height = { QQuickItem_heightRead, 0 }; +QML_PRIVATE_ACCESSOR(QQuickItem, QQuickItem *, parent, parentItem, setParentItem) +QML_PRIVATE_ACCESSOR(QQuickItem, qreal, x, x, setX) +QML_PRIVATE_ACCESSOR(QQuickItem, qreal, y, y, setY) +QML_PRIVATE_ACCESSOR(QQuickItem, qreal, width, width, setWidth) +QML_PRIVATE_ACCESSOR(QQuickItem, qreal, height, height, setHeight) + +static QQmlAccessors QQuickItem_parent = { QQuickItem_parentRead, QQuickItem_parentWrite, QQuickItem_parentNotifier }; +static QQmlAccessors QQuickItem_x = { QQuickItem_xRead, QQuickItem_xWrite, 0 }; +static QQmlAccessors QQuickItem_y = { QQuickItem_yRead, QQuickItem_yWrite, 0 }; +static QQmlAccessors QQuickItem_width = { QQuickItem_widthRead, QQuickItem_widthWrite, 0 }; +static QQmlAccessors QQuickItem_height = { QQuickItem_heightRead, QQuickItem_heightWrite, 0 }; QML_DECLARE_PROPERTIES(QQuickItem) { { QML_PROPERTY_NAME(parent), 0, &QQuickItem_parent }, -- cgit v1.2.3 From 3bf62521fe1bbc7cbbb394546a2ab7fa792599cf Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Thu, 7 Jul 2016 21:04:52 +0200 Subject: QQuick{Item,Window}: Port simple loops from foreach to range-for I'm only doing this for the very simple, obviously OK cases: for any loop that involved possible reentrancy (which in turn can lead to list modification), I have not yet ported them. Change-Id: I9fa7a989707b3e86afbda9b4b83e6870a2d7a5b6 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 10 +++++----- src/quick/items/qquickwindow.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 391a785b8f..988e1d880a 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -102,7 +102,7 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1) << item->hasActiveFocus() << item->isFocusScope() << item; - foreach (QQuickItem *child, item->childItems()) { + for (QQuickItem *child : item->childItems()) { debugFocusTree( child, item->isFocusScope() || !scope ? item : scope, @@ -3356,7 +3356,7 @@ void QQuickItemPrivate::resources_clear(QQmlListProperty *prop) QQuickItem *quickItem = static_cast(prop->object); QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(quickItem); if (quickItemPrivate->extra.isAllocated()) {//If extra is not allocated resources is empty. - foreach (QObject *object, quickItemPrivate->extra->resourcesList) { + for (QObject *object : qAsConst(quickItemPrivate->extra->resourcesList)) { qmlobject_disconnect(object, QObject, SIGNAL(destroyed(QObject*)), quickItem, QQuickItem, SLOT(_q_resourceObjectDeleted(QObject*))); } @@ -7053,7 +7053,7 @@ void QQuickItemPrivate::setHasCursorInChild(bool hasCursor) if (!hasCursor && subtreeCursorEnabled) { if (hasCursor) return; // nope! sorry, I have a cursor myself - foreach (QQuickItem *otherChild, childItems) { + for (QQuickItem *otherChild : qAsConst(childItems)) { QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild); if (otherChildPrivate->subtreeCursorEnabled || otherChildPrivate->hasCursor) return; // nope! sorry, something else wants it kept on. @@ -7078,7 +7078,7 @@ void QQuickItemPrivate::setHasHoverInChild(bool hasHover) if (!hasHover && subtreeHoverEnabled) { if (hoverEnabled) return; // nope! sorry, I need hover myself - foreach (QQuickItem *otherChild, childItems) { + for (QQuickItem *otherChild : qAsConst(childItems)) { QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild); if (otherChildPrivate->subtreeHoverEnabled || otherChildPrivate->hoverEnabled) return; // nope! sorry, something else wants it kept on. @@ -7099,7 +7099,7 @@ void QQuickItemPrivate::markObjects(QV4::ExecutionEngine *e) Q_Q(QQuickItem); QV4::QObjectWrapper::markWrapper(q, e); - foreach (QQuickItem *child, childItems) + for (QQuickItem *child : qAsConst(childItems)) QQuickItemPrivate::get(child)->markObjects(e); } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 884e6b0d24..aead9a71b5 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -4438,7 +4438,7 @@ void QQuickWindowPrivate::runAndClearJobs(QList *jobs) jobs->clear(); renderJobMutex.unlock(); - foreach (QRunnable *r, jobList) { + for (QRunnable *r : qAsConst(jobList)) { r->run(); delete r; } -- cgit v1.2.3 From 16adb102b2df540a8e79a59d5d0d3c227ecb4f64 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 8 Jul 2016 10:07:26 +0200 Subject: QQuickItem: Merge repeated checks into one Change-Id: I8fb6801230f7e19242d7ebe967cd3ced6250ae38 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 988e1d880a..6d3c93a0af 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -4868,14 +4868,16 @@ void QQuickItem::componentComplete() QQuickAnchorsPrivate::get(d->_anchors)->updateOnComplete(); } - if (d->extra.isAllocated() && d->extra->layer) - d->extra->layer->componentComplete(); + if (d->extra.isAllocated()) { + if (d->extra->layer) + d->extra->layer->componentComplete(); - if (d->extra.isAllocated() && d->extra->keyHandler) - d->extra->keyHandler->componentComplete(); + if (d->extra->keyHandler) + d->extra->keyHandler->componentComplete(); - if (d->extra.isAllocated() && d->extra->contents) - d->extra->contents->complete(); + if (d->extra->contents) + d->extra->contents->complete(); + } if (d->window && d->dirtyAttributes) { d->addToDirtyList(); -- cgit v1.2.3 From f640a9b7bbefd069f9ee3bf9435e48ccc1f19d1a Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Tue, 5 Jul 2016 09:31:18 +0200 Subject: tst_qquicklistview: Style fixes Change-Id: I18983b06d28bf8f31043070db5aa6c1540062197 Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquicklistview/tst_qquicklistview.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 48230891ba..08c0887fcf 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -657,11 +657,9 @@ void tst_QQuickListView::inserted_more(QQuickItemView::VerticalLayoutDirection v #endif QList visibleItems = QQuickItemViewPrivate::get(listview)->visibleItems; - for (QList::const_iterator itemIt = visibleItems.begin(); itemIt != visibleItems.end(); ++itemIt) - { + for (QList::const_iterator itemIt = visibleItems.begin(); itemIt != visibleItems.end(); ++itemIt) { FxViewItem *item = *itemIt; - if (item->item->position().y() >= 0 && item->item->position().y() < listview->height()) - { + if (item->item->position().y() >= 0 && item->item->position().y() < listview->height()) { QVERIFY(!QQuickItemPrivate::get(item->item)->culled); } } -- cgit v1.2.3 From 8d92e595e0e8cf19f60db2fce4a543265c9130e9 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 8 Jul 2016 10:53:20 +0200 Subject: QQuickItem: Port a number of manual loops to range-for Change-Id: I8f4aaee710b0d25c13ffaa60d48f173bf61fe361 Reviewed-by: Simon Hausmann --- src/quick/items/qquickitem.cpp | 96 ++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 6d3c93a0af..8acef9b1db 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -181,8 +181,8 @@ QQuickTransform::QQuickTransform(QQuickTransformPrivate &dd, QObject *parent) QQuickTransform::~QQuickTransform() { Q_D(QQuickTransform); - for (int ii = 0; ii < d->items.count(); ++ii) { - QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii)); + for (QQuickItem *item : qAsConst(d->items)) { + QQuickItemPrivate *p = QQuickItemPrivate::get(item); p->transforms.removeOne(this); p->dirty(QQuickItemPrivate::Transform); } @@ -191,8 +191,8 @@ QQuickTransform::~QQuickTransform() void QQuickTransform::update() { Q_D(QQuickTransform); - for (int ii = 0; ii < d->items.count(); ++ii) { - QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii)); + for (QQuickItem *item : qAsConst(d->items)) { + QQuickItemPrivate *p = QQuickItemPrivate::get(item); p->dirty(QQuickItemPrivate::Transform); } } @@ -204,9 +204,7 @@ QQuickContents::QQuickContents(QQuickItem *item) QQuickContents::~QQuickContents() { - QList children = m_item->childItems(); - for (int i = 0; i < children.count(); ++i) { - QQuickItem *child = children.at(i); + for (QQuickItem *child : m_item->childItems()) { QQuickItemPrivate::get(child)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); } } @@ -229,9 +227,8 @@ bool QQuickContents::calcHeight(QQuickItem *changed) } else { qreal top = std::numeric_limits::max(); qreal bottom = -std::numeric_limits::max(); - QList children = m_item->childItems(); - for (int i = 0; i < children.count(); ++i) { - QQuickItem *child = children.at(i); + const QList children = m_item->childItems(); + for (QQuickItem *child : qAsConst(children)) { qreal y = child->y(); if (y + child->height() > bottom) bottom = y + child->height(); @@ -264,9 +261,8 @@ bool QQuickContents::calcWidth(QQuickItem *changed) } else { qreal left = std::numeric_limits::max(); qreal right = -std::numeric_limits::max(); - QList children = m_item->childItems(); - for (int i = 0; i < children.count(); ++i) { - QQuickItem *child = children.at(i); + const QList children = m_item->childItems(); + for (QQuickItem *child : qAsConst(children)) { qreal x = child->x(); if (x + child->width() > right) right = x + child->width(); @@ -285,9 +281,7 @@ void QQuickContents::complete() { QQuickItemPrivate::get(m_item)->addItemChangeListener(this, QQuickItemPrivate::Children); - QList children = m_item->childItems(); - for (int i = 0; i < children.count(); ++i) { - QQuickItem *child = children.at(i); + for (QQuickItem *child : m_item->childItems()) { QQuickItemPrivate::get(child)->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); //###what about changes to visibility? } @@ -1342,8 +1336,7 @@ void QQuickKeysAttached::componentComplete() #ifndef QT_NO_IM Q_D(QQuickKeysAttached); if (d->item) { - for (int ii = 0; ii < d->targets.count(); ++ii) { - QQuickItem *targetItem = d->targets.at(ii); + for (QQuickItem *targetItem : qAsConst(d->targets)) { if (targetItem && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) { d->item->setFlag(QQuickItem::ItemAcceptsInputMethod); break; @@ -1365,11 +1358,10 @@ void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post) // first process forwards if (d->item && d->item->window()) { d->inPress = true; - for (int ii = 0; ii < d->targets.count(); ++ii) { - QQuickItem *i = d->targets.at(ii); - if (i && i->isVisible()) { + for (QQuickItem *targetItem : qAsConst(d->targets)) { + if (targetItem && targetItem->isVisible()) { event->accept(); - QCoreApplication::sendEvent(i, event); + QCoreApplication::sendEvent(targetItem, event); if (event->isAccepted()) { d->inPress = false; return; @@ -1409,11 +1401,10 @@ void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post) if (d->item && d->item->window()) { d->inRelease = true; - for (int ii = 0; ii < d->targets.count(); ++ii) { - QQuickItem *i = d->targets.at(ii); - if (i && i->isVisible()) { + for (QQuickItem *targetItem : qAsConst(d->targets)) { + if (targetItem && targetItem->isVisible()) { event->accept(); - QCoreApplication::sendEvent(i, event); + QCoreApplication::sendEvent(targetItem, event); if (event->isAccepted()) { d->inRelease = false; return; @@ -1437,12 +1428,11 @@ void QQuickKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post) Q_D(QQuickKeysAttached); if (post == m_processPost && d->item && !d->inIM && d->item->window()) { d->inIM = true; - for (int ii = 0; ii < d->targets.count(); ++ii) { - QQuickItem *i = d->targets.at(ii); - if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod)) { - d->item->window()->sendEvent(i, event); + for (QQuickItem *targetItem : qAsConst(d->targets)) { + if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) { + d->item->window()->sendEvent(targetItem, event); if (event->isAccepted()) { - d->imeItem = i; + d->imeItem = targetItem; d->inIM = false; return; } @@ -1457,13 +1447,12 @@ QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const { Q_D(const QQuickKeysAttached); if (d->item) { - for (int ii = 0; ii < d->targets.count(); ++ii) { - QQuickItem *i = d->targets.at(ii); - if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod) && i == d->imeItem) { - //### how robust is i == d->imeItem check? - QVariant v = i->inputMethodQuery(query); + for (QQuickItem *targetItem : qAsConst(d->targets)) { + if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod) && targetItem == d->imeItem) { + //### how robust is targetItem == d->imeItem check? + QVariant v = targetItem->inputMethodQuery(query); if (v.userType() == QVariant::RectF) - v = d->item->mapRectFromItem(i, v.toRectF()); //### cost? + v = d->item->mapRectFromItem(targetItem, v.toRectF()); //### cost? return v; } } @@ -1620,11 +1609,9 @@ void QQuickItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit) if (isMirrorImplicit) setLayoutMirror(inherit ? inheritedLayoutMirror : false); - for (int i = 0; i < childItems.count(); ++i) { - if (QQuickItem *child = qmlobject_cast(childItems.at(i))) { - QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); - childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent); - } + for (QQuickItem *child : qAsConst(childItems)) { + QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); + childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent); } } @@ -2393,8 +2380,7 @@ QQuickItem::~QQuickItem() remove themselves from our list of transforms when that list has already been destroyed after ~QQuickItem() has run. */ - for (int ii = 0; ii < d->transforms.count(); ++ii) { - QQuickTransform *t = d->transforms.at(ii); + for (QQuickTransform *t : qAsConst(d->transforms)) { QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t); tp->items.removeOne(this); } @@ -2885,8 +2871,8 @@ QList QQuickItemPrivate::paintOrderChildItems() const // If none of the items have set Z then the paint order list is the same as // the childItems list. This is by far the most common case. bool haveZ = false; - for (int i = 0; i < childItems.count(); ++i) { - if (QQuickItemPrivate::get(childItems.at(i))->z() != 0.) { + for (QQuickItem *childItem : qAsConst(childItems)) { + if (QQuickItemPrivate::get(childItem)->z() != 0.) { haveZ = true; break; } @@ -2985,8 +2971,7 @@ void QQuickItemPrivate::refWindow(QQuickWindow *c) if (!parentItem) QQuickWindowPrivate::get(window)->parentlessItems.insert(q); - for (int ii = 0; ii < childItems.count(); ++ii) { - QQuickItem *child = childItems.at(ii); + for (QQuickItem *child : qAsConst(childItems)) { QQuickItemPrivate::get(child)->refWindow(c); } @@ -3038,8 +3023,7 @@ void QQuickItemPrivate::derefWindow() paintNode = 0; - for (int ii = 0; ii < childItems.count(); ++ii) { - QQuickItem *child = childItems.at(ii); + for (QQuickItem *child : qAsConst(childItems)) { QQuickItemPrivate::get(child)->derefWindow(); } @@ -3497,8 +3481,7 @@ void QQuickItemPrivate::transform_clear(QQmlListProperty *prop) QQuickItem *that = static_cast(prop->object); QQuickItemPrivate *p = QQuickItemPrivate::get(that); - for (int ii = 0; ii < p->transforms.count(); ++ii) { - QQuickTransform *t = p->transforms.at(ii); + for (QQuickTransform *t : qAsConst(p->transforms)) { QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t); tp->items.removeOne(that); } @@ -5775,8 +5758,9 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) } bool childVisibilityChanged = false; - for (int ii = 0; ii < childItems.count(); ++ii) - childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible); + for (QQuickItem *childItem : qAsConst(childItems)) { + childVisibilityChanged |= QQuickItemPrivate::get(childItem)->setEffectiveVisibleRecur(newEffectiveVisible); + } itemChange(QQuickItem::ItemVisibleHasChanged, effectiveVisible); #ifndef QT_NO_ACCESSIBILITY @@ -5825,8 +5809,8 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec } } - for (int ii = 0; ii < childItems.count(); ++ii) { - QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur( + for (QQuickItem *childItem : qAsConst(childItems)) { + QQuickItemPrivate::get(childItem)->setEffectiveEnableRecur( (flags & QQuickItem::ItemIsFocusScope) && scope ? q : scope, newEffectiveEnable); } -- cgit v1.2.3 From 0d10a2592a5c08a8c3cd637428b444936cab0de5 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Thu, 7 Jul 2016 13:01:41 +0200 Subject: QQuickLayout: Remove unused QQuickItemPublic struct Change-Id: I4e0e929118d708a4157c07df93ce5eb4083263ee Reviewed-by: Shawn Rutledge --- src/imports/layouts/qquicklayout.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/imports/layouts/qquicklayout.cpp b/src/imports/layouts/qquicklayout.cpp index 31e2238feb..98e2aea44f 100644 --- a/src/imports/layouts/qquicklayout.cpp +++ b/src/imports/layouts/qquicklayout.cpp @@ -754,11 +754,6 @@ bool QQuickLayout::shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&in d->m_ignoredItems << child; return ignoreItem; } -struct QQuickItemPublic : public QQuickItem { - static bool isCompleted(QQuickItem *item) { - return static_cast(item)->isComponentComplete(); - } -}; void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value) { -- cgit v1.2.3 From 9ae9d5500c733692f3eb858a9f2a4082dcb975dd Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 8 Jul 2016 16:03:23 +0200 Subject: IR: Remove unused QHash Hasn't been used since it was introduced AFAICT. Change-Id: Id2691ae9abf1275319fdb618a5e3f8dd527ec72f Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder_p.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 5a07684b9f..af5b3d4ed5 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -436,7 +436,6 @@ struct Q_QML_PRIVATE_EXPORT Document quint32 unitFlags; QQmlRefPointer javaScriptCompilationUnit; - QHash extraSignalParameters; int registerString(const QString &str) { return jsGenerator.registerString(str); } QString stringAt(int index) const { return jsGenerator.stringForIndex(index); } -- cgit v1.2.3 From 6349a08f39c48c9786d82b7e6bd2249aa8bc8b80 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 8 Jul 2016 10:57:25 +0200 Subject: Add explanation to tst_QQuickMouseArea::moveAndReleaseWithoutPress The test makes sure that no events are delivered after the initial press is released. In this context QTRY_* makes little sense, since we are confirming that the defaults have not changed. Thus rather spin the event loop 11 times and hope that if we would accidentally deliver events that we shouldn't, it will be caught in that time. Change-Id: Iadeed07a81978aa6679e5513c1fe0aebcaeeb997 Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 46ccb8f80c..9c627ad69c 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -1740,11 +1740,17 @@ void tst_QQuickMouseArea::moveAndReleaseWithoutPress() QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100)); + // the press was not accepted, make sure there is no move or release event QTest::mouseMove(&window, QPoint(110,110), 50); - QTRY_COMPARE(root->property("hadMove").toBool(), false); + + // use qwait here because we want to make sure an event does NOT happen + // the test fails if the default state changes, while it shouldn't + QTest::qWait(100); + QCOMPARE(root->property("hadMove").toBool(), false); QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(110,110)); - QTRY_COMPARE(root->property("hadRelease").toBool(), false); + QTest::qWait(100); + QCOMPARE(root->property("hadRelease").toBool(), false); } void tst_QQuickMouseArea::nestedStopAtBounds_data() -- cgit v1.2.3 From 5f67301005adc431142da40f156cfe3d3b776d51 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 8 Jul 2016 11:14:37 +0200 Subject: QQuickItem: Remove QML_LEAK_CHECK This seems to have been intended as a poor mans massif, but ultimately is rather pointless, as it isn't able to tell you *where* the items were allocated, just that some were still allocated -- and only in debug builds, too. Just remove it and rely on more conventional tools (debuggers, profilers) for this sort of thing. Change-Id: Ib87da49c250166616e90cff92be355021a4622cf Reviewed-by: Volker Krause Reviewed-by: Mitch Curtis Reviewed-by: Ulf Hermann --- src/quick/items/qquickitem.cpp | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 8acef9b1db..1978349a36 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -88,10 +88,6 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(DBG_MOUSE_TARGET) Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) -#ifndef QT_NO_DEBUG -static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"); -#endif - void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1) { if (DBG_FOCUS().isEnabled(QtDebugMsg)) { @@ -2315,29 +2311,11 @@ QQuickItem::QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent) d->init(parent); } -#ifndef QT_NO_DEBUG -static int qt_item_count = 0; - -static void qt_print_item_count() -{ - qDebug("Number of leaked items: %i", qt_item_count); - qt_item_count = -1; -} -#endif - /*! Destroys the QQuickItem. */ QQuickItem::~QQuickItem() { -#ifndef QT_NO_DEBUG - if (qsg_leak_check) { - --qt_item_count; - if (qt_item_count < 0) - qDebug("Item destroyed after qt_print_item_count() was called."); - } -#endif - Q_D(QQuickItem); if (d->windowRefCount > 1) @@ -3181,17 +3159,6 @@ QQuickItemPrivate::~QQuickItemPrivate() void QQuickItemPrivate::init(QQuickItem *parent) { -#ifndef QT_NO_DEBUG - if (qsg_leak_check) { - ++qt_item_count; - static bool atexit_registered = false; - if (!atexit_registered) { - atexit(qt_print_item_count); - atexit_registered = true; - } - } -#endif - Q_Q(QQuickItem); registerAccessorProperties(); -- cgit v1.2.3 From ebc0f02d376d34b5bd03fee889547a15d536dc9f Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 7 Jul 2016 22:17:47 +0200 Subject: Remove const from events passed into QQuickPointerEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows resetting the local position of events from the outside which helps when using the events for items expecting the old event types. Change-Id: Ifbcb8ee4986e252d791712c8ba612463ac209a68 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 10 +++++----- src/quick/items/qquickevents_p_p.h | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index d261f76d6c..d7a33b7dfb 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -439,20 +439,20 @@ Item { */ -QQuickPointerEvent *QQuickPointerEvent::reset(const QEvent *ev) { +QQuickPointerEvent *QQuickPointerEvent::reset(QEvent *ev) { if (ev->type() >= QEvent::MouseButtonPress && ev->type() <= QEvent::MouseMove) { - initFromMouse(static_cast(ev)); + initFromMouse(static_cast(ev)); } else if ((ev->type() >= QEvent::TouchBegin && ev->type() <= QEvent::TouchEnd) || ev->type() == QEvent::TouchCancel) { - initFromTouch(static_cast(ev)); + initFromTouch(static_cast(ev)); } else { Q_ASSERT_X(false, "", "invalid event type"); } return this; } -void QQuickPointerEvent::initFromMouse(const QMouseEvent *ev) { +void QQuickPointerEvent::initFromMouse(QMouseEvent *ev) { m_device = QQuickWindowPrivate::genericMouseDevice; m_event = ev; m_button = ev->button(); @@ -477,7 +477,7 @@ void QQuickPointerEvent::initFromMouse(const QMouseEvent *ev) { m_mousePoint->reset(state, ev->windowPos(), 0); // mouse is 0 } -void QQuickPointerEvent::initFromTouch(const QTouchEvent *ev) { +void QQuickPointerEvent::initFromTouch(QTouchEvent *ev) { m_device = QQuickWindowPrivate::touchDevice(ev->device()); m_event = ev; m_button = Qt::NoButton; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 39f99c18a6..3a9b36275f 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -399,14 +399,14 @@ public: * * ev must be a touch, mouse or tablet event. */ - QQuickPointerEvent *reset(const QEvent *ev); + QQuickPointerEvent *reset(QEvent *ev); const QQuickPointerDevice *device() const { return m_device; } Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; } Qt::MouseButton button() const { return m_button; } Qt::MouseButtons buttons() const { return m_pressedButtons; } - const QTouchEvent *asTouchEvent() const { + QTouchEvent *asTouchEvent() const { if (!m_event) return nullptr; switch (m_event->type()) { @@ -414,7 +414,7 @@ public: case QEvent::TouchCancel: case QEvent::TouchUpdate: case QEvent::TouchEnd: - return static_cast(m_event); + return static_cast(m_event); default: break; } @@ -434,15 +434,15 @@ protected: protected: const QQuickPointerDevice *m_device; - const QInputEvent *m_event; // original event as received by QQuickWindow + QInputEvent *m_event; // original event as received by QQuickWindow Qt::MouseButton m_button; Qt::MouseButtons m_pressedButtons; QVector m_touchPoints; QQuickEventPoint *m_mousePoint; private: - void initFromMouse(const QMouseEvent *ev); - void initFromTouch(const QTouchEvent *ev); + void initFromMouse(QMouseEvent *ev); + void initFromTouch(QTouchEvent *ev); Q_DISABLE_COPY(QQuickPointerEvent) }; -- cgit v1.2.3 From 28e00b3c901b7cdf7e2dddcf7b53f6022a99bc23 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 7 Jul 2016 22:23:54 +0200 Subject: Add QQuickPointerEvent::asMouseEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the transition to the new events easier, I could imagine that this can be removed again later. Change-Id: Idecf6eeb3a96d1f75d46cc42ccb2f58d1312eb25 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 30 ++++++++++++++++++++++++++++++ src/quick/items/qquickevents_p_p.h | 21 +++++++-------------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index d7a33b7dfb..dc8d0320fa 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -492,4 +492,34 @@ void QQuickPointerEvent::initFromTouch(QTouchEvent *ev) { m_touchPoints.at(i)->reset(tps.at(i)); } +QTouchEvent *QQuickPointerEvent::asTouchEvent() const { + if (!m_event) + return nullptr; + switch (m_event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchCancel: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + return static_cast(m_event); + default: + break; + } + return nullptr; +} + +QMouseEvent *QQuickPointerEvent::asMouseEvent() const { + if (!m_event) + return nullptr; + switch (m_event->type()) { + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + return static_cast(m_event); + default: + return nullptr; + } +} + + QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 3a9b36275f..d3ca5cadf5 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -406,20 +406,13 @@ public: Qt::MouseButton button() const { return m_button; } Qt::MouseButtons buttons() const { return m_pressedButtons; } - QTouchEvent *asTouchEvent() const { - if (!m_event) - return nullptr; - switch (m_event->type()) { - case QEvent::TouchBegin: - case QEvent::TouchCancel: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - return static_cast(m_event); - default: - break; - } - return nullptr; - } + /** Returns the original touch event. */ + QTouchEvent *asTouchEvent() const; + + /** Returns the original mouse event. + * + * Returns nullptr in case the original event was not a mouse event. */ + QMouseEvent *asMouseEvent() const; // helpers for C++ event delivery, not for QML properties int pointCount() const { return asTouchEvent() ? m_touchPoints.count() : 1; } -- cgit v1.2.3 From 1c5171eb9747107ea10b4cc3b694ae064fda8655 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 8 Jul 2016 23:28:21 +0200 Subject: scenegraph: Port foreach to range-for Change-Id: I5e7c144e60959b9230c0757b4ad0e035f5f52f58 Reviewed-by: Simon Hausmann --- .../adaptations/software/qsgsoftwareinternalrectanglenode.cpp | 4 ++-- src/quick/scenegraph/qsgadaptationlayer.cpp | 2 +- src/quick/scenegraph/qsgdefaultcontext.cpp | 4 +++- src/quick/scenegraph/qsgrenderloop.cpp | 2 +- src/quick/scenegraph/qsgwindowsrenderloop.cpp | 4 ++-- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp index ea5124edf9..f6898b3879 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp @@ -117,7 +117,7 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st { //normalize stops bool needsNormalization = false; - foreach (const QGradientStop &stop, stops) { + for (const QGradientStop &stop : qAsConst(stops)) { if (stop.first < 0.0 || stop.first > 1.0) { needsNormalization = true; continue; @@ -274,7 +274,7 @@ bool QSGSoftwareInternalRectangleNode::isOpaque() const if (m_penWidth > 0.0f && m_penColor.alpha() < 255) return false; if (m_stops.count() > 0) { - foreach (QGradientStop stop, m_stops) { + for (const QGradientStop &stop : qAsConst(m_stops)) { if (stop.second.alpha() < 255) return false; } diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 50986e2528..9574f2a48e 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -195,7 +195,7 @@ void QSGDistanceFieldGlyphCache::update() storeGlyphs(distanceFields); #if defined(QSG_DISTANCEFIELD_CACHE_DEBUG) - foreach (Texture texture, m_textures) + for (Texture texture : qAsConst(m_textures)) saveTexture(texture.textureId, texture.size.width(), texture.size.height()); #endif diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp index 8272be987b..6324d84883 100644 --- a/src/quick/scenegraph/qsgdefaultcontext.cpp +++ b/src/quick/scenegraph/qsgdefaultcontext.cpp @@ -155,7 +155,9 @@ void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext qCDebug(QSG_LOG_INFO) << "GL_RENDERER: " << (const char *) funcs->glGetString(GL_RENDERER); qCDebug(QSG_LOG_INFO) << "GL_VERSION: " << (const char *) funcs->glGetString(GL_VERSION); QSet exts = openglRenderContext->openglContext()->extensions(); - QByteArray all; foreach (const QByteArray &e, exts) all += ' ' + e; + QByteArray all; + for (const QByteArray &e : qAsConst(exts)) + all += ' ' + e; qCDebug(QSG_LOG_INFO) << "GL_EXTENSIONS: " << all.constData(); qCDebug(QSG_LOG_INFO) << "Max Texture Size: " << openglRenderContext->maxTextureSize(); qCDebug(QSG_LOG_INFO) << "Debug context: " << format.testOption(QSurfaceFormat::DebugContext); diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 1a9b576af4..e99e8dfe2e 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -100,7 +100,7 @@ void QSGRenderLoop::cleanup() { if (!s_instance) return; - foreach (QQuickWindow *w, s_instance->windows()) { + for (QQuickWindow *w : s_instance->windows()) { QQuickWindowPrivate *wd = QQuickWindowPrivate::get(w); if (wd->windowManager == s_instance) { s_instance->windowDestroyed(w); diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 371f512c6e..743e524a36 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -263,7 +263,7 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window) bool QSGWindowsRenderLoop::anyoneShowing() const { - foreach (const WindowData &wd, m_windows) + for (const WindowData &wd : qAsConst(m_windows)) if (wd.window->isVisible() && wd.window->isExposed() && wd.window->size().isValid()) return true; return false; @@ -382,7 +382,7 @@ void QSGWindowsRenderLoop::render() { RLDEBUG("render"); bool rendered = false; - foreach (const WindowData &wd, m_windows) { + for (const WindowData &wd : qAsConst(m_windows)) { if (wd.pendingUpdate) { const_cast(wd).pendingUpdate = false; renderWindow(wd.window); -- cgit v1.2.3 From e2c296c46b3f922ed12f83b166b1493dfded480e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 11 Apr 2016 12:44:00 +0200 Subject: QML: Pass the kind of geometry change around This prevents re-calculation of what actually changed, and removes the now unused parameter newGeometry. Other than this change calculation, the only place where oldGeometry was used is QQuickListViewPrivate::itemGeometryChanged. To get rid of oldGeometry too, QQuickListViewPrivate now stores the current (i.e. last known) geometry, and updates it in itemGeometryChanged. Change-Id: I8a5286d08a04132c9a4c81de7ce221f5676946e6 Reviewed-by: Frederik Gladhorn Reviewed-by: Robin Burchell --- src/quick/items/qquickanchors.cpp | 32 +++++++------- src/quick/items/qquickanchors_p_p.h | 4 +- src/quick/items/qquickdrag.cpp | 7 +-- src/quick/items/qquickflickable.cpp | 6 +-- src/quick/items/qquickflickable_p_p.h | 2 +- src/quick/items/qquickitem.cpp | 53 +++++++++++----------- src/quick/items/qquickitem_p.h | 26 +++-------- src/quick/items/qquickitemchangelistener_p.h | 59 ++++++++++++++++++++++++- src/quick/items/qquickitemview.cpp | 5 ++- src/quick/items/qquickitemview_p_p.h | 2 +- src/quick/items/qquicklistview.cpp | 26 ++++++----- src/quick/items/qquickloader.cpp | 5 ++- src/quick/items/qquickloader_p_p.h | 2 +- src/quick/items/qquickpathview_p_p.h | 5 +-- src/quick/items/qquickpositioners_p_p.h | 4 +- src/quick/items/qquickshadereffectsource.cpp | 4 +- src/quick/items/qquickshadereffectsource_p.h | 2 +- src/quick/items/qquickview.cpp | 5 ++- src/quick/items/qquickview_p.h | 2 +- src/quickwidgets/qquickwidget.cpp | 5 ++- src/quickwidgets/qquickwidget_p.h | 2 +- tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 6 ++- 22 files changed, 159 insertions(+), 105 deletions(-) diff --git a/src/quick/items/qquickanchors.cpp b/src/quick/items/qquickanchors.cpp index 4cbd41106e..b6978e534e 100644 --- a/src/quick/items/qquickanchors.cpp +++ b/src/quick/items/qquickanchors.cpp @@ -285,26 +285,26 @@ void QQuickAnchorsPrivate::clearItem(QQuickItem *item) } } -int QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem) +QQuickGeometryChange QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem) { - QQuickItemPrivate::GeometryChangeTypes dependency = QQuickItemPrivate::NoChange; + QQuickGeometryChange dependency; if (!controlItem || inDestructor) return dependency; if (fill == controlItem) { if (controlItem == readParentItem(item)) - dependency |= QQuickItemPrivate::SizeChange; + dependency.setSizeChange(true); else //sibling - dependency |= QQuickItemPrivate::GeometryChange; + dependency.setAllChanged(true); return dependency; //exit early } if (centerIn == controlItem) { if (controlItem == readParentItem(item)) - dependency |= QQuickItemPrivate::SizeChange; + dependency.setSizeChange(true); else //sibling - dependency |= QQuickItemPrivate::GeometryChange; + dependency.setAllChanged(true); return dependency; //exit early } @@ -312,9 +312,9 @@ int QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem) (usedAnchors & QQuickAnchors::RightAnchor && rightAnchorItem == controlItem) || (usedAnchors & QQuickAnchors::HCenterAnchor && hCenterAnchorItem == controlItem)) { if (controlItem == readParentItem(item)) - dependency |= QQuickItemPrivate::WidthChange; + dependency.setWidthChange(true); else //sibling - dependency |= QFlags(QQuickItemPrivate::XChange | QQuickItemPrivate::WidthChange); + dependency.setHorizontalChange(true); } if ((usedAnchors & QQuickAnchors::TopAnchor && topAnchorItem == controlItem) || @@ -322,9 +322,9 @@ int QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem) (usedAnchors & QQuickAnchors::VCenterAnchor && vCenterAnchorItem == controlItem) || (usedAnchors & QQuickAnchors::BaselineAnchor && baselineAnchorItem == controlItem)) { if (controlItem == readParentItem(item)) - dependency |= QQuickItemPrivate::HeightChange; + dependency.setHeightChange(true); else //sibling - dependency |= QFlags(QQuickItemPrivate::YChange | QQuickItemPrivate::HeightChange); + dependency.setVerticalChange(true); } return dependency; @@ -336,7 +336,7 @@ void QQuickAnchorsPrivate::addDepend(QQuickItem *item) return; QQuickItemPrivate *p = QQuickItemPrivate::get(item); - p->updateOrAddGeometryChangeListener(this, QFlags(calculateDependency(item))); + p->updateOrAddGeometryChangeListener(this, calculateDependency(item)); } void QQuickAnchorsPrivate::remDepend(QQuickItem *item) @@ -345,7 +345,7 @@ void QQuickAnchorsPrivate::remDepend(QQuickItem *item) return; QQuickItemPrivate *p = QQuickItemPrivate::get(item); - p->updateOrRemoveGeometryChangeListener(this, QFlags(calculateDependency(item))); + p->updateOrRemoveGeometryChangeListener(this, calculateDependency(item)); } bool QQuickAnchors::mirrored() @@ -492,7 +492,7 @@ void QQuickAnchorsPrivate::update() } } -void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newG, const QRectF &oldG) +void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change, const QRectF &) { if (!isItemComplete()) return; @@ -502,11 +502,9 @@ void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newG, } else if (centerIn) { centerInChanged(); } else { - if ((usedAnchors & QQuickAnchors::Horizontal_Mask) - && (newG.x() != oldG.x() || newG.width() != oldG.width())) + if ((usedAnchors & QQuickAnchors::Horizontal_Mask) && change.horizontalChange()) updateHorizontalAnchors(); - if ((usedAnchors & QQuickAnchors::Vertical_Mask) - && (newG.y() != oldG.y() || newG.height() != oldG.height())) + if ((usedAnchors & QQuickAnchors::Vertical_Mask) && change.verticalChange()) updateVerticalAnchors(); } } diff --git a/src/quick/items/qquickanchors_p_p.h b/src/quick/items/qquickanchors_p_p.h index da659946c0..3357e134bf 100644 --- a/src/quick/items/qquickanchors_p_p.h +++ b/src/quick/items/qquickanchors_p_p.h @@ -124,7 +124,7 @@ public: void clearItem(QQuickItem *); - int calculateDependency(QQuickItem *); + QQuickGeometryChange calculateDependency(QQuickItem *); void addDepend(QQuickItem *); void remDepend(QQuickItem *); bool isItemComplete() const; @@ -141,7 +141,7 @@ public: void updateMe(); // QQuickItemGeometryListener interface - void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE; QQuickAnchorsPrivate *anchorPrivate() Q_DECL_OVERRIDE { return this; } bool checkHValid() const; diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp index cc30199253..8a0af6c263 100644 --- a/src/quick/items/qquickdrag.cpp +++ b/src/quick/items/qquickdrag.cpp @@ -82,7 +82,7 @@ public: { } - void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE; void itemParentChanged(QQuickItem *, QQuickItem *parent) Q_DECL_OVERRIDE; void updatePosition(); void restartDrag(); @@ -148,9 +148,10 @@ public: \sa {Qt Quick Examples - Drag and Drop}, {Qt Quick Examples - externaldraganddrop} */ -void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry) +void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change, + const QRectF &) { - if (newGeometry.topLeft() == oldGeometry.topLeft() || !active || itemMoved) + if (!change.positionChange() || !active || itemMoved) return; updatePosition(); } diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 1bcc3cc0f9..a09088dfed 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -292,14 +292,14 @@ void QQuickFlickablePrivate::AxisData::updateVelocity() } } -void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeom, const QRectF &oldGeom) +void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) { Q_Q(QQuickFlickable); if (item == contentItem) { Qt::Orientations orient = 0; - if (newGeom.x() != oldGeom.x()) + if (change.xChange()) orient |= Qt::Horizontal; - if (newGeom.y() != oldGeom.y()) + if (change.yChange()) orient |= Qt::Vertical; if (orient) q->viewportMoved(orient); diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index 3d2b32286f..ac1e39d829 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -194,7 +194,7 @@ public: qreal overShootDistance(qreal size); - void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE; void draggingStarting(); void draggingEnding(); diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 1978349a36..56426fbe37 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -289,15 +289,15 @@ void QQuickContents::updateRect() QQuickItemPrivate::get(m_item)->emitChildrenRectChanged(rectF()); } -void QQuickContents::itemGeometryChanged(QQuickItem *changed, const QRectF &newGeometry, const QRectF &oldGeometry) +void QQuickContents::itemGeometryChanged(QQuickItem *changed, QQuickGeometryChange change, const QRectF &) { Q_UNUSED(changed) bool wChanged = false; bool hChanged = false; //### we can only pass changed if the left edge has moved left, or the right edge has moved right - if (newGeometry.width() != oldGeometry.width() || newGeometry.x() != oldGeometry.x()) + if (change.horizontalChange()) wChanged = calcWidth(/*changed*/); - if (newGeometry.height() != oldGeometry.height() || newGeometry.y() != oldGeometry.y()) + if (change.verticalChange()) hChanged = calcHeight(/*changed*/); if (wChanged || hChanged) updateRect(); @@ -3676,32 +3676,30 @@ void QQuickItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo if (d->_anchors) QQuickAnchorsPrivate::get(d->_anchors)->updateMe(); - bool xChange = (newGeometry.x() != oldGeometry.x()); - bool yChange = (newGeometry.y() != oldGeometry.y()); - bool widthChange = (newGeometry.width() != oldGeometry.width()); - bool heightChange = (newGeometry.height() != oldGeometry.height()); - - const auto listeners = d->changeListeners; - for (const QQuickItemPrivate::ChangeListener &change : listeners) { - if (change.types & QQuickItemPrivate::Geometry) { - if (change.gTypes == QQuickItemPrivate::GeometryChange) { - change.listener->itemGeometryChanged(this, newGeometry, oldGeometry); - } else if ((xChange && (change.gTypes & QQuickItemPrivate::XChange)) || - (yChange && (change.gTypes & QQuickItemPrivate::YChange)) || - (widthChange && (change.gTypes & QQuickItemPrivate::WidthChange)) || - (heightChange && (change.gTypes & QQuickItemPrivate::HeightChange))) { - change.listener->itemGeometryChanged(this, newGeometry, oldGeometry); - } + QQuickGeometryChange change; + QRectF diff(newGeometry.x() - oldGeometry.x(), + newGeometry.y() - oldGeometry.y(), + newGeometry.width() - oldGeometry.width(), + newGeometry.height() - oldGeometry.height()); + change.setXChange(diff.x() != 0); + change.setYChange(diff.y() != 0); + change.setWidthChange(diff.width() != 0); + change.setHeightChange(diff.height() != 0); + + for (const QQuickItemPrivate::ChangeListener &listener : qAsConst(d->changeListeners)) { + if (listener.types & QQuickItemPrivate::Geometry) { + if (change.matches(listener.gTypes)) + listener.listener->itemGeometryChanged(this, change, diff); } } - if (xChange) + if (change.xChange()) emit xChanged(); - if (yChange) + if (change.yChange()) emit yChanged(); - if (widthChange) + if (change.widthChange()) emit widthChanged(); - if (heightChange) + if (change.heightChange()) emit heightChanged(); } @@ -3820,7 +3818,8 @@ void QQuickItemPrivate::removeItemChangeListener(QQuickItemChangeListener *liste changeListeners.removeOne(change); } -void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types) +void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, + QQuickGeometryChange types) { ChangeListener change(listener, types); int index = changeListeners.indexOf(change); @@ -3831,10 +3830,10 @@ void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListen } void QQuickItemPrivate::updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener, - GeometryChangeTypes types) + QQuickGeometryChange types) { ChangeListener change(listener, types); - if (types == NoChange) { + if (types.noChange()) { changeListeners.removeOne(change); } else { int index = changeListeners.indexOf(change); @@ -8118,7 +8117,7 @@ void QQuickItemLayer::itemOpacityChanged(QQuickItem *item) updateOpacity(); } -void QQuickItemLayer::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) +void QQuickItemLayer::itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) { updateGeometry(); } diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index de4dfc9cd7..9ea2712e18 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -98,7 +98,7 @@ public: void complete(); protected: - void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE; void itemDestroyed(QQuickItem *item) Q_DECL_OVERRIDE; void itemChildAdded(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE; void itemChildRemoved(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE; @@ -188,7 +188,7 @@ public: QQuickShaderEffectSource *effectSource() const { return m_effectSource; } - void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE; void itemOpacityChanged(QQuickItem *) Q_DECL_OVERRIDE; void itemParentChanged(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE; void itemSiblingOrderChanged(QQuickItem *) Q_DECL_OVERRIDE; @@ -318,24 +318,12 @@ public: Q_DECLARE_FLAGS(ChangeTypes, ChangeType) - enum GeometryChangeType { - NoChange = 0, - XChange = 0x01, - YChange = 0x02, - WidthChange = 0x04, - HeightChange = 0x08, - SizeChange = WidthChange | HeightChange, - GeometryChange = XChange | YChange | SizeChange - }; - - Q_DECLARE_FLAGS(GeometryChangeTypes, GeometryChangeType) - struct ChangeListener { - ChangeListener(QQuickItemChangeListener *l = Q_NULLPTR, QQuickItemPrivate::ChangeTypes t = 0) : listener(l), types(t), gTypes(GeometryChange) {} - ChangeListener(QQuickItemChangeListener *l, QQuickItemPrivate::GeometryChangeTypes gt) : listener(l), types(Geometry), gTypes(gt) {} + ChangeListener(QQuickItemChangeListener *l = nullptr, QQuickItemPrivate::ChangeTypes t = 0) : listener(l), types(t), gTypes(QQuickGeometryChange::All) {} + ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt) : listener(l), types(Geometry), gTypes(gt) {} QQuickItemChangeListener *listener; QQuickItemPrivate::ChangeTypes types; - QQuickItemPrivate::GeometryChangeTypes gTypes; //NOTE: not used for == + QQuickGeometryChange gTypes; //NOTE: not used for == bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; } }; @@ -389,8 +377,8 @@ public: void addItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types); void removeItemChangeListener(QQuickItemChangeListener *, ChangeTypes types); - void updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types); - void updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types); + void updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, QQuickGeometryChange types); + void updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener, QQuickGeometryChange types); QQuickStateGroup *_states(); QQuickStateGroup *_stateGroup; diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h index 6e3ef25506..7a4949fc0b 100644 --- a/src/quick/items/qquickitemchangelistener_p.h +++ b/src/quick/items/qquickitemchangelistener_p.h @@ -58,12 +58,69 @@ QT_BEGIN_NAMESPACE class QRectF; class QQuickItem; class QQuickAnchorsPrivate; + +class QQuickGeometryChange +{ +public: + enum Kind: int { + Nothing = 0x00, + X = 0x01, + Y = 0x02, + Width = 0x04, + Height = 0x08, + + Size = Width | Height, + All = X | Y | Size + }; + + QQuickGeometryChange(int change = Nothing) + : kind(change) + {} + + bool noChange() const { return kind == Nothing; } + bool anyChange() const { return !noChange(); } + + bool xChange() const { return kind & X; } + bool yChange() const { return kind & Y; } + bool widthChange() const { return kind & Width; } + bool heightChange() const { return kind & Height; } + + bool positionChange() const { return xChange() || yChange(); } + bool sizeChange() const { return widthChange() || heightChange(); } + + bool horizontalChange() const { return xChange() || widthChange(); } + bool verticalChange() const { return yChange() || heightChange(); } + + void setXChange(bool enabled) { set(X, enabled); } + void setYChange(bool enabled) { set(Y, enabled); } + void setWidthChange(bool enabled) { set(Width, enabled); } + void setHeightChange(bool enabled) { set(Height, enabled); } + void setSizeChange(bool enabled) { set(Size, enabled); } + void setAllChanged(bool enabled) { set(All, enabled); } + void setHorizontalChange(bool enabled) { set(X | Width, enabled); } + void setVerticalChange(bool enabled) { set(Y | Height, enabled); } + + void set(int bits, bool enabled) + { + if (enabled) { + kind |= bits; + } else { + kind &= ~bits; + } + } + + bool matches(QQuickGeometryChange other) const { return kind & other.kind; } + +private: + int kind; +}; + class QQuickItemChangeListener { public: virtual ~QQuickItemChangeListener() {} - virtual void itemGeometryChanged(QQuickItem *, const QRectF & /* new */, const QRectF & /* old */ ) {} + virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) {} virtual void itemSiblingOrderChanged(QQuickItem *) {} virtual void itemVisibilityChanged(QQuickItem *) {} virtual void itemOpacityChanged(QQuickItem *) {} diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index aff03b7539..3c1a5a6ebe 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -1205,10 +1205,11 @@ void QQuickItemViewPrivate::showVisibleItems() const } } -void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, + const QRectF &diff) { Q_Q(QQuickItemView); - QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + QQuickFlickablePrivate::itemGeometryChanged(item, change, diff); if (!q->isComponentComplete()) return; diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index 5e104cf208..0893d6e749 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -378,7 +378,7 @@ protected: virtual void updateSectionCriteria() {} virtual void updateSections() {} - void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE; }; diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 4b9b7df98a..cdbac55fa6 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -131,7 +131,7 @@ public: void updateAverage(); - void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) Q_DECL_OVERRIDE; void fixupPosition() Q_DECL_OVERRIDE; void fixup(AxisData &data, qreal minExtent, qreal maxExtent) Q_DECL_OVERRIDE; bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, @@ -1400,10 +1400,12 @@ bool QQuickListViewPrivate::hasStickyFooter() const return footer && footerPositioning != QQuickListView::InlineFooter; } -void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, + const QRectF &diff) { Q_Q(QQuickListView); - QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + + QQuickItemViewPrivate::itemGeometryChanged(item, change, diff); if (!q->isComponentComplete()) return; @@ -1417,29 +1419,31 @@ void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF & } if (item != contentItem && (!highlight || item != highlight->item)) { - if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height()) - || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) { + if ((orient == QQuickListView::Vertical && change.heightChange()) + || (orient == QQuickListView::Horizontal && change.widthChange())) { // if visibleItems.first() has resized, adjust its pos since it is used to // position all subsequent items if (visibleItems.count() && item == visibleItems.first()->item) { FxListItemSG *listItem = static_cast(visibleItems.first()); + const QRectF oldGeometry(x - diff.x(), + y - diff.y(), + width - diff.width(), + height - diff.height()); if (listItem->transitionScheduledOrRunning()) return; if (orient == QQuickListView::Vertical) { const qreal oldItemEndPosition = verticalLayoutDirection == QQuickItemView::BottomToTop ? -oldGeometry.y() : oldGeometry.y() + oldGeometry.height(); - qreal diff = newGeometry.height() - oldGeometry.height(); if (verticalLayoutDirection == QQuickListView::TopToBottom && oldItemEndPosition < q->contentY()) - listItem->setPosition(listItem->position() - diff, true); + listItem->setPosition(listItem->position() - diff.height(), true); else if (verticalLayoutDirection == QQuickListView::BottomToTop && oldItemEndPosition > q->contentY()) - listItem->setPosition(listItem->position() + diff, true); + listItem->setPosition(listItem->position() + diff.height(), true); } else { const qreal oldItemEndPosition = q->effectiveLayoutDirection() == Qt::RightToLeft ? -oldGeometry.x() : oldGeometry.x() + oldGeometry.width(); - qreal diff = newGeometry.width() - oldGeometry.width(); if (q->effectiveLayoutDirection() == Qt::LeftToRight && oldItemEndPosition < q->contentX()) - listItem->setPosition(listItem->position() - diff, true); + listItem->setPosition(listItem->position() - diff.width(), true); else if (q->effectiveLayoutDirection() == Qt::RightToLeft && oldItemEndPosition > q->contentX()) - listItem->setPosition(listItem->position() + diff, true); + listItem->setPosition(listItem->position() + diff.width(), true); } } forceLayoutPolish(); diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 63c9558d7a..9aea9c50df 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -65,11 +65,12 @@ QQuickLoaderPrivate::~QQuickLoaderPrivate() disposeInitialPropertyValues(); } -void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) +void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change + , const QRectF &diff) { if (resizeItem == item) _q_updateSize(false); - QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); + QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, diff); } void QQuickLoaderPrivate::itemImplicitWidthChanged(QQuickItem *) diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h index 9ef89a74d6..fcccbfe4f5 100644 --- a/src/quick/items/qquickloader_p_p.h +++ b/src/quick/items/qquickloader_p_p.h @@ -84,7 +84,7 @@ public: QQuickLoaderPrivate(); ~QQuickLoaderPrivate(); - void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) Q_DECL_OVERRIDE; void itemImplicitWidthChanged(QQuickItem *) Q_DECL_OVERRIDE; void itemImplicitHeightChanged(QQuickItem *) Q_DECL_OVERRIDE; void clear(); diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h index 652af3487f..0441603a4b 100644 --- a/src/quick/items/qquickpathview_p_p.h +++ b/src/quick/items/qquickpathview_p_p.h @@ -76,9 +76,8 @@ public: void init(); - void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE { - if ((newGeometry.size() != oldGeometry.size()) - && (!highlightItem || item != highlightItem)) { + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE { + if (change.sizeChange() && (!highlightItem || item != highlightItem)) { if (QQuickPathViewAttached *att = attached(item)) att->m_percent = -1; scheduleLayout(); diff --git a/src/quick/items/qquickpositioners_p_p.h b/src/quick/items/qquickpositioners_p_p.h index f0fbac2df7..d18f9b3724 100644 --- a/src/quick/items/qquickpositioners_p_p.h +++ b/src/quick/items/qquickpositioners_p_p.h @@ -137,9 +137,9 @@ public: setPositioningDirty(); } - void itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE + void itemGeometryChanged(QQuickItem *, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE { - if (newGeometry.size() != oldGeometry.size()) + if (change.sizeChange()) setPositioningDirty(); } diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp index d74dd99fe8..a60a06f59a 100644 --- a/src/quick/items/qquickshadereffectsource.cpp +++ b/src/quick/items/qquickshadereffectsource.cpp @@ -308,11 +308,11 @@ QQuickItem *QQuickShaderEffectSource::sourceItem() const return m_sourceItem; } -void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect) +void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) { Q_ASSERT(item == m_sourceItem); Q_UNUSED(item); - if (newRect.size() != oldRect.size()) + if (change.sizeChange()) update(); } diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h index de62c21488..ee18bf195a 100644 --- a/src/quick/items/qquickshadereffectsource_p.h +++ b/src/quick/items/qquickshadereffectsource_p.h @@ -168,7 +168,7 @@ protected: void releaseResources() Q_DECL_OVERRIDE; QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; - void itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE; void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE; private: diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp index 1101b88992..0ed9167fb2 100644 --- a/src/quick/items/qquickview.cpp +++ b/src/quick/items/qquickview.cpp @@ -131,14 +131,15 @@ void QQuickViewPrivate::execute() } } -void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) +void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change, + const QRectF &diff) { Q_Q(QQuickView); if (resizeItem == root && resizeMode == QQuickView::SizeViewToRootObject) { // wait for both width and height to be changed resizetimer.start(0,q); } - QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); + QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, diff); } /*! diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h index 1bbff0de0e..c21468ef53 100644 --- a/src/quick/items/qquickview_p.h +++ b/src/quick/items/qquickview_p.h @@ -88,7 +88,7 @@ public: ~QQuickViewPrivate(); void execute(); - void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE; void initResize(); void updateSize(); void setRootObject(QObject *); diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index d9538c5b7d..b2c978f3f6 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -244,14 +244,15 @@ void QQuickWidgetPrivate::execute() } } -void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) +void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change, + const QRectF &diff) { Q_Q(QQuickWidget); if (resizeItem == root && resizeMode == QQuickWidget::SizeViewToRootObject) { // wait for both width and height to be changed resizetimer.start(0,q); } - QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); + QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, diff); } void QQuickWidgetPrivate::render(bool needsSync) diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h index 56a94d1d25..3d64981797 100644 --- a/src/quickwidgets/qquickwidget_p.h +++ b/src/quickwidgets/qquickwidget_p.h @@ -87,7 +87,7 @@ public: ~QQuickWidgetPrivate(); void execute(); - void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) Q_DECL_OVERRIDE; void initResize(); void updateSize(); void updatePosition(); diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 9089e3b422..62133d6475 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -2700,7 +2700,11 @@ struct TestListener : public QQuickItemChangeListener { TestListener(bool remove = false) : remove(remove) { reset(); } - void itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &) override { ++itemGeometryChanges; value = newGeometry; } + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange, const QRectF &) override + { + ++itemGeometryChanges; + value = QRectF(item->x(), item->y(), item->width(), item->height()); + } void itemSiblingOrderChanged(QQuickItem *) override { ++itemSiblingOrderChanges; } void itemVisibilityChanged(QQuickItem *) override { ++itemVisibilityChanges; } void itemOpacityChanged(QQuickItem *) override { ++itemOpacityChanges; } -- cgit v1.2.3 From 681ef6fe7ed5b85a2690e320ff3938ff4e1f539d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 24 Jun 2016 12:42:04 +0200 Subject: QML: Use write accessors when setting constant binding values. Bindings with constant values like: Item { x: 10 } Setting these values is now also done through write accessors. On x86_64 QQmlObjectCreator::setPropertyBinding goes from ~7600 instructions to ~3600 (according to valgrind). Change-Id: I6a9ecfcd21c7b115828df3f235c292675ea9efc5 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlobjectcreator.cpp | 40 +++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 2e2d87509a..94e81cdcc7 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -342,8 +342,12 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const case QVariant::String: { Q_ASSERT(binding->evaluatesToString()); QString value = binding->valueAsString(qmlUnit); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + if (property->hasAccessors()) { + property->accessors->write(_qobject, &value); + } else { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } } break; case QVariant::StringList: { @@ -386,23 +390,35 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double d = binding->valueAsNumber(); int value = int(d); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + if (property->hasAccessors()) { + property->accessors->write(_qobject, &value); + } else { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } break; } break; case QMetaType::Float: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); float value = float(binding->valueAsNumber()); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + if (property->hasAccessors()) { + property->accessors->write(_qobject, &value); + } else { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } } break; case QVariant::Double: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double value = binding->valueAsNumber(); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + if (property->hasAccessors()) { + property->accessors->write(_qobject, &value); + } else { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } } break; case QVariant::Color: { @@ -499,8 +515,12 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const case QVariant::Bool: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean); bool value = binding->valueAsBoolean(); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + if (property->hasAccessors()) { + property->accessors->write(_qobject, &value); + } else { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } } break; case QVariant::Vector3D: { -- cgit v1.2.3 From f2e9e411cdc24683a2b201e6c00ffa6404a0717c Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 7 Jul 2016 22:25:38 +0200 Subject: QQuickPointerEvent: handle MouseButtonDblClick I'm not sure if we realistically get this, but I thought it's better not to forget it. Change-Id: I3f213185ca78303031f5630d023a5cbc7b19575d Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index dc8d0320fa..5f98db98bb 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -460,6 +460,7 @@ void QQuickPointerEvent::initFromMouse(QMouseEvent *ev) { Qt::TouchPointState state = Qt::TouchPointStationary; switch (ev->type()) { case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: state = Qt::TouchPointPressed; break; case QEvent::MouseButtonRelease: -- cgit v1.2.3 From 62bae5c556251eec5d5295c258df3fb85e3c1bcc Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sat, 9 Jul 2016 12:37:04 +0200 Subject: Move detection of event type to separate functions Change-Id: Id7fc9df6b57c63f9cbdbc96f3e141ee509782e59 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 58 +++++++++++++++++++++++--------------- src/quick/items/qquickevents_p_p.h | 4 +++ 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 5f98db98bb..520e5d2704 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -440,11 +440,10 @@ Item { QQuickPointerEvent *QQuickPointerEvent::reset(QEvent *ev) { - if (ev->type() >= QEvent::MouseButtonPress && ev->type() <= QEvent::MouseMove) - { + m_event = static_cast(ev); + if (isMouseEvent()) { initFromMouse(static_cast(ev)); - } else if ((ev->type() >= QEvent::TouchBegin && ev->type() <= QEvent::TouchEnd) - || ev->type() == QEvent::TouchCancel) { + } else if (isTouchEvent()) { initFromTouch(static_cast(ev)); } else { Q_ASSERT_X(false, "", "invalid event type"); @@ -494,33 +493,46 @@ void QQuickPointerEvent::initFromTouch(QTouchEvent *ev) { } QTouchEvent *QQuickPointerEvent::asTouchEvent() const { - if (!m_event) + if (!isTouchEvent()) return nullptr; - switch (m_event->type()) { - case QEvent::TouchBegin: - case QEvent::TouchCancel: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - return static_cast(m_event); - default: - break; - } - return nullptr; + return static_cast(m_event); } QMouseEvent *QQuickPointerEvent::asMouseEvent() const { + if (isMouseEvent()) + return static_cast(m_event); + return nullptr; +} + + +bool QQuickPointerEvent::isMouseEvent() const +{ + return m_event + && m_event->type() >= QEvent::MouseButtonPress + && m_event->type() <= QEvent::MouseMove; +} + +bool QQuickPointerEvent::isTouchEvent() const +{ + return m_event + && ((m_event->type() >= QEvent::TouchBegin && m_event->type() <= QEvent::TouchEnd) + || m_event->type() == QEvent::TouchCancel); +} + +bool QQuickPointerEvent::isTabletEvent() const +{ if (!m_event) - return nullptr; + return false; switch (m_event->type()) { - case QEvent::MouseMove: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - return static_cast(m_event); + case QEvent::TabletPress: + case QEvent::TabletRelease: + case QEvent::TabletMove: + case QEvent::TabletEnterProximity: + case QEvent::TabletLeaveProximity: + return true; default: - return nullptr; + return false; } } - QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index d3ca5cadf5..c4c4daf830 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -426,6 +426,10 @@ protected: bool isValid() const { return m_event != nullptr; } protected: + bool isMouseEvent() const; + bool isTouchEvent() const; + bool isTabletEvent() const; + const QQuickPointerDevice *m_device; QInputEvent *m_event; // original event as received by QQuickWindow Qt::MouseButton m_button; -- cgit v1.2.3 From d7ebaa1611758a9677809d77e0b11f501c103f8d Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Wed, 6 Jul 2016 12:19:18 +0200 Subject: Doc: Added definition of enum value ItemAntialiasingHasChanged Error message: Undocumented enum item 'ItemAntialiasingHasChanged' in QQuickItem::ItemChange Change-Id: I0ea7bf4ba9cdf7fb69a9794694113c6501066d75 Reviewed-by: Martin Smith --- src/quick/items/qquickitem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 5676bdb13a..aaa7ce04b9 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2102,6 +2102,9 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus) \value ItemDevicePixelRatioHasChanged The device pixel ratio of the screen the item is on has changed. ItemChangedData::realValue contains the new device pixel ratio. + + \value ItemAntialiasingHasChanged The antialiasing has changed. The current + (boolean) value can be found in QQuickItem::antialiasing. */ /*! -- cgit v1.2.3 From b3a26dae256575acc09f99f6c55d29d77bac4835 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Mon, 11 Jul 2016 09:42:55 +0200 Subject: QQuickWidget: Fix build with QT_NO_OPENGL Change-Id: I68109e19c90e951af5ca1757514effe3bb44a447 Reviewed-by: Laszlo Agocs --- src/quickwidgets/qquickwidget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index b2c978f3f6..4f22cad472 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1395,8 +1395,10 @@ bool QQuickWidget::event(QEvent *e) d->offscreenWindow->setScreen(newScreen); if (d->offscreenSurface) d->offscreenSurface->setScreen(newScreen); +#ifndef QT_NO_OPENGL if (d->context) d->context->setScreen(newScreen); +#endif } if (d->useSoftwareRenderer -- cgit v1.2.3 From 1e18a4f985f6ec4a0191a2e0cc087b13d29b1719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Martins?= Date: Tue, 28 Jun 2016 11:34:59 +0100 Subject: macOS: Use sRGB when doing native font rendering into FBO Otherwise text appears too dark. A similar bug was fixed in the past for QQuickWidget (which also uses FBO). Now this fixes it for ShaderEffectSource and QQuickItem::grabImage() too. Change-Id: Ia0e176372b9ba8282972f8d60f87f02747291ffe Task-Id: QTBUG-52906 Reviewed-by: Sean Harmer --- src/quick/scenegraph/qsgdefaultlayer.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/quick/scenegraph/qsgdefaultlayer.cpp b/src/quick/scenegraph/qsgdefaultlayer.cpp index fa69f911dd..99735564ef 100644 --- a/src/quick/scenegraph/qsgdefaultlayer.cpp +++ b/src/quick/scenegraph/qsgdefaultlayer.cpp @@ -42,6 +42,29 @@ DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY) #endif DEFINE_BOOL_CONFIG_OPTION(qmlFboFlushBeforeDetach, QML_FBO_FLUSH_BEFORE_DETACH) + + +static QOpenGLFramebufferObject *createFramebuffer(const QSize &size, + QOpenGLFramebufferObjectFormat format) +{ +#ifdef Q_OS_MACOS + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (context->hasExtension("GL_ARB_framebuffer_sRGB") + && context->hasExtension("GL_EXT_texture_sRGB") + && context->hasExtension("GL_EXT_texture_sRGB_decode")) + format.setInternalTextureFormat(GL_SRGB8_ALPHA8_EXT); +#endif + QOpenGLFramebufferObject *fbo = new QOpenGLFramebufferObject(size, format); +#ifdef Q_OS_MACOS + if (format.internalTextureFormat() == GL_SRGB8_ALPHA8_EXT) { + QOpenGLFunctions *funcs = context->functions(); + funcs->glBindTexture(GL_TEXTURE_2D, fbo->texture()); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT); + } +#endif + return fbo; +} + namespace { class BindableFbo : public QSGBindable @@ -324,7 +347,7 @@ void QSGDefaultLayer::grab() format.setInternalTextureFormat(m_format); format.setSamples(m_context->openglContext()->format().samples()); - m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); + m_secondaryFbo = createFramebuffer(m_size, format); m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo); } else { QOpenGLFramebufferObjectFormat format; @@ -333,14 +356,14 @@ void QSGDefaultLayer::grab() if (m_recursive) { deleteFboLater = true; delete m_secondaryFbo; - m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); + m_secondaryFbo = createFramebuffer(m_size, format); funcs->glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture()); updateBindOptions(true); m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo); } else { delete m_fbo; delete m_secondaryFbo; - m_fbo = new QOpenGLFramebufferObject(m_size, format); + m_fbo = createFramebuffer(m_size, format); m_secondaryFbo = 0; funcs->glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); updateBindOptions(true); @@ -354,7 +377,7 @@ void QSGDefaultLayer::grab() Q_ASSERT(m_fbo); Q_ASSERT(!m_multisampling); - m_secondaryFbo = new QOpenGLFramebufferObject(m_size, m_fbo->format()); + m_secondaryFbo = createFramebuffer(m_size, m_fbo->format()); funcs->glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture()); updateBindOptions(true); } -- cgit v1.2.3 From e160ac6c89121db843e0ba195f25cce2252115c2 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 11 Jul 2016 11:55:56 +0200 Subject: Fix reference to wrong methods The new classes lost the Simple in their names. Change-Id: I06c7866848fc05fe3aecf631305e7553be33836f Reviewed-by: Andy Nichols --- src/quick/scenegraph/util/qsgsimplerectnode.cpp | 4 ++-- src/quick/scenegraph/util/qsgsimpletexturenode.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.cpp b/src/quick/scenegraph/util/qsgsimplerectnode.cpp index f991f03136..28b177be84 100644 --- a/src/quick/scenegraph/util/qsgsimplerectnode.cpp +++ b/src/quick/scenegraph/util/qsgsimplerectnode.cpp @@ -51,8 +51,8 @@ QT_BEGIN_NAMESPACE \warning This utility class is only functional when running with the OpenGL or software backends of the Qt Quick scenegraph. For a proper cross-platform - alternative prefer using QSGSimpleRectangleNode via - QQuickWindow::createSimpleRectangleNode() or QSGEngine::createSimpleRectangleNode(). + alternative prefer using QSGRectangleNode via + QQuickWindow::createRectangleNode() or QSGEngine::createRectangleNode(). \deprecated */ diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp index c16e86c4e1..6ce37de7cb 100644 --- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp @@ -100,8 +100,8 @@ static void qsgsimpletexturenode_update(QSGGeometry *g, \warning This utility class is only functional when running with the OpenGL or software backends of the Qt Quick scenegraph. For a proper cross-platform - alternative prefer using QSGSimpleImageNode via - QQuickWindow::createSimpleImageNode() or QSGEngine::createSimpleImageNode(). + alternative prefer using QSGImageNode via + QQuickWindow::createImageNode() or QSGEngine::createImageNode(). \deprecated */ -- cgit v1.2.3 From 62719a97cf4640ff014d9ca0691cc1e7632ef173 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 8 Jul 2016 14:46:08 +0200 Subject: PointerEvents: start unified delivery code path This really does nothing but join all incoming events, package them into pointer events and then unpack them to send them on their separate ways again. Change-Id: Iad87b86051963c064d7a41d9a64b4551efe1f039 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 25 ++++++++++++++++++------- src/quick/items/qquickwindow_p.h | 6 ++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index f263ed23d1..40b24724b2 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1906,7 +1906,7 @@ void QQuickWindowPrivate::deliverDelayedTouchEvent() // Set delayedTouch to 0 before delivery to avoid redelivery in case of // event loop recursions (e.g if it the touch starts a dnd session). QScopedPointer e(delayedTouch.take()); - deliverTouchEvent(e.data()); + deliverPointerEvent(currentPointerEvent.reset(e.data())); } static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION"); @@ -1985,14 +1985,14 @@ void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) qCDebug(DBG_TOUCH) << event; if (qquickwindow_no_touch_compression || touchRecursionGuard) { - deliverTouchEvent(event); + deliverPointerEvent(currentPointerEvent.reset(event)); return; } if (!compressTouchEvent(event)) { if (delayedTouch) deliverDelayedTouchEvent(); - deliverTouchEvent(event); + deliverPointerEvent(currentPointerEvent.reset(event)); } } @@ -2033,7 +2033,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonPress: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(), event->buttons()); - deliverMouseEvent(event); + deliverPointerEvent(currentPointerEvent.reset(event)); break; case QEvent::MouseButtonRelease: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(), @@ -2043,7 +2043,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) return; } - deliverMouseEvent(event); + deliverPointerEvent(currentPointerEvent.reset(event)); if (mouseGrabberItem && !event->buttons()) mouseGrabberItem->ungrabMouse(); break; @@ -2059,7 +2059,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) return; } - deliverMouseEvent(event); + deliverPointerEvent(currentPointerEvent.reset(event)); break; case QEvent::MouseMove: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, @@ -2086,7 +2086,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) event->setAccepted(accepted); return; } - deliverMouseEvent(event); + deliverPointerEvent(currentPointerEvent.reset(event)); break; default: Q_ASSERT(false); @@ -2118,6 +2118,17 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() } } +void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) +{ + if (QMouseEvent *mouse = event->asMouseEvent()) { + deliverMouseEvent(mouse); + } else if (QTouchEvent *touch = event->asTouchEvent()) { + deliverTouchEvent(touch); + } else { + Q_ASSERT(false); + } +} + void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index e06fe4472d..713f33cc8f 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -53,6 +53,7 @@ #include "qquickitem.h" #include "qquickwindow.h" +#include "qquickevents_p_p.h" #include @@ -161,6 +162,7 @@ public: QHash > *, QSet *filtered); void handleTouchEvent(QTouchEvent *); void handleMouseEvent(QMouseEvent *); + void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QTouchEvent *); bool compressTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); @@ -238,6 +240,10 @@ public: QQuickRenderControl *renderControl; QQuickAnimatorController *animationController; QScopedPointer delayedTouch; + + // The current touch or mouse event that is delivered. + // This event gets re-used (reset) for every incoming mouse/touch event. + QQuickPointerEvent currentPointerEvent; int touchRecursionGuard; QQuickCustomRenderStage *customRenderStage; -- cgit v1.2.3 From 0f77ce872a22dfade05d2f699f0aa8a8c8ef7125 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sat, 9 Jul 2016 12:50:42 +0200 Subject: Generalize the touchRecursionGuard to pointer events If we want to do mouse event compression, this makes sense. It's also a small step towards more unified event delivery. Change-Id: Ia889e21ce2834ef4fa26b4c5906cbce04850bf38 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 18 +++++++++--------- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 40b24724b2..02056e1fbd 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -490,7 +490,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , renderer(0) , windowManager(0) , renderControl(0) - , touchRecursionGuard(0) + , pointerEventRecursionGuard(0) , customRenderStage(0) , clearColor(Qt::white) , clearBeforeRendering(true) @@ -1984,7 +1984,7 @@ void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) translateTouchEvent(event); qCDebug(DBG_TOUCH) << event; - if (qquickwindow_no_touch_compression || touchRecursionGuard) { + if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) { deliverPointerEvent(currentPointerEvent.reset(event)); return; } @@ -2120,6 +2120,11 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) { + // If users spin the eventloop as a result of event delivery, we disable + // event compression and send events directly. This is because we consider + // the usecase a bit evil, but we at least don't want to lose events. + ++pointerEventRecursionGuard; + if (QMouseEvent *mouse = event->asMouseEvent()) { deliverMouseEvent(mouse); } else if (QTouchEvent *touch = event->asTouchEvent()) { @@ -2127,17 +2132,14 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) } else { Q_ASSERT(false); } + + --pointerEventRecursionGuard; } void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event; - // If users spin the eventloop as a result of touch delivery, we disable - // touch compression and send events directly. This is because we consider - // the usecase a bit evil, but we at least don't want to lose events. - ++touchRecursionGuard; - // List of all items that received an event before // When we have TouchBegin this is and will stay empty QHash > updatedPoints; @@ -2193,8 +2195,6 @@ void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) itemForTouchPointId.clear(); } } - - --touchRecursionGuard; } // This function recurses and sends the events to the individual items diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 713f33cc8f..896cc6bf49 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -244,7 +244,7 @@ public: // The current touch or mouse event that is delivered. // This event gets re-used (reset) for every incoming mouse/touch event. QQuickPointerEvent currentPointerEvent; - int touchRecursionGuard; + int pointerEventRecursionGuard; QQuickCustomRenderStage *customRenderStage; QColor clearColor; -- cgit v1.2.3 From c3105b42aa286421fb7998a4b013f53ca5624a5f Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 15 Apr 2016 17:27:12 +0200 Subject: V4: merge basic blocks where possible The algorithmic complexity of some algorithms/functions in the optimizer are directy related to the number of basic blocks, so reducing the basic block count improves them. Two basic blocks can be merged when a block has one outgoing edge, and the successor has one incoming edge. Change-Id: Ifc870c8022f3aac8adf77c3ec5f826bed33c668d Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4ssa.cpp | 126 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 122 insertions(+), 4 deletions(-) diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 90e72facfc..283fb24897 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -642,7 +642,7 @@ public: qout << from; else qout << "(none)"; - qout << " -> " << to->index() << endl; + qout << " dominates " << to->index() << endl; } qDebug("%s", buf.data().constData()); } @@ -740,6 +740,21 @@ public: return order; } + void mergeIntoPredecessor(BasicBlock *successor) + { + int succIdx = successor->index(); + if (succIdx == InvalidBasicBlockIndex) { + return; + } + + int succDom = idom[unsigned(succIdx)]; + for (BasicBlockIndex &idx : idom) { + if (idx == succIdx) { + idx = succDom; + } + } + } + private: bool dominates(BasicBlockIndex dominator, BasicBlockIndex dominated) const { // dominator can be Invalid when the dominated block has no dominator (i.e. the start node) @@ -1175,6 +1190,15 @@ public: return _defUses[variable.index].blockOfStatement; } + void replaceBasicBlock(BasicBlock *from, BasicBlock *to) + { + for (auto &du : _defUses) { + if (du.blockOfStatement == from) { + du.blockOfStatement = to; + } + } + } + void removeUse(Stmt *usingStmt, const Temp &var) { Q_ASSERT(static_cast(var.index) < _defUses.size()); @@ -3327,6 +3351,15 @@ class BlockScheduler // this is a loop, where there in -> candidate edge is the jump back to the top of the loop. continue; + if (in == candidate) + // this is a very tight loop, e.g.: + // L1: ... + // goto L1 + // This can happen when, for example, the basic-block merging gets rid of the empty + // body block. In this case, we can safely schedule this block (if all other + // incoming edges are either loop-back edges, or have been scheduled already). + continue; + return false; // an incoming edge that is not yet emitted, and is not a back-edge } @@ -3709,6 +3742,8 @@ namespace { void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUses, StatementWorklist &W, DominatorTree &dt) { + enum { DebugUnlinking = 0 }; + struct Util { static void removeIncomingEdge(BasicBlock *from, BasicBlock *to, DefUses &defUses, StatementWorklist &W) { @@ -3747,11 +3782,17 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs } }; + Q_ASSERT(!from->isRemoved()); + Q_ASSERT(!to->isRemoved()); + // don't purge blocks that are entry points for catch statements. They might not be directly // connected, but are required anyway if (to->isExceptionHandler()) return; + if (DebugUnlinking) + qDebug("Unlinking L%d -> L%d...", from->index(), to->index()); + // First, unlink the edge from->out.removeOne(to); Util::removeIncomingEdge(from, to, defUses, W); @@ -3761,8 +3802,12 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs // Check if the target is still reachable... if (Util::isReachable(to, dt)) { // yes, recalculate the immediate dominator, and we're done. + if (DebugUnlinking) + qDebug(".. L%d is still reachable, recalulate idom.", to->index()); dt.collectSiblings(to, siblings); } else { + if (DebugUnlinking) + qDebug(".. L%d is unreachable, purging it:", to->index()); // The target is unreachable, so purge it: QVector toPurge; toPurge.reserve(8); @@ -3770,6 +3815,8 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs while (!toPurge.isEmpty()) { BasicBlock *bb = toPurge.first(); toPurge.removeFirst(); + if (DebugUnlinking) + qDebug("... purging L%d", bb->index()); if (bb->isRemoved()) continue; @@ -3813,6 +3860,8 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs } dt.recalculateIDoms(siblings); + if (DebugUnlinking) + qDebug("Unlinking done."); } bool tryOptimizingComparison(Expr *&expr) @@ -4784,13 +4833,20 @@ static void verifyCFG(IR::Function *function) Q_ASSERT(function->basicBlock(bb->index()) == bb); // Check the terminators: - if (Jump *jump = bb->terminator()->asJump()) { + Stmt *terminator = bb->terminator(); + if (terminator == nullptr) { + Stmt *last = bb->statements().last(); + Call *call = last->asExp()->expr->asCall(); + Name *baseName = call->base->asName(); + Q_ASSERT(baseName->builtin == Name::builtin_rethrow); + Q_UNUSED(baseName); + } else if (Jump *jump = terminator->asJump()) { Q_UNUSED(jump); Q_ASSERT(jump->target); Q_ASSERT(!jump->target->isRemoved()); Q_ASSERT(bb->out.size() == 1); Q_ASSERT(bb->out.first() == jump->target); - } else if (CJump *cjump = bb->terminator()->asCJump()) { + } else if (CJump *cjump = terminator->asCJump()) { Q_UNUSED(cjump); Q_ASSERT(bb->out.size() == 2); Q_ASSERT(cjump->iftrue); @@ -4799,7 +4855,7 @@ static void verifyCFG(IR::Function *function) Q_ASSERT(cjump->iffalse); Q_ASSERT(!cjump->iffalse->isRemoved()); Q_ASSERT(cjump->iffalse == bb->out[1]); - } else if (bb->terminator()->asRet()) { + } else if (terminator->asRet()) { Q_ASSERT(bb->out.size() == 0); } else { Q_UNREACHABLE(); @@ -4934,6 +4990,65 @@ private: void visitTemp(Temp *) Q_DECL_OVERRIDE Q_DECL_FINAL {} }; +void mergeBasicBlocks(IR::Function *function, DefUses *du, DominatorTree *dt) +{ + enum { DebugBlockMerging = 0 }; + + if (function->hasTry) + return; + + showMeTheCode(function, "Before basic block merging"); + + // Now merge a basic block with its successor when there is one outgoing edge, and the + // successor has one incoming edge. + for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) { + BasicBlock *bb = function->basicBlock(i); + + bb->nextLocation = QQmlJS::AST::SourceLocation(); // make sure appendStatement doesn't mess with the line info + + if (bb->isRemoved()) continue; // the block has been removed, so ignore it + if (bb->out.size() != 1) continue; // more than one outgoing edge + BasicBlock *successor = bb->out.first(); + if (successor->in.size() != 1) continue; // more than one incoming edge + + // Ok, we can merge the two basic blocks. + if (DebugBlockMerging) { + qDebug("Merging L%d into L%d", successor->index(), bb->index()); + } + Q_ASSERT(bb->terminator()->asJump()); + bb->removeStatement(bb->statementCount() - 1); // remove the terminator, and replace it with: + for (Stmt *s : successor->statements()) { + bb->appendStatement(s); // add all statements from the successor to the current basic block + if (auto cjump = s->asCJump()) + cjump->parent = bb; + } + bb->out = successor->out; // set the outgoing edges to the successor's so they're now in sync with our new terminator + for (auto newSuccessor : bb->out) { + for (auto &backlink : newSuccessor->in) { + if (backlink == successor) { + backlink = bb; // for all successors of our successor: set the incoming edges to come from bb, because we'll now jump there. + } + } + } + if (du) { + // all statements in successor have moved to bb, so make sure that the containing blocks + // stored in DefUses get updated (meaning: point to bb) + du->replaceBasicBlock(successor, bb); + } + if (dt) { + // update the immediate dominators to: any block that was dominated by the successor + // will now need to point to bb's immediate dominator. The reason is that bb itself + // won't be anyones immediate dominator, because it had just one outgoing edge. + dt->mergeIntoPredecessor(successor); + } + function->removeBasicBlock(successor); + --i; // re-run on the current basic-block, so any chain gets collapsed. + } + + showMeTheCode(function, "After basic block merging"); + verifyCFG(function); +} + } // anonymous namespace void LifeTimeInterval::setFrom(int from) { @@ -5168,6 +5283,8 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA && statementCount <= 300) { // qout << "SSA for " << (function->name ? qPrintable(*function->name) : "") << endl; + mergeBasicBlocks(function, nullptr, nullptr); + ConvertArgLocals(function).toTemps(); showMeTheCode(function, "After converting arguments to locals"); @@ -5243,6 +5360,7 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee } verifyNoPointerSharing(function); + mergeBasicBlocks(function, &defUses, &df); // Basic-block cycles that are unreachable (i.e. for loops in a then-part where the // condition is calculated to be always false) are not yet removed. This will choke the -- cgit v1.2.3 From e352fd967a4cdc9ab2ed8633733d59d64cf8134a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 1 Jul 2016 17:36:29 +0200 Subject: Move implementation of grabTouchPoints to QQuickWindowPrivate QQuickItem should not be directly manipulating QQuickWindowPrivate's variables. Change-Id: I5ff11550351398247dacf14447fb6c897b2dd0aa Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickitem.cpp | 22 +--------------------- src/quick/items/qquickwindow.cpp | 25 +++++++++++++++++++++++++ src/quick/items/qquickwindow_p.h | 1 + 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 56426fbe37..79ddd449f5 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -7219,27 +7219,7 @@ void QQuickItem::grabTouchPoints(const QVector &ids) if (!d->window) return; QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window); - - QSet ungrab; - for (int i = 0; i < ids.count(); ++i) { - QQuickItem *oldGrabber = windowPriv->itemForTouchPointId.value(ids.at(i)); - if (oldGrabber == this) - return; - - windowPriv->itemForTouchPointId[ids.at(i)] = this; - if (oldGrabber) - ungrab.insert(oldGrabber); - - QQuickItem *mouseGrabber = windowPriv->mouseGrabberItem; - if (windowPriv->touchMouseId == ids.at(i) && mouseGrabber && mouseGrabber != this) { - qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: grabber" << windowPriv->mouseGrabberItem << "-> null"; - windowPriv->mouseGrabberItem = 0; - QEvent ev(QEvent::UngrabMouse); - d->window->sendEvent(mouseGrabber, &ev); - } - } - foreach (QQuickItem *oldGrabber, ungrab) - oldGrabber->touchUngrabEvent(); + windowPriv->grabTouchPoints(this, ids); } /*! diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index aead9a71b5..7125206f16 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -766,6 +766,31 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) } } +void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector &ids) +{ + Q_Q(QQuickWindow); + QSet ungrab; + for (int i = 0; i < ids.count(); ++i) { + QQuickItem *oldGrabber = itemForTouchPointId.value(ids.at(i)); + if (oldGrabber == grabber) + return; + + itemForTouchPointId[ids.at(i)] = grabber; + if (oldGrabber) + ungrab.insert(oldGrabber); + + QQuickItem *originalMouseGrabberItem = mouseGrabberItem; + if (touchMouseId == ids.at(i) && mouseGrabberItem && mouseGrabberItem != grabber) { + qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: grabber" << mouseGrabberItem << "-> null"; + mouseGrabberItem = 0; + QEvent ev(QEvent::UngrabMouse); + q->sendEvent(originalMouseGrabberItem, &ev); + } + } + foreach (QQuickItem *oldGrabber, ungrab) + oldGrabber->touchUngrabEvent(); +} + void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch) { Q_Q(QQuickWindow); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 1b4b87e001..31fa079658 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -143,6 +143,7 @@ public: bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event); void translateTouchEvent(QTouchEvent *touchEvent); void setMouseGrabber(QQuickItem *grabber); + void grabTouchPoints(QQuickItem *grabber, const QVector &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); static void transformTouchPoints(QList &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); -- cgit v1.2.3 From 6a21b9da692b17478b0e7f783fa58590d587cd6c Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 11 Jul 2016 12:59:32 +0200 Subject: Fix undefined painting of white QSGRectangle Since the default color was white, and the colored points are not set when the color doesn't change, the points ended up being undefined for default white QSGRectangle. Change-Id: I942515582e9e2edc81de42036fd2ed558fa5a9d8 Reviewed-by: Andy Nichols --- src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp index 41db4f1c03..e1c8672add 100644 --- a/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp +++ b/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp @@ -47,11 +47,11 @@ QT_BEGIN_NAMESPACE QSGDefaultRectangleNode::QSGDefaultRectangleNode() : m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 4) - , m_color(QColor(255, 255, 255)) { QSGGeometry::updateColoredRectGeometry(&m_geometry, QRectF()); setMaterial(&m_material); setGeometry(&m_geometry); + setColor(QColor(255, 255, 255)); #ifdef QSG_RUNTIME_DESCRIPTION qsgnode_set_description(this, QLatin1String("rectangle")); #endif -- cgit v1.2.3 From 921ad53c1deb8183f888bf98248d0dcb42838b38 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 1 Jul 2016 17:41:07 +0200 Subject: QQuickWindowPrivate::grabTouchPoints: don't return early We apparently want to grab all the given touchpoints for the given item regardless if one of them was already grabbed. Change-Id: I7a7084bba4f873a631fe00ca152f514ccab1f551 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 7125206f16..296e252c69 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -773,7 +773,7 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector Date: Mon, 11 Jul 2016 12:51:22 +0200 Subject: Unite MousePress and DblClick delivery Change-Id: I8a6a0a1c4c120aa674d93bcbb586c1a7048f9045 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 02056e1fbd..65af2a558b 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1652,7 +1652,7 @@ bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) lastMousePosition = event->windowPos(); if (!mouseGrabberItem && - event->type() == QEvent::MouseButtonPress && + (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) && (event->buttons() & event->button()) == event->buttons()) { if (deliverInitialMousePressEvent(contentItem, event)) event->accept(); @@ -2050,15 +2050,6 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonDblClick: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, event->button(), event->buttons()); - - if (!mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) { - if (deliverInitialMousePressEvent(contentItem, event)) - event->accept(); - else - event->ignore(); - return; - } - deliverPointerEvent(currentPointerEvent.reset(event)); break; case QEvent::MouseMove: -- cgit v1.2.3 From 6fabc0683c6cf0736b4ae9a00f1b138803e461d1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 8 Jul 2016 13:06:21 +0200 Subject: QtQuick: fix use-after-free of shader property connections A use-after-free would occur if the sender of a connection would disconnect (and destroy the slot object), and then the receiver would try to clean-up and access the slot object again. The fix is to have the receiver take out a reference to the slot object, because it will manage the life-time, and thus delete the slot object when it doesn't need it anymore. Change-Id: Ie2033cfb7212acceb2c2cd0bd9e7e45c2dd5e434 Reviewed-by: Robin Burchell --- src/particles/qquickcustomparticle.cpp | 2 +- src/quick/items/qquickopenglshadereffect.cpp | 19 +++++++++++++++++-- src/quick/items/qquickopenglshadereffect_p.h | 4 ++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp index 8ee431aeb2..c08ae3d9ff 100644 --- a/src/particles/qquickcustomparticle.cpp +++ b/src/particles/qquickcustomparticle.cpp @@ -207,7 +207,7 @@ void QQuickCustomParticle::updateVertexShader() { m_common.disconnectPropertySignals(this, Key::VertexShader); m_common.uniformData[Key::VertexShader].clear(); - m_common.signalMappers[Key::VertexShader].clear(); + m_common.clearSignalMappers(Key::VertexShader); m_common.attributes.clear(); m_common.attributes.append("qt_ParticlePos"); m_common.attributes.append("qt_ParticleTex"); diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index 3f057ecd64..9d24a6c511 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -187,7 +187,7 @@ public: explicit MappedSlotObject(PropChangedFunc func) : QSlotObjectBase(&impl), _signalIndex(-1), func(func) - {} + { ref(); } void setSignalIndex(int idx) { _signalIndex = idx; } int signalIndex() const { return _signalIndex; } @@ -215,6 +215,12 @@ private: }; } +QQuickOpenGLShaderEffectCommon::~QQuickOpenGLShaderEffectCommon() +{ + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) + clearSignalMappers(shaderType); +} + void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { @@ -363,7 +369,7 @@ void QQuickOpenGLShaderEffectCommon::updateShader(QQuickItem *item, { disconnectPropertySignals(item, shaderType); uniformData[shaderType].clear(); - signalMappers[shaderType].clear(); + clearSignalMappers(shaderType); if (shaderType == Key::VertexShader) attributes.clear(); @@ -593,6 +599,15 @@ void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item, } } +void QQuickOpenGLShaderEffectCommon::clearSignalMappers(int shader) +{ + for (auto mapper : qAsConst(signalMappers[shader])) { + if (mapper) + mapper->destroyIfLastRef(); + } + signalMappers[shader].clear(); +} + QQuickOpenGLShaderEffect::QQuickOpenGLShaderEffect(QQuickShaderEffect *item, QObject *parent) : QObject(parent) , m_item(item) diff --git a/src/quick/items/qquickopenglshadereffect_p.h b/src/quick/items/qquickopenglshadereffect_p.h index 44b60c97d9..ed56a76409 100644 --- a/src/quick/items/qquickopenglshadereffect_p.h +++ b/src/quick/items/qquickopenglshadereffect_p.h @@ -84,6 +84,8 @@ struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon : host(host), mappedPropertyChanged(mappedPropertyChanged), fileSelector(nullptr) { } + ~QQuickOpenGLShaderEffectCommon(); + void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); void connectPropertySignals(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType); void updateParseLog(bool ignoreAttributes); @@ -97,6 +99,8 @@ struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon void sourceDestroyed(QObject *object); void propertyChanged(QQuickItem *item, const QMetaObject *itemMetaObject, int mappedId, bool *textureProviderChanged); + void clearSignalMappers(int shader); + QObject *host; std::function mappedPropertyChanged; Key source; -- cgit v1.2.3 From 7684236829541e6cdde9d3a7d81071ca8af1c86f Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Sat, 9 Jul 2016 00:09:43 +0200 Subject: QQuickTextNode: Minor cleanup Move decorations to QQuickTextNodeEngine, as the only place that uses them. Change-Id: I7d0b2bf8979bf5d7e447beac02c3419da4edb759 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquicktextnode_p.h | 9 -------- src/quick/items/qquicktextnodeengine.cpp | 36 ++++++++++++++++---------------- src/quick/items/qquicktextnodeengine_p.h | 19 +++++++++++------ 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/quick/items/qquicktextnode_p.h b/src/quick/items/qquicktextnode_p.h index fb30956fea..2969ce9dbc 100644 --- a/src/quick/items/qquicktextnode_p.h +++ b/src/quick/items/qquicktextnode_p.h @@ -77,15 +77,6 @@ class QQuickTextNodeEngine; class Q_QUICK_PRIVATE_EXPORT QQuickTextNode : public QSGTransformNode { public: - enum Decoration { - NoDecoration = 0x0, - Underline = 0x1, - Overline = 0x2, - StrikeOut = 0x4, - Background = 0x8 - }; - Q_DECLARE_FLAGS(Decorations, Decoration) - QQuickTextNode(QQuickItem *ownerElement); ~QQuickTextNode(); diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index 11249be8c6..4631b2e724 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -68,7 +68,7 @@ QQuickTextNodeEngine::BinaryTreeNodeKey::BinaryTreeNodeKey(BinaryTreeNode *node) QQuickTextNodeEngine::BinaryTreeNode::BinaryTreeNode(const QGlyphRun &g, SelectionState selState, const QRectF &brect, - const QQuickTextNode::Decorations &decs, + const Decorations &decs, const QColor &c, const QColor &bc, const QPointF &pos, qreal a) @@ -90,7 +90,7 @@ QQuickTextNodeEngine::BinaryTreeNode::BinaryTreeNode(const QGlyphRun &g, void QQuickTextNodeEngine::BinaryTreeNode::insert(QVarLengthArray *binaryTree, const QGlyphRun &glyphRun, SelectionState selectionState, - QQuickTextNode::Decorations decorations, const QColor &textColor, + Decorations decorations, const QColor &textColor, const QColor &backgroundColor, const QPointF &position) { QRectF searchRect = glyphRun.boundingRect(); @@ -99,10 +99,10 @@ void QQuickTextNodeEngine::BinaryTreeNode::insert(QVarLengthArrayselectionState; // Update decorations - if (currentDecorations != QQuickTextNode::NoDecoration) { + if (currentDecorations != Decoration::NoDecoration) { decorationRect.setY(m_position.y() + m_currentLine.y()); decorationRect.setHeight(m_currentLine.height()); @@ -279,16 +279,16 @@ void QQuickTextNodeEngine::processCurrentLine() decorationRect.setRight(node->boundingRect.left()); TextDecoration textDecoration(currentSelectionState, decorationRect, lastColor); - if (currentDecorations & QQuickTextNode::Underline) + if (currentDecorations & Decoration::Underline) pendingUnderlines.append(textDecoration); - if (currentDecorations & QQuickTextNode::Overline) + if (currentDecorations & Decoration::Overline) pendingOverlines.append(textDecoration); - if (currentDecorations & QQuickTextNode::StrikeOut) + if (currentDecorations & Decoration::StrikeOut) pendingStrikeOuts.append(textDecoration); - if (currentDecorations & QQuickTextNode::Background) + if (currentDecorations & Decoration::Background) m_backgrounds.append(qMakePair(decorationRect, lastBackgroundColor)); } @@ -344,7 +344,7 @@ void QQuickTextNodeEngine::processCurrentLine() // If previous item(s) had underline and current does not, then we add the // pending lines to the lists and likewise for overlines and strikeouts if (!pendingUnderlines.isEmpty() - && !(node->decorations & QQuickTextNode::Underline)) { + && !(node->decorations & Decoration::Underline)) { addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness); pendingUnderlines.clear(); @@ -377,19 +377,19 @@ void QQuickTextNodeEngine::processCurrentLine() // Merge current values with previous. Prefer greatest thickness QRawFont rawFont = node->glyphRun.rawFont(); - if (node->decorations & QQuickTextNode::Underline) { + if (node->decorations & Decoration::Underline) { if (rawFont.lineThickness() > underlineThickness) { underlineThickness = rawFont.lineThickness(); underlineOffset = rawFont.underlinePosition(); } } - if (node->decorations & QQuickTextNode::Overline) { + if (node->decorations & Decoration::Overline) { overlineOffset = -rawFont.ascent(); overlineThickness = rawFont.lineThickness(); } - if (node->decorations & QQuickTextNode::StrikeOut) { + if (node->decorations & Decoration::StrikeOut) { strikeOutThickness = rawFont.lineThickness(); strikeOutOffset = rawFont.ascent() / -3.0; } @@ -495,7 +495,7 @@ void QQuickTextNodeEngine::addUnselectedGlyphs(const QGlyphRun &glyphRun) BinaryTreeNode::insert(&m_currentLineTree, glyphRun, Unselected, - QQuickTextNode::NoDecoration, + Decoration::NoDecoration, m_textColor, m_backgroundColor, m_position); @@ -507,7 +507,7 @@ void QQuickTextNodeEngine::addSelectedGlyphs(const QGlyphRun &glyphRun) BinaryTreeNode::insert(&m_currentLineTree, glyphRun, Selected, - QQuickTextNode::NoDecoration, + Decoration::NoDecoration, m_textColor, m_backgroundColor, m_position); diff --git a/src/quick/items/qquicktextnodeengine_p.h b/src/quick/items/qquicktextnodeengine_p.h index 87235344e6..91ed6f4430 100644 --- a/src/quick/items/qquicktextnodeengine_p.h +++ b/src/quick/items/qquicktextnodeengine_p.h @@ -68,8 +68,15 @@ QT_BEGIN_NAMESPACE // number of nodes, and join decorations in neighbouring items class QQuickTextNodeEngine { - public: + enum Decoration { + NoDecoration = 0x0, + Underline = 0x1, + Overline = 0x2, + StrikeOut = 0x4, + Background = 0x8 + }; + Q_DECLARE_FLAGS(Decorations, Decoration) enum SelectionState { Unselected, @@ -79,26 +86,26 @@ public: struct BinaryTreeNode { BinaryTreeNode() - : selectionState(Unselected), clipNode(0), decorations(QQuickTextNode::NoDecoration) + : selectionState(Unselected), clipNode(0), decorations(Decoration::NoDecoration) , ascent(0.0), leftChildIndex(-1), rightChildIndex(-1) { } BinaryTreeNode(const QRectF &brect, const QImage &i, SelectionState selState, qreal a) - : boundingRect(brect), selectionState(selState), clipNode(0), decorations(QQuickTextNode::NoDecoration) + : boundingRect(brect), selectionState(selState), clipNode(0), decorations(Decoration::NoDecoration) , image(i), ascent(a), leftChildIndex(-1), rightChildIndex(-1) { } BinaryTreeNode(const QGlyphRun &g, SelectionState selState, const QRectF &brect, - const QQuickTextNode::Decorations &decs, const QColor &c, const QColor &bc, + const Decorations &decs, const QColor &c, const QColor &bc, const QPointF &pos, qreal a); QGlyphRun glyphRun; QRectF boundingRect; SelectionState selectionState; QQuickDefaultClipNode *clipNode; - QQuickTextNode::Decorations decorations; + Decorations decorations; QColor color; QColor backgroundColor; QPointF position; @@ -114,7 +121,7 @@ public: { insert(binaryTree, BinaryTreeNode(rect, image, selectionState, ascent)); } static void insert(QVarLengthArray *binaryTree, const QGlyphRun &glyphRun, SelectionState selectionState, - QQuickTextNode::Decorations decorations, const QColor &textColor, const QColor &backgroundColor, const QPointF &position); + Decorations decorations, const QColor &textColor, const QColor &backgroundColor, const QPointF &position); static void insert(QVarLengthArray *binaryTree, const BinaryTreeNode &binaryTreeNode); static void inOrder(const QVarLengthArray &binaryTree, QVarLengthArray *sortedIndexes, int currentIndex = 0); }; -- cgit v1.2.3 From cced077aa3c537c7593b18f930318a5d22a1eeb1 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 8 Jul 2016 23:52:08 +0200 Subject: QQuickTextInput: Use qmlobject_connect on clipboard signal Originally raised by Andrew den Exter: http://lists.qt-project.org/pipermail/development/2015-February/020230.html Raises the number of TextInput I can create per frame from around 118 to 128 per frame. Change-Id: I236010a61faf5def59d1f520c32febdbe43d794b Reviewed-by: Shawn Rutledge Reviewed-by: Andrew den Exter --- src/quick/items/qquicktextinput.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 65e066c611..0078c4ac21 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -2682,8 +2682,8 @@ void QQuickTextInputPrivate::init() #endif q->setFlag(QQuickItem::ItemHasContents); #ifndef QT_NO_CLIPBOARD - q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()), - q, SLOT(q_canPasteChanged())); + qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), + q, QQuickTextInput, SLOT(q_canPasteChanged())); #endif // QT_NO_CLIPBOARD lastSelectionStart = 0; -- cgit v1.2.3 From 5f4eb78ccb0639ec77b45ae1a94c137a77e74d8c Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 11 Jul 2016 15:37:02 +0200 Subject: Remove unused variable Change-Id: I47c00941ef458c8b16c02960a7b37749a222a04e Reviewed-by: Robin Burchell --- tests/auto/quick/touchmouse/tst_touchmouse.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 338b5d343e..dc70081f09 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -250,7 +250,6 @@ void tst_TouchMouse::simpleTouchEvent() QCOMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); - QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); QCOMPARE(window->mouseGrabberItem(), eventItem1); QPoint localPos = eventItem1->mapFromScene(p1).toPoint(); -- cgit v1.2.3 From e4f7ab42c6c4f19eed76d9d0de5accda5835a3a8 Mon Sep 17 00:00:00 2001 From: Daniel d'Andrada Date: Wed, 6 Jul 2016 17:01:35 -0300 Subject: QQuickWindow: Fill out timestamps in QHoverEvents sent to QQuickItems Task-number: QTBUG-54600 Change-Id: Ie24c44e2f68aae55ff1146c13c3dfc25349b7a29 Reviewed-by: Frederik Gladhorn Reviewed-by: Robin Burchell Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 28 +++---- src/quick/items/qquickwindow_p.h | 6 +- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 85 ++++++++++++++++++++++ 3 files changed, 103 insertions(+), 16 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 14e7915dba..de1b5f236e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -661,10 +661,10 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e lastMousePosition = me->windowPos(); bool accepted = me->isAccepted(); - bool delivered = deliverHoverEvent(contentItem, me->windowPos(), last, me->modifiers(), accepted); + bool delivered = deliverHoverEvent(contentItem, me->windowPos(), last, me->modifiers(), me->timestamp(), accepted); if (!delivered) { //take care of any exits - accepted = clearHover(); + accepted = clearHover(me->timestamp()); } me->setAccepted(accepted); break; @@ -1397,7 +1397,7 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const } -bool QQuickWindowPrivate::clearHover() +bool QQuickWindowPrivate::clearHover(ulong timestamp) { Q_Q(QQuickWindow); if (hoverItems.isEmpty()) @@ -1407,7 +1407,7 @@ bool QQuickWindowPrivate::clearHover() bool accepted = false; foreach (QQuickItem* item, hoverItems) - accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted; + accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted; hoverItems.clear(); return accepted; } @@ -1657,13 +1657,15 @@ void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event) bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, - Qt::KeyboardModifiers modifiers, bool accepted) + Qt::KeyboardModifiers modifiers, ulong timestamp, + bool accepted) { Q_Q(QQuickWindow); const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform(); //create copy of event QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers); + hoverEvent.setTimestamp(timestamp); hoverEvent.setAccepted(accepted); QSet hasFiltered; @@ -1699,10 +1701,10 @@ void QQuickWindow::mouseMoveEvent(QMouseEvent *event) d->lastMousePosition = event->windowPos(); bool accepted = event->isAccepted(); - bool delivered = d->deliverHoverEvent(d->contentItem, event->windowPos(), last, event->modifiers(), accepted); + bool delivered = d->deliverHoverEvent(d->contentItem, event->windowPos(), last, event->modifiers(), event->timestamp(), accepted); if (!delivered) { //take care of any exits - accepted = d->clearHover(); + accepted = d->clearHover(event->timestamp()); } event->setAccepted(accepted); return; @@ -1712,7 +1714,7 @@ void QQuickWindow::mouseMoveEvent(QMouseEvent *event) } bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, - Qt::KeyboardModifiers modifiers, bool &accepted) + Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted) { Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -1728,7 +1730,7 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce QQuickItem *child = children.at(ii); if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled) continue; - if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted)) + if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, timestamp, accepted)) return true; } @@ -1737,7 +1739,7 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce if (item->contains(p)) { if (!hoverItems.isEmpty() && hoverItems[0] == item) { //move - accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted); + accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted); } else { QList itemsToHover; QQuickItem* parent = item; @@ -1748,12 +1750,12 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce // Leaving from previous hovered items until we reach the item or one of its ancestors. while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) { QQuickItem *hoverLeaveItem = hoverItems.takeFirst(); - sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, accepted); + sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted); } if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item // ### Shouldn't we send moves for the parent items as well? - accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted); + accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted); } else { // Enter items that are not entered yet. int startIdx = -1; @@ -1772,7 +1774,7 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce // itemToHoverPrivate->window here prevents that case. if (itemToHoverPrivate->window == q && itemToHoverPrivate->hoverEnabled) { hoverItems.prepend(itemToHover); - sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted); + sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, timestamp, accepted); } } } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 33aa021d98..be7252d335 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -155,14 +155,14 @@ public: bool deliverTouchCancelEvent(QTouchEvent *); void deliverDelayedTouchEvent(); void flushDelayedTouchEvent(); - bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); + bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted); bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, const QList &matchingPoints, QSet *filtered); QTouchEvent *touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent); QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, - Qt::KeyboardModifiers modifiers, bool accepted); - bool clearHover(); + Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted); + bool clearHover(ulong timestamp = 0); #ifndef QT_NO_DRAGANDDROP void deliverDragEvent(QQuickDragGrabber *, QEvent *); bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *); diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index c597cf03dd..b03cb856c1 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -371,6 +371,7 @@ private slots: void testRenderJob(); void testHoverChildMouseEventFilter(); + void testHoverTimestamp(); private: QTouchDevice *touchDevice; QTouchDevice *touchDeviceWithVelocity; @@ -2282,6 +2283,90 @@ void tst_qquickwindow::testHoverChildMouseEventFilter() QCOMPARE(middleItem->eventCount(QEvent::HoverEnter), 0); } +class HoverTimestampConsumer : public QQuickItem +{ + Q_OBJECT +public: + HoverTimestampConsumer(QQuickItem *parent = 0) + : QQuickItem(parent) + { + setAcceptHoverEvents(true); + } + + void hoverEnterEvent(QHoverEvent *event) { hoverTimestamps << event->timestamp(); } + void hoverLeaveEvent(QHoverEvent *event) { hoverTimestamps << event->timestamp(); } + void hoverMoveEvent(QHoverEvent *event) { hoverTimestamps << event->timestamp(); } + + QList hoverTimestamps; +}; + +// Checks that a QHoverEvent carries the timestamp of the QMouseEvent that caused it. +// QTBUG-54600 +void tst_qquickwindow::testHoverTimestamp() +{ + QQuickWindow window; + + window.resize(200, 200); + window.setPosition(100, 100); + window.setTitle(QTest::currentTestFunction()); + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + + HoverTimestampConsumer *hoverConsumer = new HoverTimestampConsumer(window.contentItem()); + hoverConsumer->setWidth(100); + hoverConsumer->setHeight(100); + hoverConsumer->setX(50); + hoverConsumer->setY(50); + + // First position, outside + { + QMouseEvent mouseEvent(QEvent::MouseMove, QPointF(40, 40), QPointF(40, 40), QPointF(140, 140), + Qt::NoButton, Qt::NoButton, Qt::NoModifier, Qt::MouseEventNotSynthesized); + mouseEvent.setTimestamp(10); + QVERIFY(QCoreApplication::sendEvent(&window, &mouseEvent)); + } + + // Enter + { + QMouseEvent mouseEvent(QEvent::MouseMove, QPointF(50, 50), QPointF(50, 50), QPointF(150, 150), + Qt::NoButton, Qt::NoButton, Qt::NoModifier, Qt::MouseEventNotSynthesized); + mouseEvent.setTimestamp(20); + QVERIFY(QCoreApplication::sendEvent(&window, &mouseEvent)); + } + QCOMPARE(hoverConsumer->hoverTimestamps.size(), 1); + QCOMPARE(hoverConsumer->hoverTimestamps.last(), 20UL); + + // Move + { + QMouseEvent mouseEvent(QEvent::MouseMove, QPointF(60, 60), QPointF(60, 60), QPointF(160, 160), + Qt::NoButton, Qt::NoButton, Qt::NoModifier, Qt::MouseEventNotSynthesized); + mouseEvent.setTimestamp(30); + QVERIFY(QCoreApplication::sendEvent(&window, &mouseEvent)); + } + QCOMPARE(hoverConsumer->hoverTimestamps.size(), 2); + QCOMPARE(hoverConsumer->hoverTimestamps.last(), 30UL); + + // Move + { + QMouseEvent mouseEvent(QEvent::MouseMove, QPointF(100, 100), QPointF(100, 100), QPointF(200, 200), + Qt::NoButton, Qt::NoButton, Qt::NoModifier, Qt::MouseEventNotSynthesized); + mouseEvent.setTimestamp(40); + QVERIFY(QCoreApplication::sendEvent(&window, &mouseEvent)); + } + QCOMPARE(hoverConsumer->hoverTimestamps.size(), 3); + QCOMPARE(hoverConsumer->hoverTimestamps.last(), 40UL); + + // Leave + { + QMouseEvent mouseEvent(QEvent::MouseMove, QPointF(160, 160), QPointF(160, 160), QPointF(260, 260), + Qt::NoButton, Qt::NoButton, Qt::NoModifier, Qt::MouseEventNotSynthesized); + mouseEvent.setTimestamp(5); + QVERIFY(QCoreApplication::sendEvent(&window, &mouseEvent)); + } + QCOMPARE(hoverConsumer->hoverTimestamps.size(), 4); + QCOMPARE(hoverConsumer->hoverTimestamps.last(), 5UL); +} + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" -- cgit v1.2.3 From 2418a36d0a83c22a12654caa5138a0bc4f84e9f3 Mon Sep 17 00:00:00 2001 From: Alexandr Akulich Date: Thu, 2 Jun 2016 23:59:04 +0500 Subject: Set width and height simultaneously on SizeRootObjectToView On QQuickView/QQuickWidget size update we used set width (with the notify signal emission) and then height (with signal emission again). This way code, relying on width and height would be triggered twice and at the first time with there would be outdated height value. The new code takes care on proper value for both width and height. Change-Id: I6525911c40af0ca6a26ab3e7dac16d32a96d9a27 Reviewed-by: Robin Burchell Reviewed-by: Shawn Rutledge --- src/quick/items/qquickview.cpp | 9 ++++++-- src/quickwidgets/qquickwidget.cpp | 9 ++++++-- tests/auto/quick/qquickview/tst_qquickview.cpp | 31 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp index 0ed9167fb2..1d89c8bfc2 100644 --- a/src/quick/items/qquickview.cpp +++ b/src/quick/items/qquickview.cpp @@ -433,9 +433,14 @@ void QQuickViewPrivate::updateSize() q->resize(newSize); } } else if (resizeMode == QQuickView::SizeRootObjectToView) { - if (!qFuzzyCompare(q->width(), root->width())) + bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width()); + bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height()); + + if (needToUpdateWidth && needToUpdateHeight) + root->setSize(QSizeF(q->width(), q->height())); + else if (needToUpdateWidth) root->setWidth(q->width()); - if (!qFuzzyCompare(q->height(), root->height())) + else if (needToUpdateHeight) root->setHeight(q->height()); } } diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 4f22cad472..2a014546e1 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -758,9 +758,14 @@ void QQuickWidgetPrivate::updateSize() q->resize(newSize); } } else if (resizeMode == QQuickWidget::SizeRootObjectToView) { - if (!qFuzzyCompare(q->width(), root->width())) + bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width()); + bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height()); + + if (needToUpdateWidth && needToUpdateHeight) + root->setSize(QSizeF(q->width(), q->height())); + else if (needToUpdateWidth) root->setWidth(q->width()); - if (!qFuzzyCompare(q->height(), root->height())) + else if (needToUpdateHeight) root->setHeight(q->height()); } } diff --git a/tests/auto/quick/qquickview/tst_qquickview.cpp b/tests/auto/quick/qquickview/tst_qquickview.cpp index 05922ae20f..200f769a65 100644 --- a/tests/auto/quick/qquickview/tst_qquickview.cpp +++ b/tests/auto/quick/qquickview/tst_qquickview.cpp @@ -36,6 +36,30 @@ #include #include +class SizeChangesListener : public QObject, public QVector +{ + Q_OBJECT +public: + explicit SizeChangesListener(QQuickItem *item); +private slots: + void onSizeChanged(); +private: + QQuickItem *item; + +}; + +SizeChangesListener::SizeChangesListener(QQuickItem *item) : + item(item) +{ + connect(item, &QQuickItem::widthChanged, this, &SizeChangesListener::onSizeChanged); + connect(item, &QQuickItem::heightChanged, this, &SizeChangesListener::onSizeChanged); +} + +void SizeChangesListener::onSizeChanged() +{ + append(QSize(item->width(), item->height())); +} + class tst_QQuickView : public QQmlDataTest { Q_OBJECT @@ -139,8 +163,15 @@ void tst_QQuickView::resizemodeitem() QCOMPARE(QSize(item->width(), item->height()), view->sizeHint()); // size update from view + SizeChangesListener sizeListener(item); view->resize(QSize(200,300)); QTRY_COMPARE(item->width(), 200.0); + + for (int i = 0; i < sizeListener.count(); ++i) { + // Check that we have the correct geometry on all signals + QCOMPARE(sizeListener.at(i), view->size()); + } + QCOMPARE(item->height(), 300.0); QCOMPARE(view->size(), QSize(200, 300)); QCOMPARE(view->size(), view->sizeHint()); -- cgit v1.2.3 From 14e8e05ce3511e46d30710a680b0b7e987a569c7 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 11 Jul 2016 16:19:52 +0200 Subject: add QQuickPointerEvent::touchEventForItem It's similar to QQuickWindowPrivate::touchEventWithPoints(), and is used in the same way when delivering a QQuickPointerEvent by sending a QTouchEvent to the item's event() method. Change-Id: I0d181eaf924bfdad2db2de9d8acf85d0345aec3e Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 66 ++++++++++++++++++++++++++++++++++++++ src/quick/items/qquickevents_p_p.h | 10 +++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 520e5d2704..50714518b1 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qquickevents_p_p.h" +#include #include QT_BEGIN_NAMESPACE @@ -535,4 +536,69 @@ bool QQuickPointerEvent::isTabletEvent() const } } +/* + \internal + + make a new QTouchEvent, giving it a subset of the original touch points +*/ +QTouchEvent *QQuickPointerEvent::touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const +{ + QList touchPoints; + Qt::TouchPointStates eventStates; + // TODO maybe add QQuickItem::mapVector2DFromScene(QVector2D) to avoid needing QQuickItemPrivate here + // Or else just document that velocity is always scene-relative and is not scaled and rotated with the item + // but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity + QMatrix4x4 transformMatrix(QQuickItemPrivate::get(relativeTo)->windowToItemTransform()); + for (const QQuickEventPoint * p : newPoints) { + const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId()); + if (tp) { + eventStates |= tp->state(); + QTouchEvent::TouchPoint tpCopy = *tp; + tpCopy.setPos(relativeTo->mapFromScene(tpCopy.scenePos())); + tpCopy.setLastPos(relativeTo->mapFromScene(tpCopy.lastPos())); + tpCopy.setStartPos(relativeTo->mapFromScene(tpCopy.startScenePos())); + tpCopy.setRect(relativeTo->mapRectFromScene(tpCopy.sceneRect())); + tpCopy.setVelocity(transformMatrix.mapVector(tpCopy.velocity()).toVector2D()); + touchPoints << tpCopy; + } + } + + // if all points have the same state, set the event type accordingly + const QTouchEvent &event = *asTouchEvent(); + QEvent::Type eventType = event.type(); + switch (eventStates) { + case Qt::TouchPointPressed: + eventType = QEvent::TouchBegin; + break; + case Qt::TouchPointReleased: + eventType = QEvent::TouchEnd; + break; + default: + eventType = QEvent::TouchUpdate; + break; + } + + QTouchEvent *touchEvent = new QTouchEvent(eventType); + touchEvent->setWindow(event.window()); + touchEvent->setTarget(relativeTo); + touchEvent->setDevice(event.device()); + touchEvent->setModifiers(event.modifiers()); + touchEvent->setTouchPoints(touchPoints); + touchEvent->setTouchPointStates(eventStates); + touchEvent->setTimestamp(event.timestamp()); + touchEvent->accept(); + return touchEvent; +} + +const QTouchEvent::TouchPoint *QQuickPointerEvent::touchPointById(int pointId) const { + const QTouchEvent *ev = asTouchEvent(); + if (!ev) + return nullptr; + const QList &tps = ev->touchPoints(); + auto it = std::find_if(tps.constBegin(), tps.constEnd(), + [&pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } ); + // return the pointer to the actual TP in QTouchEvent::_touchPoints + return (it == tps.end() ? nullptr : it.operator->()); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index c4c4daf830..af3e385ce6 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -378,6 +378,8 @@ private: QPointerUniqueId m_uniqueId; }; +class QQuickItem; + class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject { Q_OBJECT @@ -406,6 +408,9 @@ public: Qt::MouseButton button() const { return m_button; } Qt::MouseButtons buttons() const { return m_pressedButtons; } + // ---------------------------------------------------- + // helpers for C++ event delivery, not for QML properties + /** Returns the original touch event. */ QTouchEvent *asTouchEvent() const; @@ -414,7 +419,6 @@ public: * Returns nullptr in case the original event was not a mouse event. */ QMouseEvent *asMouseEvent() const; - // helpers for C++ event delivery, not for QML properties int pointCount() const { return asTouchEvent() ? m_touchPoints.count() : 1; } const QQuickEventPoint *point(int i) const { if (asTouchEvent()) @@ -422,6 +426,10 @@ public: return i == 0 ? m_mousePoint : nullptr; } + const QTouchEvent::TouchPoint *touchPointById(int pointId) const; + + QTouchEvent *touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const; + protected: bool isValid() const { return m_event != nullptr; } -- cgit v1.2.3 From b55485bd4ad6d9084e15e982e457a835aeda831d Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 12 Jul 2016 14:49:51 +0200 Subject: Fix regression after geometry change refactoring Commit e2c296c46b3f922ed12f83b166b1493dfded480e removed the passing of the original geometry to the itemGeometryChanged method, and had a wrong calculation of the original value the one place it is still needed. Change-Id: I69fdd89c2643b4b5d6a3483f961cf5f1de4d2dbf Reviewed-by: Laszlo Agocs --- src/quick/items/qquicklistview.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index cdbac55fa6..7bbc6dc9ba 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -1426,10 +1426,10 @@ void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometry // position all subsequent items if (visibleItems.count() && item == visibleItems.first()->item) { FxListItemSG *listItem = static_cast(visibleItems.first()); - const QRectF oldGeometry(x - diff.x(), - y - diff.y(), - width - diff.width(), - height - diff.height()); + const QRectF oldGeometry(item->x() - diff.x(), + item->y() - diff.y(), + item->width() - diff.width(), + item->height() - diff.height()); if (listItem->transitionScheduledOrRunning()) return; if (orient == QQuickListView::Vertical) { -- cgit v1.2.3 From 5018abaac39b372e76010bdc20c79b1a6235da25 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 12 Jul 2016 14:49:43 +0200 Subject: Simplify event acceptance Patch by Shawn Rutledge taken from bigger refactoring. Change-Id: Id8c30eb2ccea4ce935f9f9919c8fb5bea55e5cc3 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 65af2a558b..c227e360da 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2308,10 +2308,8 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - event->setAccepted(translateTouchToMouse(item, touchEvent.data())); - if (event->isAccepted()) { + if (translateTouchToMouse(item, touchEvent.data())) touchEventAccepted = true; - } } if (touchEventAccepted) { -- cgit v1.2.3 From 647c36b81eb8ea57df2dffcda2399b64867a7ec9 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 12 Jul 2016 17:01:03 +0200 Subject: Fix setLastPos in QQuickPointerEvent::touchEventForItem Change-Id: I4d3ba6bc24ad26be2dadfc2bea84cfccad1cd143 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 50714518b1..d670898020 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -555,7 +555,7 @@ QTouchEvent *QQuickPointerEvent::touchEventForItem(const QListstate(); QTouchEvent::TouchPoint tpCopy = *tp; tpCopy.setPos(relativeTo->mapFromScene(tpCopy.scenePos())); - tpCopy.setLastPos(relativeTo->mapFromScene(tpCopy.lastPos())); + tpCopy.setLastPos(relativeTo->mapFromScene(tpCopy.lastScenePos())); tpCopy.setStartPos(relativeTo->mapFromScene(tpCopy.startScenePos())); tpCopy.setRect(relativeTo->mapRectFromScene(tpCopy.sceneRect())); tpCopy.setVelocity(transformMatrix.mapVector(tpCopy.velocity()).toVector2D()); -- cgit v1.2.3 From b53ea019c4e0ab50bc2c505d64d21e9fe1f32266 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 12 Jul 2016 18:12:42 +0200 Subject: Don't store Qt::TouchPointState in 4 bits It's not enough on Windows: it gets sign-extended, so 0x08 becomes -8 as an int, in comparisons. The result was that touchpoints were never released. Anyway we hardly have to worry about this now that we're not copying the event points. Change-Id: I0d47a0974a9cf5bc9a090a15948d112df32403eb Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents_p_p.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index af3e385ce6..04f43667a7 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -317,7 +317,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) public: - QQuickEventPoint() : QObject(), m_pointId(0), m_valid(false), m_accept(false), m_state(Qt::TouchPointReleased) + QQuickEventPoint() : QObject(), m_pointId(0), m_state(Qt::TouchPointReleased), m_valid(false), m_accept(false) { Q_UNUSED(m_reserved); } @@ -343,10 +343,10 @@ public: private: QPointF m_scenePos; quint64 m_pointId; + Qt::TouchPointState m_state; bool m_valid : 1; bool m_accept : 1; - Qt::TouchPointState m_state : 4; - int m_reserved : 26; + int m_reserved : 30; }; class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint -- cgit v1.2.3 From 89008f37232927ed6ae0157c3ac0c401d71267cc Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 12 Jul 2016 15:46:19 +0200 Subject: Port to ranged for Change-Id: I5e28e03ddc3b6e424a351d757d1a3e3239124551 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index c227e360da..12efcc0a1e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2362,8 +2362,8 @@ QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QT QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList &newPoints) { Qt::TouchPointStates eventStates; - for (int i=0; i Date: Tue, 12 Jul 2016 14:52:13 +0200 Subject: Simplify event acceptance part 2 Patch by Shawn Rutledge taken from bigger refactoring. Change-Id: Iced01a7c3735347a4d3644980aeb125a6ec79d7d Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 12efcc0a1e..fbf719d99d 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2161,9 +2161,8 @@ void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) if (newPoints.count() > 0 || updatedPoints.count() > 0) { QSet acceptedNewPoints; QSet hasFiltered; - event->setAccepted(deliverTouchPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints, &hasFiltered)); - } else - event->ignore(); + deliverTouchPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints, &hasFiltered); + } // Remove released points from itemForTouchPointId if (event->touchPointStates() & Qt::TouchPointReleased) { -- cgit v1.2.3 From 1a276ab5a41fd36fa9afcd02ff61a56173821d98 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 11 Jul 2016 22:34:51 +0200 Subject: Move delivery of pointer event into deliverTouchEvent Change-Id: Ie014105668e6cbba60203af988948249e2e0228c Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 7 ++++--- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index fbf719d99d..fc727e625e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2118,8 +2118,8 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) if (QMouseEvent *mouse = event->asMouseEvent()) { deliverMouseEvent(mouse); - } else if (QTouchEvent *touch = event->asTouchEvent()) { - deliverTouchEvent(touch); + } else if (event->asTouchEvent()) { + deliverTouchEvent(event); } else { Q_ASSERT(false); } @@ -2127,8 +2127,9 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) --pointerEventRecursionGuard; } -void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) +void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerEvent *pointerEvent) { + QTouchEvent *event = pointerEvent->asTouchEvent(); qCDebug(DBG_TOUCH) << " - delivering" << event; // List of all items that received an event before diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 896cc6bf49..a0e4f48330 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -163,7 +163,7 @@ public: void handleTouchEvent(QTouchEvent *); void handleMouseEvent(QMouseEvent *); void deliverPointerEvent(QQuickPointerEvent *); - void deliverTouchEvent(QTouchEvent *); + void deliverTouchEvent(QQuickPointerEvent *); bool compressTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); void deliverDelayedTouchEvent(); -- cgit v1.2.3 From 9109d15398f8c869e401aa9aacc578c96200e217 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 29 Jun 2016 15:06:08 +0200 Subject: V4: allow for String::createHashValue to be inlined Notably into QHashedString(Ref)::computeHash. Change-Id: Icf8487ed3da0f117cb0911f20c9b88498f61510a Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4string.cpp | 65 ++------------------------------------ src/qml/jsruntime/qv4string_p.h | 66 ++++++++++++++++++++++++++++++++++++--- src/qml/qml/ftw/qhashedstring.cpp | 23 -------------- src/qml/qml/ftw/qhashedstring_p.h | 35 +++++++++++++++++++-- 4 files changed, 96 insertions(+), 93 deletions(-) diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 7c965ce441..3901514326 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -46,59 +46,10 @@ #include "qv4stringobject_p.h" #endif #include -#include using namespace QV4; -static inline uint toUInt(const QChar *ch) { return ch->unicode(); } #ifndef V4_BOOTSTRAP -static inline uint toUInt(const char *ch) { return *ch; } -#endif - -template -static inline uint toArrayIndex(const T *ch, const T *end) -{ - uint i = toUInt(ch) - '0'; - if (i > 9) - return UINT_MAX; - ++ch; - // reject "01", "001", ... - if (i == 0 && ch != end) - return UINT_MAX; - - while (ch < end) { - uint x = toUInt(ch) - '0'; - if (x > 9) - return UINT_MAX; - if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) - return UINT_MAX; - ++ch; - } - return i; -} - -#ifndef V4_BOOTSTRAP - -template -static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype) -{ - // array indices get their number as hash value - uint h = ::toArrayIndex(ch, end); - if (h != UINT_MAX) { - if (subtype) - *subtype = Heap::String::StringType_ArrayIndex; - return h; - } - - while (ch < end) { - h = 31 * h + toUInt(ch); - ++ch; - } - - if (subtype) - *subtype = Heap::String::StringType_Regular; - return h; -} DEFINE_MANAGED_VTABLE(String); @@ -225,19 +176,7 @@ void Heap::String::createHashValue() const Q_ASSERT(!largestSubLength); const QChar *ch = reinterpret_cast(text->data()); const QChar *end = ch + text->size; - stringHash = calculateHashValue(ch, end, &subtype); -} - -uint String::createHashValue(const QChar *ch, int length, uint *subtype) -{ - const QChar *end = ch + length; - return calculateHashValue(ch, end, subtype); -} - -uint String::createHashValue(const char *ch, int length, uint *subtype) -{ - const char *end = ch + length; - return calculateHashValue(ch, end, subtype); + stringHash = QV4::String::calculateHashValue(ch, end, &subtype); } uint String::getLength(const Managed *m) @@ -249,6 +188,6 @@ uint String::getLength(const Managed *m) uint String::toArrayIndex(const QString &str) { - return ::toArrayIndex(str.constData(), str.constData() + str.length()); + return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length()); } diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 60e2655536..ff42ab6471 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -52,6 +52,7 @@ #include #include "qv4managed_p.h" +#include QT_BEGIN_NAMESPACE @@ -62,7 +63,6 @@ struct Identifier; namespace Heap { -#ifndef V4_BOOTSTRAP struct Q_QML_PRIVATE_EXPORT String : Base { enum StringType { StringType_Unknown, @@ -70,6 +70,7 @@ struct Q_QML_PRIVATE_EXPORT String : Base { StringType_ArrayIndex }; +#ifndef V4_BOOTSTRAP String(MemoryManager *mm, const QString &text); String(MemoryManager *mm, String *l, String *n); ~String() { @@ -130,8 +131,8 @@ struct Q_QML_PRIVATE_EXPORT String : Base { MemoryManager *mm; private: static void append(const String *data, QChar *ch); -}; #endif +}; } @@ -183,8 +184,17 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { void makeIdentifierImpl(ExecutionEngine *e) const; - static uint createHashValue(const QChar *ch, int length, uint *subtype); - static uint createHashValue(const char *ch, int length, uint *subtype); + static uint createHashValue(const QChar *ch, int length, uint *subtype) + { + const QChar *end = ch + length; + return calculateHashValue(ch, end, subtype); + } + + static uint createHashValue(const char *ch, int length, uint *subtype) + { + const char *end = ch + length; + return calculateHashValue(ch, end, subtype); + } bool startsWithUpper() const { const String::Data *l = d(); @@ -203,6 +213,54 @@ protected: public: static uint toArrayIndex(const QString &str); + +private: + static inline uint toUInt(const QChar *ch) { return ch->unicode(); } + static inline uint toUInt(const char *ch) { return *ch; } + + template + static inline uint toArrayIndex(const T *ch, const T *end) + { + uint i = toUInt(ch) - '0'; + if (i > 9) + return UINT_MAX; + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return UINT_MAX; + + while (ch < end) { + uint x = toUInt(ch) - '0'; + if (x > 9) + return UINT_MAX; + if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x + return UINT_MAX; + ++ch; + } + return i; + } + +public: + template + static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype) + { + // array indices get their number as hash value + uint h = toArrayIndex(ch, end); + if (h != UINT_MAX) { + if (subtype) + *subtype = Heap::String::StringType_ArrayIndex; + return h; + } + + while (ch < end) { + h = 31 * h + toUInt(ch); + ++ch; + } + + if (subtype) + *subtype = Heap::String::StringType_Regular; + return h; + } }; template<> diff --git a/src/qml/qml/ftw/qhashedstring.cpp b/src/qml/qml/ftw/qhashedstring.cpp index 5c5d2a31ac..117670dbfc 100644 --- a/src/qml/qml/ftw/qhashedstring.cpp +++ b/src/qml/qml/ftw/qhashedstring.cpp @@ -39,30 +39,7 @@ #include "qhashedstring_p.h" -inline quint32 stringHash(const QChar* data, int length) -{ - return QV4::String::createHashValue(data, length, Q_NULLPTR); -} -inline quint32 stringHash(const char *data, int length) -{ - return QV4::String::createHashValue(data, length, Q_NULLPTR); -} - -void QHashedString::computeHash() const -{ - m_hash = stringHash(constData(), length()); -} - -void QHashedStringRef::computeHash() const -{ - m_hash = stringHash(m_data, m_length); -} - -void QHashedCStringRef::computeHash() const -{ - m_hash = stringHash(m_data, m_length); -} /* A QHash has initially around pow(2, MinNumBits) buckets. For diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h index 03361f47f7..9ee50ec931 100644 --- a/src/qml/qml/ftw/qhashedstring_p.h +++ b/src/qml/qml/ftw/qhashedstring_p.h @@ -85,11 +85,15 @@ public: static bool compare(const QChar *lhs, const QChar *rhs, int length); static inline bool compare(const QChar *lhs, const char *rhs, int length); static inline bool compare(const char *lhs, const char *rhs, int length); + + static inline quint32 stringHash(const QChar* data, int length); + static inline quint32 stringHash(const char *data, int length); + private: friend class QHashedStringRef; friend class QStringHashNode; - void computeHash() const; + inline void computeHash() const; mutable quint32 m_hash; }; @@ -136,7 +140,7 @@ public: private: friend class QHashedString; - void computeHash() const; + inline void computeHash() const; const QChar *m_data; int m_length; @@ -163,7 +167,7 @@ public: private: friend class QHashedStringRef; - void computeHash() const; + inline void computeHash() const; const char *m_data; int m_length; @@ -1214,6 +1218,11 @@ bool QHashedStringRef::isLatin1() const return true; } +void QHashedStringRef::computeHash() const +{ + m_hash = QHashedString::stringHash(m_data, m_length); +} + bool QHashedStringRef::startsWithUpper() const { if (m_length < 1) return false; @@ -1280,6 +1289,11 @@ void QHashedCStringRef::writeUtf16(quint16 *output) const *output++ = *d++; } +void QHashedCStringRef::computeHash() const +{ + m_hash = QHashedString::stringHash(m_data, m_length); +} + bool QHashedString::compare(const QChar *lhs, const char *rhs, int length) { Q_ASSERT(lhs && rhs); @@ -1295,6 +1309,21 @@ bool QHashedString::compare(const char *lhs, const char *rhs, int length) return 0 == ::memcmp(lhs, rhs, length); } +quint32 QHashedString::stringHash(const QChar *data, int length) +{ + return QV4::String::createHashValue(data, length, Q_NULLPTR); +} + +quint32 QHashedString::stringHash(const char *data, int length) +{ + return QV4::String::createHashValue(data, length, Q_NULLPTR); +} + +void QHashedString::computeHash() const +{ + m_hash = stringHash(constData(), length()); +} + QT_END_NAMESPACE #endif // QHASHEDSTRING_P_H -- cgit v1.2.3 From c744706be9d277bb4a265d8368b93de6da23dea2 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 5 Jul 2016 16:12:10 +0200 Subject: deliver touch events as QQuickPointerEvents QQuickPointerEvent::touchEventForItem should replace QQuickWindow::touchEventWithPoints, but the latter is still called from one place. This can be fixed when we redo parent-filtering using a presorted delivery list. Change-Id: I350a912a9801294d4f44d3d56b53fe3be5302a60 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 106 ++++++++++++++++++++------------------- src/quick/items/qquickwindow_p.h | 6 +-- 2 files changed, 58 insertions(+), 54 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index fc727e625e..b6737338dd 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2127,32 +2127,31 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) --pointerEventRecursionGuard; } -void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerEvent *pointerEvent) +void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerEvent *event) { - QTouchEvent *event = pointerEvent->asTouchEvent(); - qCDebug(DBG_TOUCH) << " - delivering" << event; + qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); // List of all items that received an event before // When we have TouchBegin this is and will stay empty - QHash > updatedPoints; + QHash > updatedPoints; // Figure out who accepted a touch point last and put it in updatedPoints // Add additional item to newPoints - const QList &touchPoints = event->touchPoints(); - QList newPoints; - for (int i=0; ipointCount(); + QList newPoints; + for (int i = 0; i < pointCount; ++i) { + const QQuickEventPoint *point = event->point(i); + if (point->state() == Qt::TouchPointPressed) { + newPoints << point; } else { // TouchPointStationary is relevant only to items which // are also receiving touch points with some other state. // But we have not yet decided which points go to which item, // so for now we must include all non-new points in updatedPoints. - if (itemForTouchPointId.contains(touchPoint.id())) { - QQuickItem *item = itemForTouchPointId.value(touchPoint.id()); + if (itemForTouchPointId.contains(point->pointId())) { + QQuickItem *item = itemForTouchPointId.value(point->pointId()); if (item) - updatedPoints[item].append(touchPoint); + updatedPoints[item].append(point); } } } @@ -2160,44 +2159,45 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerEvent *pointerEvent) // Deliver the event, but only if there is at least one new point // or some item accepted a point and should receive an update if (newPoints.count() > 0 || updatedPoints.count() > 0) { - QSet acceptedNewPoints; + QSet acceptedNewPoints; QSet hasFiltered; - deliverTouchPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints, &hasFiltered); + deliverPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints, &hasFiltered); } // Remove released points from itemForTouchPointId - if (event->touchPointStates() & Qt::TouchPointReleased) { - for (int i=0; ipoint(i); + int id = point->pointId(); + if (point->state() == Qt::TouchPointReleased) { + qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released"; + itemForTouchPointId.remove(id); + if (id == touchMouseId) + touchMouseId = -1; + touchMouseIdCandidates.remove(id); + } else { + allReleased = false; } } - if (event->type() == QEvent::TouchEnd) { - if (!itemForTouchPointId.isEmpty()) { - qWarning() << "No release received for" << itemForTouchPointId.size() - << "touch points over" << itemForTouchPointId.begin().value() - << "on touch end."; - itemForTouchPointId.clear(); - } + if (allReleased && !itemForTouchPointId.isEmpty()) { + qWarning() << "No release received for" << itemForTouchPointId.size() + << "touch points over" << itemForTouchPointId.begin().value() + << "on touch end."; + itemForTouchPointId.clear(); } } // This function recurses and sends the events to the individual items -bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList &newPoints, - QSet *acceptedNewPoints, QHash > *updatedPoints, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, const QQuickPointerEvent *event, const QList &newPoints, + QSet *acceptedNewPoints, QHash > *updatedPoints, + QSet *hasFiltered) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - for (int i=0; imapFromScene(newPoints[i].scenePos()); + for (const QQuickEventPoint * point : qAsConst(newPoints)) { + QPointF p = item->mapFromScene(point->scenePos()); if (!item->contains(p)) return false; } @@ -2210,7 +2210,7 @@ bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even QQuickItem *child = children.at(ii); if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled) continue; - if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints, hasFiltered)) + if (deliverPoints(child, event, newPoints, acceptedNewPoints, updatedPoints, hasFiltered)) return true; } @@ -2222,18 +2222,19 @@ bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even // (A point which was already accepted is effectively "grabbed" by the item.) // set of IDs of "interesting" new points - QSet matchingNewPoints; + QSet matchingNewPoints; // set of points which this item has previously accepted, for starters - QList matchingPoints = (*updatedPoints)[item]; + QList matchingPoints = (*updatedPoints)[item]; // now add the new points which are inside this item's bounds if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) { for (int i = 0; i < newPoints.count(); i++) { - if (acceptedNewPoints->contains(newPoints[i].id())) + const QQuickEventPoint * point = newPoints[i]; + if (acceptedNewPoints->contains(point->pointId())) continue; - QPointF p = item->mapFromScene(newPoints[i].scenePos()); + QPointF p = item->mapFromScene(point->scenePos()); if (item->contains(p)) { - matchingNewPoints.insert(newPoints[i].id()); - matchingPoints << newPoints[i]; + matchingNewPoints.insert(point->pointId()); + matchingPoints << point; } } } @@ -2244,8 +2245,8 @@ bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even if (matchingNewPoints.isEmpty()) { bool stationaryOnly = true; - foreach (const QTouchEvent::TouchPoint &tp, matchingPoints) { - if (tp.state() != Qt::TouchPointStationary) { + for (const QQuickEventPoint *tp : matchingPoints) { + if (tp->state() != Qt::TouchPointStationary) { stationaryOnly = false; break; } @@ -2258,8 +2259,6 @@ bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even if (!matchingPoints.isEmpty()) { // Now we know this item might be interested in the event. Copy and send it, but // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints. - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform()); deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints, hasFiltered); } @@ -2274,17 +2273,20 @@ bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even // only the points that are relevant for this item. Thus the need for // matchingPoints to already be that set of interesting points. // They are all pre-transformed, too. -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, const QList &matchingPoints, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerEvent *event, QSet *acceptedNewPoints, + const QSet &matchingNewPoints, const QList &matchingPoints, + QSet *hasFiltered) { - QScopedPointer touchEvent(touchEventWithPoints(*event, matchingPoints)); - touchEvent.data()->setTarget(item); + QScopedPointer touchEvent(event->touchEventForItem(matchingPoints, item)); + if (touchEvent.data()->touchPoints().isEmpty()) + return false; bool touchEventAccepted = false; qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item; // First check whether the parent wants to be a filter, // and if the parent accepts the event we are done. - if (sendFilteredTouchEvent(item->parentItem(), item, event, hasFiltered)) { + if (sendFilteredTouchEvent(item->parentItem(), item, event->asTouchEvent(), hasFiltered)) { // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; @@ -2359,6 +2361,7 @@ QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QT } // copy a touch event's basic properties but give it new touch points +// TODO remove this variant and use QQuickPointerEvent/QQuickEventPoint QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList &newPoints) { Qt::TouchPointStates eventStates; @@ -2562,6 +2565,7 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF } #endif +// Child event filtering is legacy stuff, so here we use QTouchEvent bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *hasFiltered) { if (!target) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index a0e4f48330..7e75e63817 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -158,8 +158,8 @@ public: bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *); #endif static QQuickPointerDevice *touchDevice(QTouchDevice *d); - bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList &, QSet *, - QHash > *, QSet *filtered); + bool deliverPoints(QQuickItem *, const QQuickPointerEvent *, const QList &, + QSet *, QHash > *, QSet *); void handleTouchEvent(QTouchEvent *); void handleMouseEvent(QMouseEvent *); void deliverPointerEvent(QQuickPointerEvent *); @@ -169,7 +169,7 @@ public: void deliverDelayedTouchEvent(); void flushFrameSynchronousEvents(); bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); - bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, const QList &matchingPoints, QSet *filtered); + bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, const QList &matchingPoints, QSet *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); -- cgit v1.2.3 From 03305d33482b1447da2df8425f131477dd146064 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 12 Jul 2016 16:13:36 +0200 Subject: Sort pointer event delivery functions Change-Id: Ib21a2c4f762a53f2f1d386a40380a68a5383dbe3 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow_p.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 7e75e63817..e18127f3fb 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -158,24 +158,31 @@ public: bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *); #endif static QQuickPointerDevice *touchDevice(QTouchDevice *d); - bool deliverPoints(QQuickItem *, const QQuickPointerEvent *, const QList &, - QSet *, QHash > *, QSet *); + + // entry point of events to the window void handleTouchEvent(QTouchEvent *); void handleMouseEvent(QMouseEvent *); + bool compressTouchEvent(QTouchEvent *); + void flushFrameSynchronousEvents(); + void deliverDelayedTouchEvent(); + + // delivery of pointer events: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerEvent *); - bool compressTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - void deliverDelayedTouchEvent(); - void flushFrameSynchronousEvents(); - bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); + bool deliverPoints(QQuickItem *, const QQuickPointerEvent *, const QList &, + QSet *, QHash > *, QSet *); bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, const QList &matchingPoints, QSet *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); + + // hover delivery + bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool accepted); bool clearHover(); + #ifndef QT_NO_DRAGANDDROP void deliverDragEvent(QQuickDragGrabber *, QEvent *); bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *); -- cgit v1.2.3 From 909b37e45ec24234c2aecfb211fdc2dc4fe520b6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 12 Jul 2016 18:54:50 +0200 Subject: QQuickPointerEvent: make bool test functions public; use for bool tests Maybe if (isTouchEvent()) is more efficient than if (asTouchEvent()). Change-Id: Ie3e216ff5c9b512abc4a25690f98cb948e8b5399 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents_p_p.h | 16 +++++++--------- src/quick/items/qquickwindow.cpp | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 04f43667a7..ae55f2f399 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -419,9 +419,14 @@ public: * Returns nullptr in case the original event was not a mouse event. */ QMouseEvent *asMouseEvent() const; - int pointCount() const { return asTouchEvent() ? m_touchPoints.count() : 1; } + bool isMouseEvent() const; + bool isTouchEvent() const; + bool isTabletEvent() const; + bool isValid() const { return m_event != nullptr; } + + int pointCount() const { return isTouchEvent() ? m_touchPoints.count() : 1; } const QQuickEventPoint *point(int i) const { - if (asTouchEvent()) + if (isTouchEvent()) return m_touchPoints.at(i); return i == 0 ? m_mousePoint : nullptr; } @@ -431,13 +436,6 @@ public: QTouchEvent *touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const; protected: - bool isValid() const { return m_event != nullptr; } - -protected: - bool isMouseEvent() const; - bool isTouchEvent() const; - bool isTabletEvent() const; - const QQuickPointerDevice *m_device; QInputEvent *m_event; // original event as received by QQuickWindow Qt::MouseButton m_button; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index b6737338dd..ad2d361aa2 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2118,7 +2118,7 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) if (QMouseEvent *mouse = event->asMouseEvent()) { deliverMouseEvent(mouse); - } else if (event->asTouchEvent()) { + } else if (event->isTouchEvent()) { deliverTouchEvent(event); } else { Q_ASSERT(false); -- cgit v1.2.3 From 5e1a2785bcd623a13c7a1889deff6a28f3d2efea Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 13 Jul 2016 10:49:35 +0200 Subject: Fix quickcontrols test failure after tryCompare change One test also used tryCompare with indexes instead of property strings, so continue to allow that. Change-Id: If8bdb9935181666f975f02fd256734a9992d2b48 Reviewed-by: Mitch Curtis --- src/imports/testlib/TestCase.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index cff265869d..d5e256a123 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -711,8 +711,8 @@ Item { \sa compare(), SignalSpy::wait() */ function tryCompare(obj, prop, value, timeout, msg) { - if (arguments.length == 1 || typeof(prop) != "string") { - qtest_results.fail("A property name as string is required for tryCompare", + if (arguments.length == 1 || (typeof(prop) != "string" && typeof(prop) != "number")) { + qtest_results.fail("A property name as string or index is required for tryCompare", util.callerFile(), util.callerLine()) throw new Error("QtQuickTest::fail") } -- cgit v1.2.3 From 15621772e288c33d697cb7345ee04b8c33436d1f Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 29 Jun 2016 15:57:30 +0200 Subject: QML: QQmlPropertyCache: Allow for more inlining These are simple and often-called methods. Change-Id: Id681c75e13ec262c17dc00ce9f3a0c30c9183991 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache.cpp | 68 --------------------------------------- src/qml/qml/qqmlpropertycache_p.h | 66 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 68 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 84081e707d..614a485df6 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -423,12 +423,6 @@ void QQmlPropertyCache::appendMethod(const QString &name, quint32 flags, int cor setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0)); } -// Returns this property cache's metaObject. May be null if it hasn't been created yet. -const QMetaObject *QQmlPropertyCache::metaObject() const -{ - return _metaObject; -} - // Returns this property cache's metaObject, creating it if necessary. const QMetaObject *QQmlPropertyCache::createMetaObject() { @@ -444,22 +438,11 @@ const QMetaObject *QQmlPropertyCache::createMetaObject() return _metaObject; } -// Returns the name of the default property for this cache -QString QQmlPropertyCache::defaultPropertyName() const -{ - return _defaultPropertyName; -} - QQmlPropertyData *QQmlPropertyCache::defaultProperty() const { return property(defaultPropertyName(), 0, 0); } -QQmlPropertyCache *QQmlPropertyCache::parent() const -{ - return _parent; -} - void QQmlPropertyCache::setParent(QQmlPropertyCache *newParent) { if (_parent == newParent) @@ -470,15 +453,6 @@ void QQmlPropertyCache::setParent(QQmlPropertyCache *newParent) _parent->addref(); } -// Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by -// QML -const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const -{ - while (_parent && (_metaObject == 0 || _ownMetaObject)) - return _parent->firstCppMetaObject(); - return _metaObject; -} - QQmlPropertyCache * QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject, QQmlPropertyData::Flag propertyFlags, @@ -814,48 +788,6 @@ void QQmlPropertyCache::invalidate(const QMetaObject *metaObject) } } -/*! \internal - \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()). - This is different from QMetaMethod::methodIndex(). -*/ -QQmlPropertyData * -QQmlPropertyCache::signal(int index) const -{ - if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count())) - return 0; - - if (index < signalHandlerIndexCacheStart) - return _parent->signal(index); - - QQmlPropertyData *rv = const_cast(&methodIndexCache.at(index - signalHandlerIndexCacheStart)); - Q_ASSERT(rv->isSignal() || rv->coreIndex == -1); - return ensureResolved(rv); -} - -int QQmlPropertyCache::methodIndexToSignalIndex(int index) const -{ - if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) - return index; - - if (index < methodIndexCacheStart) - return _parent->methodIndexToSignalIndex(index); - - return index - methodIndexCacheStart + signalHandlerIndexCacheStart; -} - -QQmlPropertyData * -QQmlPropertyCache::method(int index) const -{ - if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) - return 0; - - if (index < methodIndexCacheStart) - return _parent->method(index); - - QQmlPropertyData *rv = const_cast(&methodIndexCache.at(index - methodIndexCacheStart)); - return ensureResolved(rv); -} - QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, QObject *object, QQmlContextData *context) const { QQmlData *data = (object ? QQmlData::get(object) : 0); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 26fa533103..aecfdf1a74 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -526,6 +526,21 @@ inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) return p; } +// Returns this property cache's metaObject. May be null if it hasn't been created yet. +inline const QMetaObject *QQmlPropertyCache::metaObject() const +{ + return _metaObject; +} + +// Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by +// QML +inline const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const +{ + while (_parent && (_metaObject == 0 || _ownMetaObject)) + return _parent->firstCppMetaObject(); + return _metaObject; +} + inline QQmlPropertyData *QQmlPropertyCache::property(int index) const { if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count())) @@ -538,6 +553,57 @@ inline QQmlPropertyData *QQmlPropertyCache::property(int index) const return ensureResolved(rv); } +inline QQmlPropertyData *QQmlPropertyCache::method(int index) const +{ + if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) + return 0; + + if (index < methodIndexCacheStart) + return _parent->method(index); + + QQmlPropertyData *rv = const_cast(&methodIndexCache.at(index - methodIndexCacheStart)); + return ensureResolved(rv); +} + +/*! \internal + \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()). + This is different from QMetaMethod::methodIndex(). +*/ +inline QQmlPropertyData *QQmlPropertyCache::signal(int index) const +{ + if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count())) + return 0; + + if (index < signalHandlerIndexCacheStart) + return _parent->signal(index); + + QQmlPropertyData *rv = const_cast(&methodIndexCache.at(index - signalHandlerIndexCacheStart)); + Q_ASSERT(rv->isSignal() || rv->coreIndex == -1); + return ensureResolved(rv); +} + +inline int QQmlPropertyCache::methodIndexToSignalIndex(int index) const +{ + if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) + return index; + + if (index < methodIndexCacheStart) + return _parent->methodIndexToSignalIndex(index); + + return index - methodIndexCacheStart + signalHandlerIndexCacheStart; +} + +// Returns the name of the default property for this cache +inline QString QQmlPropertyCache::defaultPropertyName() const +{ + return _defaultPropertyName; +} + +inline QQmlPropertyCache *QQmlPropertyCache::parent() const +{ + return _parent; +} + QQmlPropertyData * QQmlPropertyCache::overrideData(QQmlPropertyData *data) const { -- cgit v1.2.3 From 501d69e7b7f2a7722e206b70553fd9bfb592046e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 13 Jul 2016 14:12:13 +0200 Subject: qquickwindow_p.h: group and sort the forward declarations Change-Id: I3e260b592174254ce7b2fdc1b7c205cbca0babd1 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow_p.h | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index e18127f3fb..a1853cbc44 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -69,13 +69,19 @@ QT_BEGIN_NAMESPACE -//Make it easy to identify and customize the root item if needed - +class QOpenGLVertexArrayObjectHelper; class QQuickAnimatorController; -class QSGRenderLoop; -class QQuickRenderControl; class QQuickDragGrabber; +class QQuickItemPrivate; +class QQuickPointerDevice; +class QQuickRenderControl; +class QQuickWindowIncubationController; +class QQuickWindowPrivate; +class QQuickWindowRenderLoop; +class QSGRenderLoop; +class QTouchEvent; +//Make it easy to identify and customize the root item if needed class QQuickRootItem : public QQuickItem { Q_OBJECT @@ -86,16 +92,6 @@ public Q_SLOTS: void setHeight(int h) {QQuickItem::setHeight(qreal(h));} }; -class QQuickItemPrivate; -class QQuickWindowPrivate; - -class QTouchEvent; -class QQuickPointerDevice; -class QQuickWindowRenderLoop; -class QQuickWindowIncubationController; - -class QOpenGLVertexArrayObjectHelper; - class Q_QUICK_PRIVATE_EXPORT QQuickCustomRenderStage { public: -- cgit v1.2.3 From 35d500526990fafe5b71d97b051a770a2a81e82a Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 13 Jul 2016 11:22:11 +0200 Subject: Fix itemGeometryChanged() listeners We must iterate a (cheap) copy of changeListeners, because the container might be indirectly modified during the loop. For example, listeners may remove themselves from the list. In such scenario, the copy gets detached and the loop remains safe. I've added notes in comments, and extended the tests to prevent the issue re-surfacing again. Change-Id: Ib48d6e0765d45370d2fffa119a4c351e0119e40a Task-number: QTBUG-54732 Reviewed-by: Mitch Curtis --- src/quick/items/qquickitem.cpp | 25 +-- tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 263 +++++++++++++++--------- 2 files changed, 174 insertions(+), 114 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 79ddd449f5..07a415eecd 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2329,7 +2329,7 @@ QQuickItem::~QQuickItem() while (!d->childItems.isEmpty()) d->childItems.constFirst()->setParentItem(0); - const auto listeners = d->changeListeners; + const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate(); if (anchor) @@ -3572,7 +3572,7 @@ QQuickAnchors *QQuickItemPrivate::anchors() const void QQuickItemPrivate::siblingOrderChanged() { Q_Q(QQuickItem); - const auto listeners = changeListeners; + const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::SiblingOrder) { change.listener->itemSiblingOrderChanged(q); @@ -3686,7 +3686,8 @@ void QQuickItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo change.setWidthChange(diff.width() != 0); change.setHeightChange(diff.height() != 0); - for (const QQuickItemPrivate::ChangeListener &listener : qAsConst(d->changeListeners)) { + const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732) + for (const QQuickItemPrivate::ChangeListener &listener : listeners) { if (listener.types & QQuickItemPrivate::Geometry) { if (change.matches(listener.gTypes)) listener.listener->itemGeometryChanged(this, change, diff); @@ -4245,7 +4246,7 @@ void QQuickItem::setBaselineOffset(qreal offset) d->baselineOffset = offset; - const auto listeners = d->changeListeners; + const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Geometry) { QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate(); @@ -5920,7 +5921,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt switch (change) { case QQuickItem::ItemChildAddedChange: { q->itemChange(change, data); - const auto listeners = changeListeners; + const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Children) { change.listener->itemChildAdded(q, data.item); @@ -5930,7 +5931,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt } case QQuickItem::ItemChildRemovedChange: { q->itemChange(change, data); - const auto listeners = changeListeners; + const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Children) { change.listener->itemChildRemoved(q, data.item); @@ -5943,7 +5944,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt break; case QQuickItem::ItemVisibleHasChanged: { q->itemChange(change, data); - const auto listeners = changeListeners; + const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Visibility) { change.listener->itemVisibilityChanged(q); @@ -5953,7 +5954,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt } case QQuickItem::ItemParentHasChanged: { q->itemChange(change, data); - const auto listeners = changeListeners; + const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Parent) { change.listener->itemParentChanged(q, data.item); @@ -5963,7 +5964,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt } case QQuickItem::ItemOpacityHasChanged: { q->itemChange(change, data); - const auto listeners = changeListeners; + const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Opacity) { change.listener->itemOpacityChanged(q); @@ -5976,7 +5977,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt break; case QQuickItem::ItemRotationHasChanged: { q->itemChange(change, data); - const auto listeners = changeListeners; + const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::Rotation) { change.listener->itemRotationChanged(q); @@ -6338,7 +6339,7 @@ void QQuickItem::resetWidth() void QQuickItemPrivate::implicitWidthChanged() { Q_Q(QQuickItem); - const auto listeners = changeListeners; + const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::ImplicitWidth) { change.listener->itemImplicitWidthChanged(q); @@ -6502,7 +6503,7 @@ void QQuickItem::resetHeight() void QQuickItemPrivate::implicitHeightChanged() { Q_Q(QQuickItem); - const auto listeners = changeListeners; + const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732) for (const QQuickItemPrivate::ChangeListener &change : listeners) { if (change.types & QQuickItemPrivate::ImplicitHeight) { change.listener->itemImplicitHeightChanged(q); diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 62133d6475..169ef1cbab 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -2698,117 +2698,122 @@ void tst_QQuickItem::childrenRectBottomRightCorner() struct TestListener : public QQuickItemChangeListener { - TestListener(bool remove = false) : remove(remove) { reset(); } + TestListener(bool remove = false) : remove(remove) { } - void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange, const QRectF &) override + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange, const QRectF &diff) override { - ++itemGeometryChanges; - value = QRectF(item->x(), item->y(), item->width(), item->height()); + record(item, QQuickItemPrivate::Geometry, diff); + } + void itemSiblingOrderChanged(QQuickItem *item) override + { + record(item, QQuickItemPrivate::SiblingOrder); + } + void itemVisibilityChanged(QQuickItem *item) override + { + record(item, QQuickItemPrivate::Visibility); + } + void itemOpacityChanged(QQuickItem *item) override + { + record(item, QQuickItemPrivate::Opacity); + } + void itemRotationChanged(QQuickItem *item) override + { + record(item, QQuickItemPrivate::Rotation); + } + void itemImplicitWidthChanged(QQuickItem *item) override + { + record(item, QQuickItemPrivate::ImplicitWidth); + } + void itemImplicitHeightChanged(QQuickItem *item) override + { + record(item, QQuickItemPrivate::ImplicitHeight); } - void itemSiblingOrderChanged(QQuickItem *) override { ++itemSiblingOrderChanges; } - void itemVisibilityChanged(QQuickItem *) override { ++itemVisibilityChanges; } - void itemOpacityChanged(QQuickItem *) override { ++itemOpacityChanges; } - void itemRotationChanged(QQuickItem *) override { ++itemRotationChanges; } - void itemImplicitWidthChanged(QQuickItem *) override { ++itemImplicitWidthChanges; } - void itemImplicitHeightChanged(QQuickItem *) override { ++itemImplicitHeightChanges; } - void itemDestroyed(QQuickItem *item) override { - ++itemDestructions; - // QTBUG-53453 - if (remove) - QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed); + record(item, QQuickItemPrivate::Destroyed); } void itemChildAdded(QQuickItem *item, QQuickItem *child) override { - ++itemChildAdditions; - value = QVariant::fromValue(child); - // QTBUG-53453 - if (remove) - QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Children); + record(item, QQuickItemPrivate::Children, QVariant::fromValue(child)); } void itemChildRemoved(QQuickItem *item, QQuickItem *child) override { - ++itemChildRemovals; - value = QVariant::fromValue(child); - // QTBUG-53453 - if (remove) - QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Children); + record(item, QQuickItemPrivate::Children, QVariant::fromValue(child)); } void itemParentChanged(QQuickItem *item, QQuickItem *parent) override { - ++itemParentChanges; - value = QVariant::fromValue(parent); - // QTBUG-53453 - if (remove) - QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Parent); + record(item, QQuickItemPrivate::Parent, QVariant::fromValue(parent)); } QQuickAnchorsPrivate *anchorPrivate() override { return nullptr; } - void reset() + void record(QQuickItem *item, QQuickItemPrivate::ChangeType change, const QVariant &value = QVariant()) + { + changes += change; + values[change] = value; + // QTBUG-54732 + if (remove) + QQuickItemPrivate::get(item)->removeItemChangeListener(this, change); + } + + int count(QQuickItemPrivate::ChangeType change) const { - value = QVariant(); - itemGeometryChanges = 0; - itemSiblingOrderChanges = 0; - itemVisibilityChanges = 0; - itemOpacityChanges = 0; - itemDestructions = 0; - itemChildAdditions = 0; - itemChildRemovals = 0; - itemParentChanges = 0; - itemRotationChanges = 0; - itemImplicitWidthChanges = 0; - itemImplicitHeightChanges = 0; + return changes.count(change); + } + + QVariant value(QQuickItemPrivate::ChangeType change) const + { + return values.value(change); } bool remove; - QVariant value; - int itemGeometryChanges; - int itemSiblingOrderChanges; - int itemVisibilityChanges; - int itemOpacityChanges; - int itemDestructions; - int itemChildAdditions; - int itemChildRemovals; - int itemParentChanges; - int itemRotationChanges; - int itemImplicitWidthChanges; - int itemImplicitHeightChanges; + QList changes; + QHash values; }; void tst_QQuickItem::changeListener() { - QQuickItem item; + QQuickWindow window; + window.show(); + QTest::qWaitForWindowExposed(&window); + + QQuickItem *item = new QQuickItem; TestListener itemListener; - QQuickItemPrivate::get(&item)->addItemChangeListener(&itemListener, QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | - QQuickItemPrivate::Opacity | QQuickItemPrivate::Rotation); + QQuickItemPrivate::get(item)->addItemChangeListener(&itemListener, QQuickItemPrivate::ChangeTypes(0xffff)); + + item->setImplicitWidth(10); + QCOMPARE(itemListener.count(QQuickItemPrivate::ImplicitWidth), 1); + QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 1); + QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,10,0))); - item.setImplicitWidth(50); - QCOMPARE(itemListener.itemImplicitWidthChanges, 1); - QCOMPARE(itemListener.itemGeometryChanges, 1); - QCOMPARE(itemListener.value, QVariant(QRectF(0,0,50,0))); + item->setImplicitHeight(20); + QCOMPARE(itemListener.count(QQuickItemPrivate::ImplicitHeight), 1); + QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 2); + QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,0,20))); - item.setImplicitHeight(50); - QCOMPARE(itemListener.itemImplicitHeightChanges, 1); - QCOMPARE(itemListener.itemGeometryChanges, 2); - QCOMPARE(itemListener.value, QVariant(QRectF(0,0,50,50))); + item->setWidth(item->width() + 30); + QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 3); + QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,30,0))); - item.setWidth(100); - QCOMPARE(itemListener.itemGeometryChanges, 3); - QCOMPARE(itemListener.value, QVariant(QRectF(0,0,100,50))); + item->setHeight(item->height() + 40); + QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 4); + QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,0,40))); - item.setHeight(100); - QCOMPARE(itemListener.itemGeometryChanges, 4); - QCOMPARE(itemListener.value, QVariant(QRectF(0,0,100,100))); + item->setOpacity(0.5); + QCOMPARE(itemListener.count(QQuickItemPrivate::Opacity), 1); - item.setOpacity(0.5); - QCOMPARE(itemListener.itemOpacityChanges, 1); + item->setRotation(90); + QCOMPARE(itemListener.count(QQuickItemPrivate::Rotation), 1); + + item->setParentItem(window.contentItem()); + QCOMPARE(itemListener.count(QQuickItemPrivate::Parent), 1); + + item->setVisible(false); + QCOMPARE(itemListener.count(QQuickItemPrivate::Visibility), 1); - item.setRotation(90); - QCOMPARE(itemListener.itemRotationChanges, 1); + QQuickItemPrivate::get(item)->removeItemChangeListener(&itemListener, QQuickItemPrivate::ChangeTypes(0xffff)); - QQuickItem *parent = new QQuickItem; + QQuickItem *parent = new QQuickItem(window.contentItem()); TestListener parentListener; QQuickItemPrivate::get(parent)->addItemChangeListener(&parentListener, QQuickItemPrivate::Children); @@ -2820,52 +2825,79 @@ void tst_QQuickItem::changeListener() QQuickItemPrivate::get(child2)->addItemChangeListener(&child2Listener, QQuickItemPrivate::Parent | QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::Destroyed); child1->setParentItem(parent); - QCOMPARE(parentListener.itemChildAdditions, 1); - QCOMPARE(parentListener.value, QVariant::fromValue(child1)); - QCOMPARE(child1Listener.itemParentChanges, 1); - QCOMPARE(child1Listener.value, QVariant::fromValue(parent)); + QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 1); + QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child1)); + QCOMPARE(child1Listener.count(QQuickItemPrivate::Parent), 1); + QCOMPARE(child1Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue(parent)); child2->setParentItem(parent); - QCOMPARE(parentListener.itemChildAdditions, 2); - QCOMPARE(parentListener.value, QVariant::fromValue(child2)); - QCOMPARE(child2Listener.itemParentChanges, 1); - QCOMPARE(child2Listener.value, QVariant::fromValue(parent)); + QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 2); + QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child2)); + QCOMPARE(child2Listener.count(QQuickItemPrivate::Parent), 1); + QCOMPARE(child2Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue(parent)); child2->stackBefore(child1); - QCOMPARE(child1Listener.itemSiblingOrderChanges, 1); - QCOMPARE(child2Listener.itemSiblingOrderChanges, 1); + QCOMPARE(child1Listener.count(QQuickItemPrivate::SiblingOrder), 1); + QCOMPARE(child2Listener.count(QQuickItemPrivate::SiblingOrder), 1); child1->setParentItem(nullptr); - QCOMPARE(parentListener.itemChildRemovals, 1); - QCOMPARE(parentListener.value, QVariant::fromValue(child1)); - QCOMPARE(child1Listener.itemParentChanges, 2); - QCOMPARE(child1Listener.value, QVariant::fromValue(nullptr)); + QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 3); + QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child1)); + QCOMPARE(child1Listener.count(QQuickItemPrivate::Parent), 2); + QCOMPARE(child1Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue(nullptr)); delete child1; - QCOMPARE(child1Listener.itemDestructions, 1); + QCOMPARE(child1Listener.count(QQuickItemPrivate::Destroyed), 1); delete child2; - QCOMPARE(parentListener.itemChildRemovals, 2); - QCOMPARE(parentListener.value, QVariant::fromValue(child2)); - QCOMPARE(child2Listener.itemParentChanges, 2); - QCOMPARE(child2Listener.value, QVariant::fromValue(nullptr)); - QCOMPARE(child2Listener.itemDestructions, 1); + QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 4); + QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child2)); + QCOMPARE(child2Listener.count(QQuickItemPrivate::Parent), 2); + QCOMPARE(child2Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue(nullptr)); + QCOMPARE(child2Listener.count(QQuickItemPrivate::Destroyed), 1); QQuickItemPrivate::get(parent)->removeItemChangeListener(&parentListener, QQuickItemPrivate::Children); QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); - // QTBUG-53453: all listeners should get invoked even if they remove themselves while iterating the listeners + // QTBUG-54732: all listeners should get invoked even if they remove themselves while iterating the listeners QList listeners; for (int i = 0; i < 5; ++i) listeners << new TestListener(true); + // itemVisibilityChanged x 5 + foreach (TestListener *listener, listeners) + QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Visibility); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + parent->setVisible(false); + foreach (TestListener *listener, listeners) + QCOMPARE(listener->count(QQuickItemPrivate::Visibility), 1); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + + // itemRotationChanged x 5 + foreach (TestListener *listener, listeners) + QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Rotation); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + parent->setRotation(90); + foreach (TestListener *listener, listeners) + QCOMPARE(listener->count(QQuickItemPrivate::Rotation), 1); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + + // itemOpacityChanged x 5 + foreach (TestListener *listener, listeners) + QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Opacity); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + parent->setOpacity(0.5); + foreach (TestListener *listener, listeners) + QCOMPARE(listener->count(QQuickItemPrivate::Opacity), 1); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + // itemChildAdded() x 5 foreach (TestListener *listener, listeners) QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children); QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); child1 = new QQuickItem(parent); foreach (TestListener *listener, listeners) - QCOMPARE(listener->itemChildAdditions, 1); + QCOMPARE(listener->count(QQuickItemPrivate::Children), 1); QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); // itemParentChanged() x 5 @@ -2874,9 +2906,36 @@ void tst_QQuickItem::changeListener() QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.count(), listeners.count()); child1->setParentItem(nullptr); foreach (TestListener *listener, listeners) - QCOMPARE(listener->itemParentChanges, 1); + QCOMPARE(listener->count(QQuickItemPrivate::Parent), 1); QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.count(), 0); + // itemImplicitWidthChanged() x 5 + foreach (TestListener *listener, listeners) + QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitWidth); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + parent->setImplicitWidth(parent->implicitWidth() + 1); + foreach (TestListener *listener, listeners) + QCOMPARE(listener->count(QQuickItemPrivate::ImplicitWidth), 1); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + + // itemImplicitHeightChanged() x 5 + foreach (TestListener *listener, listeners) + QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitHeight); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + parent->setImplicitHeight(parent->implicitHeight() + 1); + foreach (TestListener *listener, listeners) + QCOMPARE(listener->count(QQuickItemPrivate::ImplicitHeight), 1); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + + // itemGeometryChanged() x 5 + foreach (TestListener *listener, listeners) + QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Geometry); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + parent->setWidth(parent->width() + 1); + foreach (TestListener *listener, listeners) + QCOMPARE(listener->count(QQuickItemPrivate::Geometry), 1); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + // itemChildRemoved() x 5 child1->setParentItem(parent); foreach (TestListener *listener, listeners) @@ -2884,7 +2943,7 @@ void tst_QQuickItem::changeListener() QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); delete child1; foreach (TestListener *listener, listeners) - QCOMPARE(listener->itemChildRemovals, 1); + QCOMPARE(listener->count(QQuickItemPrivate::Children), 2); QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); // itemDestroyed() x 5 @@ -2893,7 +2952,7 @@ void tst_QQuickItem::changeListener() QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); delete parent; foreach (TestListener *listener, listeners) - QCOMPARE(listener->itemDestructions, 1); + QCOMPARE(listener->count(QQuickItemPrivate::Destroyed), 1); } // QTBUG-13893 -- cgit v1.2.3 From c333b097be9c5cc20111e5ea03322b33afb84090 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 13 Jul 2016 11:44:10 +0200 Subject: qquickitemchangelistener_p.h: add a comment for the sake of clarity QQuickItemChangeListener::itemGeometryChanged() used to have two QRectF arguments, which were the new and old geometries. In commit e2c296c the remaining QRectF argument was changed to a diff of the geometry change. The meaning of the unnamed argument is not obvious from the method signature, so add an explanatory comment similar to 83cd7c2. Change-Id: I62043b3399a86be3490e48c83cedff4f9d630652 Reviewed-by: Mitch Curtis Reviewed-by: Robin Burchell --- src/quick/items/qquickitemchangelistener_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h index 7a4949fc0b..19ff73056b 100644 --- a/src/quick/items/qquickitemchangelistener_p.h +++ b/src/quick/items/qquickitemchangelistener_p.h @@ -120,7 +120,7 @@ class QQuickItemChangeListener public: virtual ~QQuickItemChangeListener() {} - virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) {} + virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF & /* diff */) {} virtual void itemSiblingOrderChanged(QQuickItem *) {} virtual void itemVisibilityChanged(QQuickItem *) {} virtual void itemOpacityChanged(QQuickItem *) {} -- cgit v1.2.3 From 36d4501a08b1c8708136d3636236a1c6256fbcea Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 13 Jul 2016 12:03:20 +0200 Subject: Fix crash in qmljs When running in pure JS mode, we may not have a QQmlEngine. Change-Id: Ibb6e9087312b13cca53cb672a8f5372764cdf515 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compileddata.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index b05abdc0c8..a58bd3d1f8 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -215,8 +215,8 @@ void CompilationUnit::markObjects(QV4::ExecutionEngine *e) void CompilationUnit::destroy() { QQmlEngine *qmlEngine = 0; - if (engine) - qmlEngine = engine->qmlEngine(); + if (engine && engine->v8Engine) + qmlEngine = engine->v8Engine->engine(); if (qmlEngine) QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); else -- cgit v1.2.3 From a25f0ed5d2605e981cdb774fb1734f168910f3a7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Jul 2016 14:07:49 +0200 Subject: Minor cleanup in location handling Store the line and column in CompiledData::Location as unsigned values. The qmlSourceCoordinate() function(s) already now act as normalizers, mapping values <= 0 to 0 as indicator for a missing/invalid line/column. Valid values start at 1 and therefore there is no need to store negative values in the location structure. Coincidentally this also fixes a bunch of warnings about conversions from signed to unsigned. Change-Id: Ic69ff395d4991989aede695f2e8c58903f1bd2bf Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata_p.h | 6 +++--- src/qml/compiler/qv4jsir.cpp | 4 ++-- src/qml/compiler/qv4jsir_p.h | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index a9a0ebbf51..f796061cb7 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -115,10 +115,10 @@ struct TableIterator struct Location { - qint32 line : 20; - qint32 column : 12; + quint32 line : 20; + quint32 column : 12; - Location(): line(-1), column(-1) {} + Location(): line(0), column(0) {} inline bool operator<(const Location &other) const { return line < other.line || diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index c6a6c95cd8..583311d1ae 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -370,8 +370,8 @@ Function::Function(Module *module, Function *outer, const QString &name) , hasWith(false) , isQmlBinding(false) , unused(0) - , line(-1) - , column(-1) + , line(0) + , column(0) , _allBasicBlocks(0) , _statementCount(0) { diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index a2bd6ad044..2d6d7d728f 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -1275,9 +1275,9 @@ struct Function { uint isQmlBinding: 1; uint unused : 24; - // Location of declaration in source code (-1 if not specified) - int line; - int column; + // Location of declaration in source code (0 if not specified) + uint line; + uint column; // Qml extension: SmallSet idObjectDependencies; -- cgit v1.2.3 From ccc5c54602821761a2f1a42c4bc473afd53439c9 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 12 Jul 2016 16:33:15 +0200 Subject: Re-factor deliverInitialMousePress Use pointerTargets to get a list of relevant QQuickItems, try delivering the event to them. Change-Id: Ib45f05ce84181c9a0c092dbc255f75e5f1f220af Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 51 +++++++++++++++------------------------- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ad2d361aa2..4a61c91f21 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1606,42 +1606,28 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t return me; } -bool QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event) +bool QQuickWindowPrivate::deliverInitialMousePressEvent(QMouseEvent *event) { Q_Q(QQuickWindow); - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - - if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - QPointF p = item->mapFromScene(event->windowPos()); - if (!item->contains(p)) - return false; - } - - QList children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QQuickItem *child = children.at(ii); - if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled) - continue; - if (deliverInitialMousePressEvent(child, event)) - return true; - } - - if (itemPrivate->acceptedMouseButtons() & event->button()) { - QPointF localPos = item->mapFromScene(event->windowPos()); - if (item->contains(localPos)) { - QScopedPointer me(cloneMouseEvent(event, &localPos)); - me->accept(); - item->grabMouse(); - q->sendEvent(item, me.data()); - event->setAccepted(me->isAccepted()); - if (me->isAccepted()) - return true; - if (mouseGrabberItem) - mouseGrabberItem->ungrabMouse(); + QVector targets = pointerTargets(contentItem, event->windowPos()); + for (QQuickItem *item: qAsConst(targets)) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + if (itemPrivate->acceptedMouseButtons() & event->button()) { + QPointF localPos = item->mapFromScene(event->windowPos()); + if (item->contains(localPos)) { + QScopedPointer me(cloneMouseEvent(event, &localPos)); + me->accept(); + item->grabMouse(); + q->sendEvent(item, me.data()); + event->setAccepted(me->isAccepted()); + if (me->isAccepted()) + return true; + if (mouseGrabberItem) + mouseGrabberItem->ungrabMouse(); + } } } - return false; } @@ -1654,7 +1640,8 @@ bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) if (!mouseGrabberItem && (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) && (event->buttons() & event->button()) == event->buttons()) { - if (deliverInitialMousePressEvent(contentItem, event)) + + if (deliverInitialMousePressEvent(event)) event->accept(); else event->ignore(); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index a1853cbc44..2af60ef9a7 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -144,7 +144,7 @@ public: void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); static void transformTouchPoints(QList &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); - bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *); + bool deliverInitialMousePressEvent(QMouseEvent *); bool deliverMouseEvent(QMouseEvent *); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet *); #ifndef QT_NO_WHEELEVENT -- cgit v1.2.3 From 6adc36115f4fc658fb907ee8af3013f2609ae761 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 12 Jul 2016 16:20:50 +0200 Subject: Add pointerTargets to determine list of items at a position Change-Id: If4e040063f0141ef745a4ee335cdec0640a73c64 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 30 ++++++++++++++++++++++++++++++ src/quick/items/qquickwindow_p.h | 1 + 2 files changed, 31 insertions(+) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 4a61c91f21..7a9a096c34 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2114,6 +2114,36 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) --pointerEventRecursionGuard; } +// check if item or any of its child items contain the point +// FIXME: should this be iterative instead of recursive? +QVector QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos) const +{ + QVector targets; + auto itemPrivate = QQuickItemPrivate::get(item); + QPointF itemPos = item->mapFromScene(scenePos); + // if the item clips, we can potentially return early + if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { + if (!item->contains(itemPos)) + return targets; + } + + // recurse for children + QList children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QQuickItem *child = children.at(ii); + auto childPrivate = QQuickItemPrivate::get(child); + if (!child->isVisible() || !child->isEnabled() || childPrivate->culled) + continue; + targets << pointerTargets(child, scenePos); + } + + if (item->contains(itemPos) && itemPrivate->acceptedMouseButtons()) { + // add this item last - children take precedence + targets << item; + } + return targets; +} + void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 2af60ef9a7..7183d17514 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -173,6 +173,7 @@ public: static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); + QVector pointerTargets(QQuickItem *, const QPointF &) const; // hover delivery bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, -- cgit v1.2.3 From eb3496167543753b6e8f5e85c69a1a77f95674b0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 11 Jul 2016 22:30:12 +0200 Subject: QQuickPointerEvent: store pointCount separately In 649b309ea9ed7d03aa74565d51edb416c23460d9 it was correct that the pool did not need to be static, because the whole event is a singleton; but it allowed the actual m_touchPoints vector to keep growing, rather than resizing to the current number of points. When one finger is pressed, then two, then one is released, pointCount must go from 1 -> 2 -> 1. And yet we'd like to avoid heap-allocating a fresh QQuickEventTouchPoint object every time a touchpoint is pressed. The choices are then to 1) exploit implementation details of QVector to allow its count() to be different than its stored size but still guarantee that every pointer it stores points to a long-lived QQuickEventTouchPoint (the docs do say "Since Qt 5.6, resize() doesn't shrink the capacity anymore"), which is what we thought we were doing; 2) store the pointCount in a separate variable, so that the prefix of m_touchPoints are the valid ones while the remainder is the "pool", or 3) hold a separate reusable pool of touchpoints. The separate m_pointCount is cheaper than the pool, so let's go with option 2. Change-Id: Ic59b5393e9f4b05e4ec27db15d6f06253aa820d5 Reviewed-by: Shawn Rutledge Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 10 ++++++---- src/quick/items/qquickevents_p_p.h | 10 ++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index d670898020..c79f77061a 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -475,6 +475,7 @@ void QQuickPointerEvent::initFromMouse(QMouseEvent *ev) { if (!m_mousePoint) m_mousePoint = new QQuickEventPoint; + m_pointCount = 1; m_mousePoint->reset(state, ev->windowPos(), 0); // mouse is 0 } @@ -485,11 +486,12 @@ void QQuickPointerEvent::initFromTouch(QTouchEvent *ev) { m_pressedButtons = Qt::NoButton; const QList &tps = ev->touchPoints(); - const int pointCount = tps.count(); - while (pointCount > m_touchPoints.count()) - m_touchPoints.append(new QQuickEventTouchPoint); + m_pointCount = tps.count(); + m_touchPoints.reserve(m_pointCount); + for (int i = m_touchPoints.size(); i < m_pointCount; ++i) + m_touchPoints.insert(i, new QQuickEventTouchPoint); - for (int i = 0; i < pointCount; ++i) + for (int i = 0; i < m_pointCount; ++i) m_touchPoints.at(i)->reset(tps.at(i)); } diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index ae55f2f399..0ce395d7df 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -395,6 +395,7 @@ public: , m_event(nullptr) , m_button(Qt::NoButton) , m_pressedButtons(Qt::NoButton) + , m_pointCount(0) , m_mousePoint(nullptr) { } /** Reset the current event to \a ev. @@ -424,11 +425,15 @@ public: bool isTabletEvent() const; bool isValid() const { return m_event != nullptr; } - int pointCount() const { return isTouchEvent() ? m_touchPoints.count() : 1; } + int pointCount() const { return m_pointCount; } const QQuickEventPoint *point(int i) const { + if (Q_UNLIKELY(i < 0 || i >= m_pointCount)) + return nullptr; if (isTouchEvent()) return m_touchPoints.at(i); - return i == 0 ? m_mousePoint : nullptr; + if (isMouseEvent()) + return m_mousePoint; + return nullptr; } const QTouchEvent::TouchPoint *touchPointById(int pointId) const; @@ -440,6 +445,7 @@ protected: QInputEvent *m_event; // original event as received by QQuickWindow Qt::MouseButton m_button; Qt::MouseButtons m_pressedButtons; + int m_pointCount; QVector m_touchPoints; QQuickEventPoint *m_mousePoint; -- cgit v1.2.3 From 73eceacfaceff0b860a32da2da9e04f3065931b8 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 13 Jul 2016 14:55:50 +0200 Subject: add tst_qquickwindow::eventPointCount Change-Id: I552cf69fa179d8b1260a5502e37553fa57bf32cf Reviewed-by: Frederik Gladhorn --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 0985198d65..2a3f54fcd8 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -370,6 +370,9 @@ private slots: void testRenderJob(); void testHoverChildMouseEventFilter(); + + void pointerEventTypeAndPointCount(); + private: QTouchDevice *touchDevice; QTouchDevice *touchDeviceWithVelocity; @@ -2326,6 +2329,52 @@ void tst_qquickwindow::testHoverChildMouseEventFilter() QCOMPARE(middleItem->eventCount(QEvent::HoverEnter), 0); } +void tst_qquickwindow::pointerEventTypeAndPointCount() +{ + QQuickPointerEvent pe; + QMouseEvent me(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QTouchEvent te(QEvent::TouchBegin, touchDevice, Qt::NoModifier, Qt::TouchPointPressed, + QList() << QTouchEvent::TouchPoint(1)); + + QVERIFY(!pe.isValid()); + + pe.reset(&me); + QVERIFY(pe.isValid()); + QVERIFY(pe.isMouseEvent()); + QVERIFY(!pe.isTouchEvent()); + QVERIFY(!pe.isTabletEvent()); + QVERIFY(pe.asMouseEvent()); + QVERIFY(!pe.asTouchEvent()); +// QVERIFY(!pe.asTabletEvent()); // TODO + QCOMPARE(pe.pointCount(), 1); + + pe.reset(&te); + QVERIFY(pe.isValid()); + QVERIFY(!pe.isMouseEvent()); + QVERIFY(pe.isTouchEvent()); + QVERIFY(!pe.isTabletEvent()); + QVERIFY(!pe.asMouseEvent()); + QVERIFY(pe.asTouchEvent()); +// QVERIFY(!pe.asTabletEvent()); // TODO + QCOMPARE(pe.pointCount(), 1); + QCOMPARE(pe.touchPointById(1)->id(), 1); + QVERIFY(!pe.touchPointById(0)); + + te.setTouchPoints(QList() << QTouchEvent::TouchPoint(1) << QTouchEvent::TouchPoint(2)); + pe.reset(&te); + QCOMPARE(pe.pointCount(), 2); + QCOMPARE(pe.touchPointById(1)->id(), 1); + QCOMPARE(pe.touchPointById(2)->id(), 2); + QVERIFY(!pe.touchPointById(0)); + + te.setTouchPoints(QList() << QTouchEvent::TouchPoint(2)); + pe.reset(&te); + QCOMPARE(pe.pointCount(), 1); + QCOMPARE(pe.touchPointById(2)->id(), 2); + QVERIFY(!pe.touchPointById(1)); + QVERIFY(!pe.touchPointById(0)); +} + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" -- cgit v1.2.3 From c100c6360881f4a151330d405e91346f61564674 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 11 Jul 2016 22:10:03 +0200 Subject: QQuickPointerEvent: cleanup of docs and file organization Change-Id: I104edaabc35a073e14e8c66cd268ecd3782ed361 Reviewed-by: Shawn Rutledge Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 69 +++++++++++++++++++++++++++++--------- src/quick/items/qquickevents_p_p.h | 36 +++++--------------- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index c79f77061a..9c32c9781c 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -439,7 +439,24 @@ Item { \l inverted always returns false. */ +/*! + \internal + \class QQuickPointerEvent + + QQuickPointerEvent is used as a long-lived object to store data related to + an event from a pointing device, such as a mouse, touch or tablet event, + during event delivery. It also provides properties which may be used later + to expose the event to QML, the same as is done with QQuickMouseEvent, + QQuickTouchPoint, QQuickKeyEvent, etc. Since only one event can be + delivered at a time, this class is effectively a singleton. We don't worry + about the QObject overhead because we never dynamically create and destroy + objects of this type. +*/ +/*! + \internal + Reset the current event to \a ev, which must be a touch, mouse or tablet event. +*/ QQuickPointerEvent *QQuickPointerEvent::reset(QEvent *ev) { m_event = static_cast(ev); if (isMouseEvent()) { @@ -495,19 +512,26 @@ void QQuickPointerEvent::initFromTouch(QTouchEvent *ev) { m_touchPoints.at(i)->reset(tps.at(i)); } +/*! + \internal + Returns the original touch event, or nullptr if it was not a touch event. +*/ QTouchEvent *QQuickPointerEvent::asTouchEvent() const { if (!isTouchEvent()) return nullptr; return static_cast(m_event); } +/*! + \internal + Returns the original mouse event, or nullptr if it was not a mouse event. +*/ QMouseEvent *QQuickPointerEvent::asMouseEvent() const { if (isMouseEvent()) return static_cast(m_event); return nullptr; } - bool QQuickPointerEvent::isMouseEvent() const { return m_event @@ -538,10 +562,36 @@ bool QQuickPointerEvent::isTabletEvent() const } } -/* - \internal +const QQuickEventPoint *QQuickPointerEvent::point(int i) const { + if (Q_UNLIKELY(i < 0 || i >= m_pointCount)) + return nullptr; + if (isTouchEvent()) + return m_touchPoints.at(i); + if (isMouseEvent()) + return m_mousePoint; + return nullptr; +} + +/*! + \internal + Returns a pointer to the original TouchPoint which has the same + \l {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a + QTouchEvent, and if that point is found. Otherwise, returns nullptr. +*/ +const QTouchEvent::TouchPoint *QQuickPointerEvent::touchPointById(int pointId) const { + const QTouchEvent *ev = asTouchEvent(); + if (!ev) + return nullptr; + const QList &tps = ev->touchPoints(); + auto it = std::find_if(tps.constBegin(), tps.constEnd(), + [&pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } ); + // return the pointer to the actual TP in QTouchEvent::_touchPoints + return (it == tps.end() ? nullptr : it.operator->()); +} - make a new QTouchEvent, giving it a subset of the original touch points +/*! + \internal + Make a new QTouchEvent, giving it a subset of the original touch points. */ QTouchEvent *QQuickPointerEvent::touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const { @@ -592,15 +642,4 @@ QTouchEvent *QQuickPointerEvent::touchEventForItem(const QList &tps = ev->touchPoints(); - auto it = std::find_if(tps.constBegin(), tps.constEnd(), - [&pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } ); - // return the pointer to the actual TP in QTouchEvent::_touchPoints - return (it == tps.end() ? nullptr : it.operator->()); -} - QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 0ce395d7df..e5f2ac3fb7 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -398,26 +398,16 @@ public: , m_pointCount(0) , m_mousePoint(nullptr) { } - /** Reset the current event to \a ev. - * - * ev must be a touch, mouse or tablet event. - */ - QQuickPointerEvent *reset(QEvent *ev); - +public: // property accessors const QQuickPointerDevice *device() const { return m_device; } Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; } Qt::MouseButton button() const { return m_button; } Qt::MouseButtons buttons() const { return m_pressedButtons; } - // ---------------------------------------------------- - // helpers for C++ event delivery, not for QML properties +public: // helpers for C++ only (during event delivery) + QQuickPointerEvent *reset(QEvent *ev); - /** Returns the original touch event. */ QTouchEvent *asTouchEvent() const; - - /** Returns the original mouse event. - * - * Returns nullptr in case the original event was not a mouse event. */ QMouseEvent *asMouseEvent() const; bool isMouseEvent() const; @@ -426,21 +416,17 @@ public: bool isValid() const { return m_event != nullptr; } int pointCount() const { return m_pointCount; } - const QQuickEventPoint *point(int i) const { - if (Q_UNLIKELY(i < 0 || i >= m_pointCount)) - return nullptr; - if (isTouchEvent()) - return m_touchPoints.at(i); - if (isMouseEvent()) - return m_mousePoint; - return nullptr; - } + const QQuickEventPoint *point(int i) const; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; QTouchEvent *touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const; -protected: +private: + void initFromMouse(QMouseEvent *ev); + void initFromTouch(QTouchEvent *ev); + +private: const QQuickPointerDevice *m_device; QInputEvent *m_event; // original event as received by QQuickWindow Qt::MouseButton m_button; @@ -449,10 +435,6 @@ protected: QVector m_touchPoints; QQuickEventPoint *m_mousePoint; -private: - void initFromMouse(QMouseEvent *ev); - void initFromTouch(QTouchEvent *ev); - Q_DISABLE_COPY(QQuickPointerEvent) }; -- cgit v1.2.3 From cfe4e8b8dc994ed84d4bbb6c52ae1b33336bc3d9 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 13 Jul 2016 14:42:47 +0200 Subject: tst_qquickwindow: make use of new QTest::createTouchDevice function So gui-private API isn't necessary anymore. Same as change 94324bd93fe9f4f61346b8f9e934c01279cb700e Task-number: QTBUG-44030 Change-Id: I780cc1eedf9285336b96219a1fb1cd4016512701 Reviewed-by: Frederik Gladhorn --- tests/auto/quick/qquickwindow/qquickwindow.pro | 2 +- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 15 +++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/tests/auto/quick/qquickwindow/qquickwindow.pro b/tests/auto/quick/qquickwindow/qquickwindow.pro index f0d287f30f..05093ba8e0 100644 --- a/tests/auto/quick/qquickwindow/qquickwindow.pro +++ b/tests/auto/quick/qquickwindow/qquickwindow.pro @@ -7,7 +7,7 @@ include(../shared/util.pri) macx:CONFIG -= app_bundle -QT += core-private gui-private qml-private quick-private testlib +QT += core-private qml-private quick-private testlib TESTDATA = data/* diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 0985198d65..329bbbed60 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -40,7 +40,6 @@ #include "../shared/visualtestutil.h" #include "../shared/viewtestutil.h" #include -#include #include #include #include @@ -275,22 +274,14 @@ class tst_qquickwindow : public QQmlDataTest Q_OBJECT public: tst_qquickwindow() + : touchDevice(QTest::createTouchDevice()) + , touchDeviceWithVelocity(QTest::createTouchDevice()) { QQuickWindow::setDefaultAlphaBuffer(true); + touchDeviceWithVelocity->setCapabilities(QTouchDevice::Position | QTouchDevice::Velocity); } private slots: - void initTestCase() - { - QQmlDataTest::initTestCase(); - touchDevice = new QTouchDevice; - touchDevice->setType(QTouchDevice::TouchScreen); - QWindowSystemInterface::registerTouchDevice(touchDevice); - touchDeviceWithVelocity = new QTouchDevice; - touchDeviceWithVelocity->setType(QTouchDevice::TouchScreen); - touchDeviceWithVelocity->setCapabilities(QTouchDevice::Position | QTouchDevice::Velocity); - QWindowSystemInterface::registerTouchDevice(touchDeviceWithVelocity); - } void cleanup(); #ifndef QT_NO_OPENGL void openglContextCreatedSignal(); -- cgit v1.2.3 From 8dc7cb73ffdcd50fbbe2a1bc74a06b79092db4d8 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Sun, 10 Jul 2016 01:15:38 +0200 Subject: QQuickImplicitSizeItem: Document the purpose of this class I only just figured this out myself after staring at it for a few minutes, so save someone else wandering down this path the trouble. Change-Id: I17cd26b78c678ca2993e6936ca3359eb1eb49c80 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickimplicitsizeitem.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/quick/items/qquickimplicitsizeitem.cpp b/src/quick/items/qquickimplicitsizeitem.cpp index 5dadf81ce4..08886329fd 100644 --- a/src/quick/items/qquickimplicitsizeitem.cpp +++ b/src/quick/items/qquickimplicitsizeitem.cpp @@ -42,6 +42,19 @@ QT_BEGIN_NAMESPACE +/*! + \internal + + The purpose of QQuickImplicitSizeItem is not immediately clear, as both + the implicit size properties and signals exist on QQuickItem. However, + for some items - where the implicit size has an underlying meaning (such as + Image, where the implicit size represents the real size of the image) + having implicit size writable is an undesirable thing. + + QQuickImplicitSizeItem redefines the properties as being readonly. + Unfortunately, this also means they need to redefine the change signals. + See QTBUG-30258 for more information. +*/ void QQuickImplicitSizeItemPrivate::implicitWidthChanged() { Q_Q(QQuickImplicitSizeItem); -- cgit v1.2.3 From be491913c036b148cd4f90fa0132e269a507dbad Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Jul 2016 14:19:09 +0200 Subject: Fix endian support in compiled data structures The goal is to make the compiled data structures persistent on disk. In order to make it possible to create these data structures on a "host" system that may have a different endianness than the target system, we now make all the word sized fields little-endian. The template wrappers from QJson provide zero-overhead access for little-endian machines (the vast majority) while maintaining a large degree of source compatibility. Change-Id: I3d30da1fcf3bffb98dbe9337d3a35482fb7b57c8 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder.cpp | 11 +- src/qml/compiler/qqmlirbuilder_p.h | 2 +- src/qml/compiler/qqmltypecompiler.cpp | 10 +- src/qml/compiler/qv4compileddata.cpp | 6 +- src/qml/compiler/qv4compileddata_p.h | 313 ++++++++++++--------- src/qml/compiler/qv4compiler.cpp | 6 + src/qml/jsruntime/qv4function.cpp | 6 +- src/qml/qml/qqmljavascriptexpression.cpp | 6 +- src/qml/qml/qqmlobjectcreator.cpp | 2 +- src/qml/qml/qqmlvmemetaobject.cpp | 2 +- tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 20 +- .../qml/qqmltranslation/tst_qqmltranslation.cpp | 4 +- 12 files changed, 221 insertions(+), 167 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index c282c13c53..f736e04b88 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -193,7 +193,7 @@ void Object::appendFunction(QmlIR::Function *f) QString Object::appendBinding(Binding *b, bool isListBinding) { - const bool bindingToDefaultProperty = (b->propertyNameIndex == 0); + const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0)); if (!isListBinding && !bindingToDefaultProperty && b->type != QV4::CompiledData::Binding::Type_GroupProperty && b->type != QV4::CompiledData::Binding::Type_AttachedProperty @@ -640,7 +640,10 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node) } if (node->versionToken.isValid()) { - extractVersion(textRefAt(node->versionToken), &import->majorVersion, &import->minorVersion); + int major, minor; + extractVersion(textRefAt(node->versionToken), &major, &minor); + import->majorVersion = major; + import->minorVersion = minor; } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Library import requires a version")); return false; @@ -997,13 +1000,13 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST binding->value.b = false; } else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast(expr)) { binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.d = lit->value; + binding->setNumberValueInternal(lit->value); } else { if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast(expr)) { if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast(unaryMinus->expression)) { binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.d = -lit->value; + binding->setNumberValueInternal(-lit->value); } } } diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index af5b3d4ed5..4e0d4e563b 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -350,7 +350,7 @@ public: int id; int indexOfDefaultPropertyOrAlias; bool defaultPropertyIsAlias; - int flags; + quint32 flags; QV4::CompiledData::Location location; QV4::CompiledData::Location locationOfIdProperty; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index d73ce6f09d..ceff1611c5 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -546,7 +546,7 @@ bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QS COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString())); } binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.d = (double)enumValue; + binding->setNumberValueInternal((double)enumValue); binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; return true; } @@ -701,7 +701,7 @@ void QQmlAliasAnnotator::annotateBindingsToAliases() if (!binding->isValueBinding()) continue; bool notInRevision = false; - QQmlPropertyData *pd = binding->propertyNameIndex != 0 ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; + QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; if (pd && pd->isAlias()) binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias; } @@ -733,7 +733,7 @@ void QQmlScriptStringScanner::scan() if (binding->type != QV4::CompiledData::Binding::Type_Script) continue; bool notInRevision = false; - QQmlPropertyData *pd = binding->propertyNameIndex != 0 ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; + QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; if (!pd || pd->propType != scriptStringMetaType) continue; @@ -783,7 +783,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI } QQmlPropertyData *pd = 0; - if (binding->propertyNameIndex != 0) { + if (binding->propertyNameIndex != quint32(0)) { bool notInRevision = false; pd = propertyResolver.property(stringAt(binding->propertyNameIndex), ¬InRevision); } else { @@ -1346,7 +1346,7 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex) QmlIR::Binding *previousBinding = 0; QmlIR::Binding *binding = object->firstBinding(); while (binding) { - if (binding->propertyNameIndex == 0 || stringAt(binding->propertyNameIndex) != defaultProperty) { + if (binding->propertyNameIndex == quint32(0) || stringAt(binding->propertyNameIndex) != defaultProperty) { previousBinding = binding; binding = binding->next; continue; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index a58bd3d1f8..17eee03a0a 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -122,7 +122,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) for (uint i = 0; i < data->lookupTableSize; ++i) { QV4::Lookup *l = runtimeLookups + i; - Lookup::Type type = Lookup::Type(compiledLookups[i].type_and_flags); + Lookup::Type type = Lookup::Type(uint(compiledLookups[i].type_and_flags)); if (type == CompiledData::Lookup::Type_Getter) l->getter = QV4::Lookup::getterGeneric; else if (type == CompiledData::Lookup::Type_Setter) @@ -229,7 +229,7 @@ IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjec if (it == namedObjectsPerComponentCache.end()) { IdentifierHash namedObjectCache(engine); const CompiledData::Object *component = data->objectAt(componentObjectIndex); - const quint32 *namedObjectIndexPtr = component->namedObjectsInComponentTable(); + const LEUInt32 *namedObjectIndexPtr = component->namedObjectsInComponentTable(); for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr); namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); @@ -355,7 +355,7 @@ QString Binding::valueAsString(const Unit *unit) const case Type_Boolean: return value.b ? QStringLiteral("true") : QStringLiteral("false"); case Type_Number: - return QString::number(value.d); + return QString::number(valueAsNumber()); case Type_Invalid: return QString(); #ifdef QT_NO_TRANSLATION diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index f796061cb7..616ac6da0e 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -62,6 +62,7 @@ #include #include #include +#include #ifndef V4_BOOTSTRAP #include #include @@ -90,6 +91,12 @@ struct Function; namespace CompiledData { +typedef QJsonPrivate::q_littleendian LEInt16; +typedef QJsonPrivate::q_littleendian LEUInt16; +typedef QJsonPrivate::q_littleendian LEUInt32; +typedef QJsonPrivate::q_littleendian LEInt32; +typedef QJsonPrivate::q_littleendian LEUInt64; + struct String; struct Function; struct Lookup; @@ -115,10 +122,12 @@ struct TableIterator struct Location { - quint32 line : 20; - quint32 column : 12; + union { + QJsonPrivate::qle_bitfield<0, 20> line; + QJsonPrivate::qle_bitfield<20, 12> column; + }; - Location(): line(0), column(0) {} + Location() { line = 0; column = 0; } inline bool operator<(const Location &other) const { return line < other.line || @@ -128,20 +137,22 @@ struct Location struct RegExp { - enum Flags { + enum Flags : unsigned int { RegExp_Global = 0x01, RegExp_IgnoreCase = 0x02, RegExp_Multiline = 0x04 }; - quint32 flags : 4; - quint32 stringIndex : 28; + union { + QJsonPrivate::qle_bitfield<0, 4> flags; + QJsonPrivate::qle_bitfield<4, 28> stringIndex; + }; static int calculateSize() { return sizeof(RegExp); } }; struct Lookup { - enum Type { + enum Type : unsigned int { Type_Getter = 0x0, Type_Setter = 0x1, Type_GlobalGetter = 2, @@ -149,21 +160,29 @@ struct Lookup Type_IndexedSetter = 4 }; - quint32 type_and_flags : 4; - quint32 nameIndex : 28; + union { + QJsonPrivate::qle_bitfield<0, 4> type_and_flags; + QJsonPrivate::qle_bitfield<4, 28> nameIndex; + }; + + Lookup() { type_and_flags = 0; nameIndex = 0; } static int calculateSize() { return sizeof(Lookup); } }; struct JSClassMember { - quint32 nameOffset : 31; - quint32 isAccessor : 1; + union { + QJsonPrivate::qle_bitfield<0, 31> nameOffset; + QJsonPrivate::qle_bitfield<31, 1> isAccessor; + }; + + JSClassMember() { nameOffset = 0; isAccessor = 0; } }; struct JSClass { - uint nMembers; + LEUInt32 nMembers; // JSClassMember[nMembers] static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; } @@ -171,7 +190,7 @@ struct JSClass struct String { - qint32 size; + LEInt32 size; // uint16 strdata[] static int calculateSize(const QString &str) { @@ -181,7 +200,7 @@ struct String struct Function { - enum Flags { + enum Flags : unsigned int { HasDirectEval = 0x1, UsesArgumentsObject = 0x2, IsStrict = 0x4, @@ -190,43 +209,43 @@ struct Function }; quint8 flags; - quint32 nameIndex; - quint32 nFormals; - quint32 formalsOffset; - quint32 nLocals; - quint32 localsOffset; - quint32 nInnerFunctions; - quint32 innerFunctionsOffset; + LEUInt32 nameIndex; + LEUInt32 nFormals; + LEUInt32 formalsOffset; + LEUInt32 nLocals; + LEUInt32 localsOffset; + LEUInt32 nInnerFunctions; + LEUInt32 innerFunctionsOffset; Location location; // Qml Extensions Begin - quint32 nDependingIdObjects; - quint32 dependingIdObjectsOffset; // Array of resolved ID objects - quint32 nDependingContextProperties; - quint32 dependingContextPropertiesOffset; // Array of int pairs (property index and notify index) - quint32 nDependingScopeProperties; - quint32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index) + LEUInt32 nDependingIdObjects; + LEUInt32 dependingIdObjectsOffset; // Array of resolved ID objects + LEUInt32 nDependingContextProperties; + LEUInt32 dependingContextPropertiesOffset; // Array of int pairs (property index and notify index) + LEUInt32 nDependingScopeProperties; + LEUInt32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index) // Qml Extensions End // Absolute offset into file where the code for this function is located. Only used when the function // is serialized. - quint64 codeOffset; - quint64 codeSize; + LEUInt64 codeOffset; + LEUInt64 codeSize; // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] // quint32 offsetForInnerFunctions[nInnerFunctions] // Function[nInnerFunctions] - const quint32 *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } - const quint32 *localsTable() const { return reinterpret_cast(reinterpret_cast(this) + localsOffset); } - const quint32 *qmlIdObjectDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingIdObjectsOffset); } - const quint32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingContextPropertiesOffset); } - const quint32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingScopePropertiesOffset); } + const LEUInt32 *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } + const LEUInt32 *localsTable() const { return reinterpret_cast(reinterpret_cast(this) + localsOffset); } + const LEUInt32 *qmlIdObjectDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingIdObjectsOffset); } + const LEUInt32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingContextPropertiesOffset); } + const LEUInt32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingScopePropertiesOffset); } // --- QQmlPropertyCacheCreator interface - const quint32 *formalsBegin() const { return formalsTable(); } - const quint32 *formalsEnd() const { return formalsTable() + nFormals; } + const LEUInt32 *formalsBegin() const { return formalsTable(); } + const LEUInt32 *formalsEnd() const { return formalsTable() + nFormals; } // --- inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; } @@ -239,15 +258,15 @@ struct Function // Qml data structures struct Q_QML_EXPORT TranslationData { - quint32 commentIndex; - int number; + LEUInt32 commentIndex; + LEInt32 number; }; struct Q_QML_PRIVATE_EXPORT Binding { - quint32 propertyNameIndex; + LEUInt32 propertyNameIndex; - enum ValueType { + enum ValueType : unsigned int { Type_Invalid, Type_Boolean, Type_Number, @@ -260,7 +279,7 @@ struct Q_QML_PRIVATE_EXPORT Binding Type_GroupProperty }; - enum Flags { + enum Flags : unsigned int { IsSignalHandlerExpression = 0x1, IsSignalHandlerObject = 0x2, IsOnAssignment = 0x4, @@ -272,16 +291,18 @@ struct Q_QML_PRIVATE_EXPORT Binding IsCustomParserBinding = 0x100, }; - quint32 flags : 16; - quint32 type : 16; + union { + QJsonPrivate::qle_bitfield<0, 16> flags; + QJsonPrivate::qle_bitfield<16, 16> type; + }; union { bool b; - double d; - quint32 compiledScriptIndex; // used when Type_Script - quint32 objectIndex; + quint64 doubleValue; // do not access directly, needs endian protected access + LEUInt32 compiledScriptIndex; // used when Type_Script + LEUInt32 objectIndex; TranslationData translationData; // used when Type_Translation } value; - quint32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings) + LEUInt32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings) Location location; Location valueLocation; @@ -341,11 +362,20 @@ struct Q_QML_PRIVATE_EXPORT Binding QString valueAsScriptString(const Unit *unit) const; double valueAsNumber() const { - if (type == Type_Number) - return value.d; - return 0.0; - + if (type != Type_Number) + return 0.0; + quint64 intval = qFromLittleEndian(value.doubleValue); + double d; + memcpy(&d, &intval, sizeof(double)); + return d; + } + void setNumberValueInternal(double d) + { + quint64 intval; + memcpy(&intval, &d, sizeof(double)); + value.doubleValue = qToLittleEndian(intval); } + bool valueAsBoolean() const { if (type == Type_Boolean) @@ -357,16 +387,16 @@ struct Q_QML_PRIVATE_EXPORT Binding struct Parameter { - quint32 nameIndex; - quint32 type; - quint32 customTypeNameIndex; + LEUInt32 nameIndex; + LEUInt32 type; + LEUInt32 customTypeNameIndex; Location location; }; struct Signal { - quint32 nameIndex; - quint32 nParameters; + LEUInt32 nameIndex; + LEUInt32 nParameters; Location location; // Parameter parameters[1]; @@ -389,37 +419,41 @@ struct Signal struct Property { - enum Type { Var = 0, Variant, Int, Bool, Real, String, Url, Color, + enum Type : unsigned int { Var = 0, Variant, Int, Bool, Real, String, Url, Color, Font, Time, Date, DateTime, Rect, Point, Size, Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, Custom, CustomList }; - enum Flags { + enum Flags : unsigned int { IsReadOnly = 0x1 }; - quint32 nameIndex; - quint32 type : 31; - quint32 flags : 1; // readonly - quint32 customTypeNameIndex; // If type >= Custom + LEUInt32 nameIndex; + union { + QJsonPrivate::qle_bitfield<0, 31> type; + QJsonPrivate::qle_bitfield<31, 1> flags; // readonly + }; + LEUInt32 customTypeNameIndex; // If type >= Custom Location location; }; struct Alias { - enum Flags { + enum Flags : unsigned int { IsReadOnly = 0x1, Resolved = 0x2, AliasPointsToPointerObject = 0x4 }; - quint32 nameIndex : 29; - quint32 flags : 3; union { - quint32 idIndex; // string index - quint32 targetObjectId; // object id index (in QQmlContextData::idValues) + QJsonPrivate::qle_bitfield<0, 29> nameIndex; + QJsonPrivate::qle_bitfield<29, 3> flags; }; union { - quint32 propertyNameIndex; // string index - qint32 encodedMetaPropertyIndex; + LEUInt32 idIndex; // string index + LEUInt32 targetObjectId; // object id index (in QQmlContextData::idValues) + }; + union { + LEUInt32 propertyNameIndex; // string index + LEInt32 encodedMetaPropertyIndex; }; Location location; Location referenceLocation; @@ -432,7 +466,7 @@ struct Alias { struct Object { - enum Flags { + enum Flags : unsigned int { NoFlag = 0x0, IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary HasDeferredBindings = 0x2, // any of the bindings are deferred @@ -442,24 +476,26 @@ struct Object // Depending on the use, this may be the type name to instantiate before instantiating this // object. For grouped properties the type name will be empty and for attached properties // it will be the name of the attached type. - quint32 inheritedTypeNameIndex; - quint32 idNameIndex; - qint32 id : 16; - qint32 flags : 15; - quint32 defaultPropertyIsAlias : 1; - qint32 indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object - quint32 nFunctions; - quint32 offsetToFunctions; - quint32 nProperties; - quint32 offsetToProperties; - quint32 nAliases; - quint32 offsetToAliases; - quint32 nSignals; - quint32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects - quint32 nBindings; - quint32 offsetToBindings; - quint32 nNamedObjectsInComponent; - quint32 offsetToNamedObjectsInComponent; + LEUInt32 inheritedTypeNameIndex; + LEUInt32 idNameIndex; + union { + QJsonPrivate::qle_bitfield<0, 15> flags; + QJsonPrivate::qle_bitfield<15, 1> defaultPropertyIsAlias; + QJsonPrivate::qle_signedbitfield<16, 16> id; + }; + LEInt32 indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object + LEUInt32 nFunctions; + LEUInt32 offsetToFunctions; + LEUInt32 nProperties; + LEUInt32 offsetToProperties; + LEUInt32 nAliases; + LEUInt32 offsetToAliases; + LEUInt32 nSignals; + LEUInt32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects + LEUInt32 nBindings; + LEUInt32 offsetToBindings; + LEUInt32 nNamedObjectsInComponent; + LEUInt32 offsetToNamedObjectsInComponent; Location location; Location locationOfIdProperty; // Function[] @@ -480,9 +516,9 @@ struct Object ) & ~0x7; } - const quint32 *functionOffsetTable() const + const LEUInt32 *functionOffsetTable() const { - return reinterpret_cast(reinterpret_cast(this) + offsetToFunctions); + return reinterpret_cast(reinterpret_cast(this) + offsetToFunctions); } const Property *propertyTable() const @@ -502,14 +538,14 @@ struct Object const Signal *signalAt(int idx) const { - const uint *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToSignals); - const uint offset = offsetTable[idx]; + const LEUInt32 *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToSignals); + const LEUInt32 offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } - const quint32 *namedObjectsInComponentTable() const + const LEUInt32 *namedObjectsInComponentTable() const { - return reinterpret_cast(reinterpret_cast(this) + offsetToNamedObjectsInComponent); + return reinterpret_cast(reinterpret_cast(this) + offsetToNamedObjectsInComponent); } // --- QQmlPropertyCacheCreator interface @@ -535,22 +571,22 @@ struct Object struct Import { - enum ImportType { + enum ImportType : unsigned int { ImportLibrary = 0x1, ImportFile = 0x2, ImportScript = 0x3 }; - quint32 type; + quint8 type; - quint32 uriIndex; - quint32 qualifierIndex; + LEUInt32 uriIndex; + LEUInt32 qualifierIndex; - qint32 majorVersion; - qint32 minorVersion; + LEInt32 majorVersion; + LEInt32 minorVersion; Location location; - Import(): type(0), uriIndex(0), qualifierIndex(0), majorVersion(0), minorVersion(0) {} + Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; } }; static const char magic_str[] = "qv4cdata"; @@ -558,47 +594,47 @@ static const char magic_str[] = "qv4cdata"; struct Unit { char magic[8]; - qint16 architecture; - qint16 version; - quint32 unitSize; // Size of the Unit and any depending data. + LEInt16 architecture; + LEInt16 version; + LEUInt32 unitSize; // Size of the Unit and any depending data. - enum { + enum : unsigned int { IsJavascript = 0x1, IsQml = 0x2, StaticData = 0x4, // Unit data persistent in memory? IsSingleton = 0x8, IsSharedLibrary = 0x10 // .pragma shared? }; - quint32 flags; - uint stringTableSize; - uint offsetToStringTable; - uint functionTableSize; - uint offsetToFunctionTable; - uint lookupTableSize; - uint offsetToLookupTable; - uint regexpTableSize; - uint offsetToRegexpTable; - uint constantTableSize; - uint offsetToConstantTable; - uint jsClassTableSize; - uint offsetToJSClassTable; - qint32 indexOfRootFunction; - quint32 sourceFileIndex; + LEUInt32 flags; + LEUInt32 stringTableSize; + LEUInt32 offsetToStringTable; + LEUInt32 functionTableSize; + LEUInt32 offsetToFunctionTable; + LEUInt32 lookupTableSize; + LEUInt32 offsetToLookupTable; + LEUInt32 regexpTableSize; + LEUInt32 offsetToRegexpTable; + LEUInt32 constantTableSize; + LEUInt32 offsetToConstantTable; + LEUInt32 jsClassTableSize; + LEUInt32 offsetToJSClassTable; + LEInt32 indexOfRootFunction; + LEUInt32 sourceFileIndex; /* QML specific fields */ - quint32 nImports; - quint32 offsetToImports; - quint32 nObjects; - quint32 offsetToObjects; - quint32 indexOfRootObject; + LEUInt32 nImports; + LEUInt32 offsetToImports; + LEUInt32 nObjects; + LEUInt32 offsetToObjects; + LEUInt32 indexOfRootObject; const Import *importAt(int idx) const { return reinterpret_cast((reinterpret_cast(this)) + offsetToImports + idx * sizeof(Import)); } const Object *objectAt(int idx) const { - const uint *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToObjects); - const uint offset = offsetTable[idx]; + const LEUInt32 *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToObjects); + const LEUInt32 offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } @@ -608,22 +644,31 @@ struct Unit /* end QML specific fields*/ QString stringAt(int idx) const { - const uint *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToStringTable); - const uint offset = offsetTable[idx]; + const LEUInt32 *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToStringTable); + const LEUInt32 offset = offsetTable[idx]; const String *str = reinterpret_cast(reinterpret_cast(this) + offset); if (str->size == 0) return QString(); +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN const QChar *characters = reinterpret_cast(str + 1); if (flags & StaticData) return QString::fromRawData(characters, str->size); return QString(characters, str->size); +#else + const LEUInt16 *characters = reinterpret_cast(str + 1); + QString qstr(str->size, Qt::Uninitialized); + QChar *ch = qstr.data(); + for (int i = 0; i < str->size; ++i) + ch[i] = QChar(characters[i]); + return qstr; +#endif } - const uint *functionOffsetTable() const { return reinterpret_cast((reinterpret_cast(this)) + offsetToFunctionTable); } + const LEUInt32 *functionOffsetTable() const { return reinterpret_cast((reinterpret_cast(this)) + offsetToFunctionTable); } const Function *functionAt(int idx) const { - const uint *offsetTable = functionOffsetTable(); - const uint offset = offsetTable[idx]; + const LEUInt32 *offsetTable = functionOffsetTable(); + const LEUInt32 offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } @@ -636,8 +681,8 @@ struct Unit } const JSClassMember *jsClassAt(int idx, int *nMembers) const { - const uint *offsetTable = reinterpret_cast(reinterpret_cast(this) + offsetToJSClassTable); - const uint offset = offsetTable[idx]; + const LEUInt32 *offsetTable = reinterpret_cast(reinterpret_cast(this) + offsetToJSClassTable); + const LEUInt32 offset = offsetTable[idx]; const char *ptr = reinterpret_cast(this) + offset; const JSClass *klass = reinterpret_cast(ptr); *nMembers = klass->nMembers; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 55d2501024..8499f9b9aa 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -83,7 +83,13 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) QV4::CompiledData::String *s = (QV4::CompiledData::String*)(stringData); s->size = qstr.length(); +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN memcpy(s + 1, qstr.constData(), qstr.length()*sizeof(ushort)); +#else + ushort *uc = reinterpret_cast(s + 1); + for (int i = 0; i < qstr.length(); ++i) + uc[i] = qToLittleEndian(qstr.at(i).unicode()); +#endif stringData += QV4::CompiledData::String::calculateSize(qstr); } diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index f314e20863..caabee322a 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -59,7 +59,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, Q_UNUSED(engine); internalClass = engine->emptyClass; - const quint32 *formalsIndices = compiledFunction->formalsTable(); + const CompiledData::LEUInt32 *formalsIndices = compiledFunction->formalsTable(); // iterate backwards, so we get the right ordering for duplicate names Scope scope(engine); ScopedString arg(scope); @@ -78,7 +78,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, } nFormals = compiledFunction->nFormals; - const quint32 *localsIndices = compiledFunction->localsTable(); + const CompiledData::LEUInt32 *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); @@ -110,7 +110,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QListlocalsTable(); + const CompiledData::LEUInt32 *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 7fb66a49bf..5d96240e5b 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -312,7 +312,7 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct QV4::Scoped context(scope, engine->qmlContext()); QQmlContextData *qmlContext = context->qmlContext(); - const quint32 *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); + const QV4::CompiledData::LEUInt32 *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) { Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount); @@ -321,7 +321,7 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct } Q_ASSERT(qmlContext->contextObject); - const quint32 *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable(); + const QV4::CompiledData::LEUInt32 *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable(); const int contextPropertyDependencyCount = compiledFunction->nDependingContextProperties; for (int i = 0; i < contextPropertyDependencyCount; ++i) { const int propertyIndex = *contextPropertyDependency++; @@ -331,7 +331,7 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct } QObject *scopeObject = context->qmlScope(); - const quint32 *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); + const QV4::CompiledData::LEUInt32 *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties; for (int i = 0; i < scopePropertyDependencyCount; ++i) { const int propertyIndex = *scopePropertyDependency++; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 94e81cdcc7..3d2c143295 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -999,7 +999,7 @@ void QQmlObjectCreator::setupFunctions() QV4::ScopedValue function(scope); QV4::ScopedContext qmlContext(scope, currentQmlContext()); - const quint32 *functionIdx = _compiledObject->functionOffsetTable(); + const QV4::CompiledData::LEUInt32 *functionIdx = _compiledObject->functionOffsetTable(); for (quint32 i = 0; i < _compiledObject->nFunctions; ++i, ++functionIdx) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[*functionIdx]; const QString name = runtimeFunction->name()->toQString(); diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index a9aec4cded..0ce43e9383 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -630,7 +630,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * id -= propOffset(); if (id < propertyCount) { - const QV4::CompiledData::Property::Type t = static_cast(compiledObject->propertyTable()[id].type); + const QV4::CompiledData::Property::Type t = static_cast(qint32(compiledObject->propertyTable()[id].type)); bool needActivate = false; if (t == QV4::CompiledData::Property::Var) { diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index edab49e4be..186a9e7cf7 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -133,14 +133,14 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); - QCOMPARE(testUnit->nObjects, quint32(1)); + QCOMPARE(quint32(testUnit->nObjects), quint32(1)); const QV4::CompiledData::Object *obj = testUnit->objectAt(0); - QCOMPARE(obj->nBindings, quint32(1)); - QCOMPARE(obj->bindingTable()->type, quint32(QV4::CompiledData::Binding::Type_Script)); - QCOMPARE(obj->bindingTable()->value.compiledScriptIndex, quint32(1)); + QCOMPARE(quint32(obj->nBindings), quint32(1)); + QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Script)); + QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(1)); - QCOMPARE(testUnit->functionTableSize, quint32(2)); + QCOMPARE(quint32(testUnit->functionTableSize), quint32(2)); const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1); QVERIFY(bindingFunction->codeOffset > testUnit->unitSize); @@ -163,14 +163,14 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); - QCOMPARE(testUnit->nObjects, quint32(1)); + QCOMPARE(quint32(testUnit->nObjects), quint32(1)); const QV4::CompiledData::Object *obj = testUnit->objectAt(0); - QCOMPARE(obj->nBindings, quint32(2)); - QCOMPARE(obj->bindingTable()->type, quint32(QV4::CompiledData::Binding::Type_Number)); - QCOMPARE(obj->bindingTable()->value.d, double(42)); + QCOMPARE(quint32(obj->nBindings), quint32(2)); + QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number)); + QCOMPARE(obj->bindingTable()->valueAsNumber(), double(42)); - QCOMPARE(testUnit->functionTableSize, quint32(2)); + QCOMPARE(quint32(testUnit->functionTableSize), quint32(2)); const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1); QVERIFY(bindingFunction->codeOffset > testUnit->unitSize); diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index f26d638082..1fc803a395 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -95,7 +95,7 @@ void tst_qqmltranslation::translation() if (expectCompiledTranslation) { if (binding->type != QV4::CompiledData::Binding::Type_Translation) qDebug() << "binding for property" << propertyName << "is not a compiled translation"; - QCOMPARE(binding->type, quint32(QV4::CompiledData::Binding::Type_Translation)); + QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_Translation)); } else { if (binding->type == QV4::CompiledData::Binding::Type_Translation) qDebug() << "binding for property" << propertyName << "is not supposed to be a compiled translation"; @@ -147,7 +147,7 @@ void tst_qqmltranslation::idTranslation() if (propertyName == "idTranslation") { if (binding->type != QV4::CompiledData::Binding::Type_TranslationById) qDebug() << "binding for property" << propertyName << "is not a compiled translation"; - QCOMPARE(binding->type, quint32(QV4::CompiledData::Binding::Type_TranslationById)); + QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_TranslationById)); } else { QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation); } -- cgit v1.2.3 From cc7cf5f5705553b625c1b28b49c41587b587bb38 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Jul 2016 15:11:03 +0200 Subject: Revert "QML: When available, use QQmlAccessors to read properties." This reverts commit f6fee09942de7901a708c4e16db0c7c82550e8c5. The accessor pointers were embedded in the generated machine/byte code, which makes it non-relocatable. As discussed, for the moment the ability to have relocatable code is prioritized. But the goal is to re-enable accessor accelerated property access through lookups. Change-Id: I18ec9ce31901c1fae3e58ac0c41bc87791e8c380 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4instr_moth_p.h | 90 ----------------- src/qml/compiler/qv4isel_moth.cpp | 104 +------------------ src/qml/compiler/qv4isel_moth_p.h | 4 +- src/qml/compiler/qv4isel_p.cpp | 7 +- src/qml/compiler/qv4isel_p.h | 5 +- src/qml/compiler/qv4jsir.cpp | 12 +-- src/qml/jit/qv4isel_masm.cpp | 79 +-------------- src/qml/jit/qv4isel_masm_p.h | 4 +- src/qml/jit/qv4regalloc.cpp | 4 +- src/qml/jsruntime/qv4qobjectwrapper_p.h | 1 - src/qml/jsruntime/qv4runtime.cpp | 172 -------------------------------- src/qml/jsruntime/qv4runtime_p.h | 5 - src/qml/jsruntime/qv4runtimeapi_p.h | 21 ---- src/qml/jsruntime/qv4vme_moth.cpp | 40 -------- src/qml/qml/qqmlobjectcreator.cpp | 1 + 15 files changed, 22 insertions(+), 527 deletions(-) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 93043135a4..93a7170e68 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -81,19 +81,9 @@ QT_BEGIN_NAMESPACE F(SetLookup, setLookup) \ F(StoreQObjectProperty, storeQObjectProperty) \ F(LoadQObjectProperty, loadQObjectProperty) \ - F(LoadQRealQObjectPropertyDirectly, loadQRealQObjectPropertyDirectly) \ - F(LoadQObjectQObjectPropertyDirectly, loadQObjectQObjectPropertyDirectly) \ - F(LoadIntQObjectPropertyDirectly, loadIntQObjectPropertyDirectly) \ - F(LoadBoolQObjectPropertyDirectly, loadBoolQObjectPropertyDirectly) \ - F(LoadQStringQObjectPropertyDirectly, loadQStringQObjectPropertyDirectly) \ F(StoreScopeObjectProperty, storeScopeObjectProperty) \ F(StoreContextObjectProperty, storeContextObjectProperty) \ F(LoadScopeObjectProperty, loadScopeObjectProperty) \ - F(LoadScopeObjectQRealPropertyDirectly, loadScopeObjectQRealPropertyDirectly) \ - F(LoadScopeObjectQObjectPropertyDirectly, loadScopeObjectQObjectPropertyDirectly) \ - F(LoadScopeObjectIntPropertyDirectly, loadScopeObjectIntPropertyDirectly) \ - F(LoadScopeObjectBoolPropertyDirectly, loadScopeObjectBoolPropertyDirectly) \ - F(LoadScopeObjectQStringPropertyDirectly, loadScopeObjectQStringPropertyDirectly) \ F(LoadContextObjectProperty, loadContextObjectProperty) \ F(LoadIdObject, loadIdObject) \ F(LoadAttachedQObjectProperty, loadAttachedQObjectProperty) \ @@ -333,36 +323,6 @@ union Instr Param base; Param result; }; - struct instr_loadScopeObjectQRealPropertyDirectly { - MOTH_INSTR_HEADER - Param base; - Param result; - QQmlAccessors *accessors; - }; - struct instr_loadScopeObjectQObjectPropertyDirectly { - MOTH_INSTR_HEADER - Param base; - Param result; - QQmlAccessors *accessors; - }; - struct instr_loadScopeObjectIntPropertyDirectly { - MOTH_INSTR_HEADER - Param base; - Param result; - QQmlAccessors *accessors; - }; - struct instr_loadScopeObjectBoolPropertyDirectly { - MOTH_INSTR_HEADER - Param base; - Param result; - QQmlAccessors *accessors; - }; - struct instr_loadScopeObjectQStringPropertyDirectly { - MOTH_INSTR_HEADER - Param base; - Param result; - QQmlAccessors *accessors; - }; struct instr_loadContextObjectProperty { MOTH_INSTR_HEADER int propertyIndex; @@ -382,46 +342,6 @@ union Instr Param result; bool captureRequired; }; - struct instr_loadQRealQObjectPropertyDirectly { - MOTH_INSTR_HEADER - Param base; - Param result; - QQmlAccessors *accessors; - int coreIndex; - int notifyIndex; - }; - struct instr_loadQObjectQObjectPropertyDirectly { - MOTH_INSTR_HEADER - Param base; - Param result; - QQmlAccessors *accessors; - int coreIndex; - int notifyIndex; - }; - struct instr_loadIntQObjectPropertyDirectly { - MOTH_INSTR_HEADER - Param base; - Param result; - QQmlAccessors *accessors; - int coreIndex; - int notifyIndex; - }; - struct instr_loadBoolQObjectPropertyDirectly { - MOTH_INSTR_HEADER - Param base; - Param result; - QQmlAccessors *accessors; - int coreIndex; - int notifyIndex; - }; - struct instr_loadQStringQObjectPropertyDirectly { - MOTH_INSTR_HEADER - Param base; - Param result; - QQmlAccessors *accessors; - int coreIndex; - int notifyIndex; - }; struct instr_loadAttachedQObjectProperty { MOTH_INSTR_HEADER int propertyIndex; @@ -880,19 +800,9 @@ union Instr instr_loadProperty loadProperty; instr_getLookup getLookup; instr_loadScopeObjectProperty loadScopeObjectProperty; - instr_loadScopeObjectQRealPropertyDirectly loadScopeObjectQRealPropertyDirectly; - instr_loadScopeObjectQObjectPropertyDirectly loadScopeObjectQObjectPropertyDirectly; - instr_loadScopeObjectIntPropertyDirectly loadScopeObjectIntPropertyDirectly; - instr_loadScopeObjectBoolPropertyDirectly loadScopeObjectBoolPropertyDirectly; - instr_loadScopeObjectQStringPropertyDirectly loadScopeObjectQStringPropertyDirectly; instr_loadContextObjectProperty loadContextObjectProperty; instr_loadIdObject loadIdObject; instr_loadQObjectProperty loadQObjectProperty; - instr_loadQRealQObjectPropertyDirectly loadQRealQObjectPropertyDirectly; - instr_loadQObjectQObjectPropertyDirectly loadQObjectQObjectPropertyDirectly; - instr_loadIntQObjectPropertyDirectly loadIntQObjectPropertyDirectly; - instr_loadBoolQObjectPropertyDirectly loadBoolQObjectPropertyDirectly; - instr_loadQStringQObjectPropertyDirectly loadQStringQObjectPropertyDirectly; instr_loadAttachedQObjectProperty loadAttachedQObjectProperty; instr_storeProperty storeProperty; instr_setLookup setLookup; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 967dca13a1..8814ad8a78 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -46,8 +46,6 @@ #include #include #include -#include "qml/qqmlaccessors_p.h" -#include "qml/qqmlpropertycache_p.h" #undef USE_TYPE_INFO @@ -739,51 +737,8 @@ void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *target addInstruction(store); } -void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, - QQmlPropertyData *property, int index, - IR::Expr *target) -{ - if (property && property->hasAccessors() && property->isFullyResolved()) { - if (kind == IR::Member::MemberOfQmlScopeObject) { - if (property->propType == QMetaType::QReal) { - Instruction::LoadScopeObjectQRealPropertyDirectly load; - load.base = getParam(source); - load.accessors = property->accessors; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->isQObject()) { - Instruction::LoadScopeObjectQObjectPropertyDirectly load; - load.base = getParam(source); - load.accessors = property->accessors; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::Int) { - Instruction::LoadScopeObjectIntPropertyDirectly load; - load.base = getParam(source); - load.accessors = property->accessors; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::Bool) { - Instruction::LoadScopeObjectBoolPropertyDirectly load; - load.base = getParam(source); - load.accessors = property->accessors; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::QString) { - Instruction::LoadScopeObjectQStringPropertyDirectly load; - load.base = getParam(source); - load.accessors = property->accessors; - load.result = getResultParam(target); - addInstruction(load); - return; - } - } - } - +void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target) +{ if (kind == IR::Member::MemberOfQmlScopeObject) { Instruction::LoadScopeObjectProperty load; load.base = getParam(source); @@ -807,59 +762,8 @@ void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::M } } -void InstructionSelection::getQObjectProperty(IR::Expr *base, QQmlPropertyData *property, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) -{ - if (property && property->hasAccessors() && property->isFullyResolved()) { - if (!attachedPropertiesId && !isSingletonProperty) { - if (property->propType == QMetaType::QReal) { - Instruction::LoadQRealQObjectPropertyDirectly load; - load.base = getParam(base); - load.accessors = property->accessors; - load.coreIndex = property->coreIndex; - load.notifyIndex = captureRequired ? property->notifyIndex : -1; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->isQObject()) { - Instruction::LoadQObjectQObjectPropertyDirectly load; - load.base = getParam(base); - load.accessors = property->accessors; - load.coreIndex = property->coreIndex; - load.notifyIndex = captureRequired ? property->notifyIndex : -1; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::Int) { - Instruction::LoadIntQObjectPropertyDirectly load; - load.base = getParam(base); - load.accessors = property->accessors; - load.coreIndex = property->coreIndex; - load.notifyIndex = captureRequired ? property->notifyIndex : -1; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::Bool) { - Instruction::LoadBoolQObjectPropertyDirectly load; - load.base = getParam(base); - load.accessors = property->accessors; - load.coreIndex = property->coreIndex; - load.notifyIndex = captureRequired ? property->notifyIndex : -1; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::QString) { - Instruction::LoadQStringQObjectPropertyDirectly load; - load.base = getParam(base); - load.accessors = property->accessors; - load.coreIndex = property->coreIndex; - load.notifyIndex = captureRequired ? property->notifyIndex : -1; - load.result = getResultParam(target); - addInstruction(load); - return; - } - } - } - const int propertyIndex = property->coreIndex; +void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) +{ if (attachedPropertiesId != 0) { Instruction::LoadAttachedQObjectProperty load; load.propertyIndex = propertyIndex; diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index bf3909682d..29d117af38 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -134,8 +134,8 @@ protected: virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName); virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex); virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex); - virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, QQmlPropertyData *property, int index, IR::Expr *target); - virtual void getQObjectProperty(IR::Expr *base, QQmlPropertyData *property, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target); + virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target); + virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target); virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target); virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex); virtual void copyValue(IR::Expr *source, IR::Expr *target); diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 6ba23a0951..0ae08160ab 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -156,15 +156,14 @@ void IRDecoder::visitMove(IR::Move *s) } } if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { - getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property, - m->property->coreIndex, s->target); + getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex, s->target); return; } - getQObjectProperty(m->base, m->property, captureRequired, isSingletonProperty, attachedPropertiesId, s->target); + getQObjectProperty(m->base, m->property->coreIndex, captureRequired, isSingletonProperty, attachedPropertiesId, s->target); #endif // V4_BOOTSTRAP return; } else if (m->kind == IR::Member::MemberOfIdObjectsArray) { - getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, nullptr, m->idIndex, s->target); + getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->idIndex, s->target); return; } else if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) { getProperty(m->base, *m->name, s->target); diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index 61fd11c435..ecafafcea1 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -61,7 +61,6 @@ QT_BEGIN_NAMESPACE -class QQmlAccessors; class QQmlEnginePrivate; namespace QV4 { @@ -183,8 +182,8 @@ public: // to implement by subclasses: virtual void setActivationProperty(IR::Expr *source, const QString &targetName) = 0; virtual void initClosure(IR::Closure *closure, IR::Expr *target) = 0; virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) = 0; - virtual void getQObjectProperty(IR::Expr *base, QQmlPropertyData *property, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) = 0; - virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, QQmlPropertyData *property, int index, IR::Expr *target) = 0; + virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) = 0; + virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target) = 0; virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) = 0; virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) = 0; virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) = 0; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 583311d1ae..b6c5226894 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -910,16 +910,12 @@ void IRPrinter::visitMember(Member *e) visit(e->base); *out << '.' << *e->name; #ifndef V4_BOOTSTRAP - if (e->property) { + if (e->property) *out << " (meta-property " << e->property->coreIndex - << " <" << QMetaType::typeName(e->property->propType) << ">"; - if (e->property->hasAccessors() && e->property->isFullyResolved()) { - *out << ", accessible"; - } - *out << ")"; - } else if (e->kind == Member::MemberOfIdObjectsArray) { + << " <" << QMetaType::typeName(e->property->propType) + << ">)"; + else if (e->kind == Member::MemberOfIdObjectsArray) *out << "(id object " << e->idIndex << ")"; - } #endif } diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index d3c624ff60..a45d74bb4c 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -49,7 +49,6 @@ #include "qv4assembler_p.h" #include "qv4unop_p.h" #include "qv4binop_p.h" -#include #include #include @@ -754,39 +753,8 @@ void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR:: } } -void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, QQmlPropertyData *property, int index, IR::Expr *target) +void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, IR::Expr *target) { - if (property && property->hasAccessors() && property->isFullyResolved()) { - if (kind == IR::Member::MemberOfQmlScopeObject) { - if (property->propType == QMetaType::QReal) { - generateRuntimeCall(target, accessQmlScopeObjectQRealProperty, - Assembler::PointerToValue(base), - Assembler::TrustedImmPtr(property->accessors)); - return; - } else if (property->isQObject()) { - generateRuntimeCall(target, accessQmlScopeObjectQObjectProperty, - Assembler::PointerToValue(base), - Assembler::TrustedImmPtr(property->accessors)); - return; - } else if (property->propType == QMetaType::Int) { - generateRuntimeCall(target, accessQmlScopeObjectIntProperty, - Assembler::PointerToValue(base), - Assembler::TrustedImmPtr(property->accessors)); - return; - } else if (property->propType == QMetaType::Bool) { - generateRuntimeCall(target, accessQmlScopeObjectBoolProperty, - Assembler::PointerToValue(base), - Assembler::TrustedImmPtr(property->accessors)); - return; - } else if (property->propType == QMetaType::QString) { - generateRuntimeCall(target, accessQmlScopeObjectQStringProperty, - Assembler::PointerToValue(base), - Assembler::TrustedImmPtr(property->accessors)); - return; - } - } - } - if (kind == IR::Member::MemberOfQmlScopeObject) generateRuntimeCall(target, getQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); else if (kind == IR::Member::MemberOfQmlContextObject) @@ -797,51 +765,8 @@ void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::Mem Q_ASSERT(false); } -void InstructionSelection::getQObjectProperty(IR::Expr *base, QQmlPropertyData *property, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) +void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) { - if (property && property->hasAccessors() && property->isFullyResolved()) { - if (!attachedPropertiesId && !isSingleton) { - const int notifyIndex = captureRequired ? property->notifyIndex : -1; - if (property->propType == QMetaType::QReal) { - generateRuntimeCall(target, accessQObjectQRealProperty, - Assembler::EngineRegister, Assembler::PointerToValue(base), - Assembler::TrustedImmPtr(property->accessors), - Assembler::TrustedImm32(property->coreIndex), - Assembler::TrustedImm32(notifyIndex)); - return; - } else if (property->isQObject()) { - generateRuntimeCall(target, accessQObjectQObjectProperty, - Assembler::EngineRegister, Assembler::PointerToValue(base), - Assembler::TrustedImmPtr(property->accessors), - Assembler::TrustedImm32(property->coreIndex), - Assembler::TrustedImm32(notifyIndex)); - return; - } else if (property->propType == QMetaType::Int) { - generateRuntimeCall(target, accessQObjectIntProperty, - Assembler::EngineRegister, Assembler::PointerToValue(base), - Assembler::TrustedImmPtr(property->accessors), - Assembler::TrustedImm32(property->coreIndex), - Assembler::TrustedImm32(notifyIndex)); - return; - } else if (property->propType == QMetaType::Bool) { - generateRuntimeCall(target, accessQObjectBoolProperty, - Assembler::EngineRegister, Assembler::PointerToValue(base), - Assembler::TrustedImmPtr(property->accessors), - Assembler::TrustedImm32(property->coreIndex), - Assembler::TrustedImm32(notifyIndex)); - return; - } else if (property->propType == QMetaType::QString) { - generateRuntimeCall(target, accessQObjectQStringProperty, - Assembler::EngineRegister, Assembler::PointerToValue(base), - Assembler::TrustedImmPtr(property->accessors), - Assembler::TrustedImm32(property->coreIndex), - Assembler::TrustedImm32(notifyIndex)); - return; - } - } - } - - const int propertyIndex = property->coreIndex; if (attachedPropertiesId != 0) generateRuntimeCall(target, getQmlAttachedProperty, Assembler::EngineRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex)); else if (isSingleton) diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index db9d440e83..a92196f5f7 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -124,8 +124,8 @@ protected: virtual void setActivationProperty(IR::Expr *source, const QString &targetName); virtual void initClosure(IR::Closure *closure, IR::Expr *target); virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target); - virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, QQmlPropertyData *property, int index, IR::Expr *target); - virtual void getQObjectProperty(IR::Expr *base, QQmlPropertyData *property, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target); + virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target); + virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target); virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName); virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex); virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex); diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index 04b8b83b4a..deef719b67 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -528,14 +528,14 @@ protected: // IRDecoder addCall(); } - virtual void getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, QQmlPropertyData * /*property*/, int /*index*/, IR::Expr *target) + virtual void getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int /*index*/, IR::Expr *target) { addDef(target); addUses(base->asTemp(), Use::CouldHaveRegister); addCall(); } - virtual void getQObjectProperty(IR::Expr *base, QQmlPropertyData * /*property*/, bool /*captureRequired*/, bool /*isSingleton*/, int /*attachedPropertiesId*/, IR::Expr *target) + virtual void getQObjectProperty(IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/, bool /*isSingleton*/, int /*attachedPropertiesId*/, IR::Expr *target) { addDef(target); addUses(base->asTemp(), Use::CouldHaveRegister); diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index dab59886de..8ab5c96c3a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -59,7 +59,6 @@ #include #include #include -#include #include #include diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 66ea6b33d3..a69874cacb 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1443,116 +1443,6 @@ ReturnedValue Runtime::method_getQmlQObjectProperty(ExecutionEngine *engine, con return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->object(), propertyIndex, captureRequired); } -template -static inline PropertyType getQObjectProperty(QObject *object, ExecutionEngine *engine, QQmlAccessors *accessors, int coreIndex, int notifyIndex) -{ - PropertyType t; - accessors->read(object, &t); - if (notifyIndex != -1) { - QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0; - if (ep && ep->propertyCapture) { - if (accessors->notifier) { - QQmlNotifier *n = nullptr; - accessors->notifier(object, &n); - if (n) { - ep->propertyCapture->captureProperty(n); - } - } else { - ep->propertyCapture->captureProperty(object, coreIndex, notifyIndex); - } - } - } - - return t; -} - -ReturnedValue Runtime::method_accessQObjectQRealProperty(ExecutionEngine *engine, - const Value &object, - QQmlAccessors *accessors, int coreIndex, - int notifyIndex) -{ - auto casted = object.as(); - QObject *o = casted ? casted->object() : nullptr; - if (Q_LIKELY(o)) { - return QV4::Encode(getQObjectProperty(o, engine, accessors, coreIndex, notifyIndex)); - } - engine->throwTypeError(QStringLiteral("Cannot read property of null")); - return Encode::undefined(); -} - -ReturnedValue Runtime::method_accessQObjectQObjectProperty(ExecutionEngine *engine, - const Value &object, - QQmlAccessors *accessors, int coreIndex, - int notifyIndex) -{ - auto casted = object.as(); - QObject *o = casted ? casted->object() : nullptr; - if (Q_LIKELY(o)) { - return QV4::QObjectWrapper::wrap(engine, getQObjectProperty(o, engine, accessors, - coreIndex, - notifyIndex)); - } - - engine->throwTypeError(QStringLiteral("Cannot read property of null")); - return Encode::undefined(); -} - -ReturnedValue Runtime::method_accessQObjectIntProperty(ExecutionEngine *engine, const Value &object, - QQmlAccessors *accessors, int coreIndex, - int notifyIndex) -{ - auto casted = object.as(); - QObject *o = casted ? casted->object() : nullptr; - if (Q_LIKELY(o)) { - return QV4::Encode(getQObjectProperty(o, engine, accessors, coreIndex, notifyIndex)); - } - engine->throwTypeError(QStringLiteral("Cannot read property of null")); - return Encode::undefined(); -} - -ReturnedValue Runtime::method_accessQObjectBoolProperty(ExecutionEngine *engine, const Value &object, - QQmlAccessors *accessors, int coreIndex, - int notifyIndex) -{ - auto casted = object.as(); - QObject *o = casted ? casted->object() : nullptr; - if (Q_LIKELY(o)) { - return QV4::Encode(getQObjectProperty(o, engine, accessors, coreIndex, notifyIndex)); - } - engine->throwTypeError(QStringLiteral("Cannot read property of null")); - return Encode::undefined(); -} - -ReturnedValue Runtime::method_accessQObjectQStringProperty(ExecutionEngine *engine, - const Value &object, - QQmlAccessors *accessors, int coreIndex, - int notifyIndex) -{ - auto casted = object.as(); - QObject *o = casted ? casted->object() : nullptr; - if (Q_LIKELY(o)) { - return QV4::Encode(engine->newString(getQObjectProperty(o, engine, accessors, - coreIndex, notifyIndex))); - } - engine->throwTypeError(QStringLiteral("Cannot read property of null")); - return Encode::undefined(); -} - -ReturnedValue Runtime::method_accessQmlScopeObjectQObjectProperty(const Value &context, - QQmlAccessors *accessors) -{ -#ifndef V4_BOOTSTRAP - const QmlContext &c = static_cast(context); - QObject *rv = 0; - accessors->read(c.d()->qml->scopeObject, &rv); - return QV4::QObjectWrapper::wrap(c.engine(), rv); -#else - Q_UNUSED(context); - Q_UNUSED(accessors); - return QV4::Encode::undefined(); -#endif -} - QV4::ReturnedValue Runtime::method_getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex) { QObject *scopeObject = engine->qmlScopeObject(); @@ -1934,68 +1824,6 @@ Bool Runtime::method_toBoolean(const Value &value) return value.toBoolean(); } -ReturnedValue Runtime::method_accessQmlScopeObjectQRealProperty(const Value &context, - QQmlAccessors *accessors) -{ -#ifndef V4_BOOTSTRAP - const QmlContext &c = static_cast(context); - qreal rv = 0; - accessors->read(c.d()->qml->scopeObject, &rv); - return QV4::Encode(rv); -#else - Q_UNUSED(context); - Q_UNUSED(accessors); - return QV4::Encode::undefined(); -#endif -} - -ReturnedValue Runtime::method_accessQmlScopeObjectIntProperty(const Value &context, - QQmlAccessors *accessors) -{ -#ifndef V4_BOOTSTRAP - const QmlContext &c = static_cast(context); - int rv = 0; - accessors->read(c.d()->qml->scopeObject, &rv); - return QV4::Encode(rv); -#else - Q_UNUSED(context); - Q_UNUSED(accessors); - return QV4::Encode::undefined(); -#endif -} - -ReturnedValue Runtime::method_accessQmlScopeObjectBoolProperty(const Value &context, - QQmlAccessors *accessors) -{ -#ifndef V4_BOOTSTRAP - const QmlContext &c = static_cast(context); - bool rv = false; - accessors->read(c.d()->qml->scopeObject, &rv); - return QV4::Encode(rv); -#else - Q_UNUSED(context); - Q_UNUSED(accessors); - return QV4::Encode::undefined(); -#endif -} - -ReturnedValue Runtime::method_accessQmlScopeObjectQStringProperty(ExecutionEngine *engine, - const Value &context, - QQmlAccessors *accessors) -{ -#ifndef V4_BOOTSTRAP - const QmlContext &c = static_cast(context); - QString rv; - accessors->read(c.d()->qml->scopeObject, &rv); - return QV4::Encode(engine->newString(rv)); -#else - Q_UNUSED(engine); - Q_UNUSED(context); - Q_UNUSED(accessors); - return QV4::Encode::undefined(); -#endif -} - #endif // V4_BOOTSTRAP } // namespace QV4 diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 975d100ef4..88c09ec5f9 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -56,11 +56,6 @@ #include "qv4engine_p.h" #include "qv4math_p.h" #include "qv4runtimeapi_p.h" -#ifndef V4_BOOTSTRAP -#include -#include -#endif - #include QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index ded2b541dc..cbc7a2ddc1 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -167,16 +167,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime { , INIT_RUNTIME_METHOD(setQmlScopeObjectProperty) , INIT_RUNTIME_METHOD(setQmlContextObjectProperty) , INIT_RUNTIME_METHOD(setQmlQObjectProperty) - , INIT_RUNTIME_METHOD(accessQObjectQRealProperty) - , INIT_RUNTIME_METHOD(accessQObjectQObjectProperty) - , INIT_RUNTIME_METHOD(accessQObjectIntProperty) - , INIT_RUNTIME_METHOD(accessQObjectBoolProperty) - , INIT_RUNTIME_METHOD(accessQObjectQStringProperty) - , INIT_RUNTIME_METHOD(accessQmlScopeObjectQRealProperty) - , INIT_RUNTIME_METHOD(accessQmlScopeObjectQObjectProperty) - , INIT_RUNTIME_METHOD(accessQmlScopeObjectIntProperty) - , INIT_RUNTIME_METHOD(accessQmlScopeObjectBoolProperty) - , INIT_RUNTIME_METHOD(accessQmlScopeObjectQStringProperty) { } // call @@ -313,17 +303,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime { RUNTIME_METHOD(void, setQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)); RUNTIME_METHOD(void, setQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)); RUNTIME_METHOD(void, setQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value)); - - RUNTIME_METHOD(ReturnedValue, accessQObjectQRealProperty, (ExecutionEngine *engine, const Value &object, QQmlAccessors *accessors, int coreIndex, int notifyIndex)); - RUNTIME_METHOD(ReturnedValue, accessQObjectQObjectProperty, (ExecutionEngine *engine, const Value &object, QQmlAccessors *accessors, int coreIndex, int notifyIndex)); - RUNTIME_METHOD(ReturnedValue, accessQObjectIntProperty, (ExecutionEngine *engine, const Value &object, QQmlAccessors *accessors, int coreIndex, int notifyIndex)); - RUNTIME_METHOD(ReturnedValue, accessQObjectBoolProperty, (ExecutionEngine *engine, const Value &object, QQmlAccessors *accessors, int coreIndex, int notifyIndex)); - RUNTIME_METHOD(ReturnedValue, accessQObjectQStringProperty, (ExecutionEngine *engine, const Value &object, QQmlAccessors *accessors, int coreIndex, int notifyIndex)); - RUNTIME_METHOD(ReturnedValue, accessQmlScopeObjectQRealProperty, (const Value &context, QQmlAccessors *accessors)); - RUNTIME_METHOD(ReturnedValue, accessQmlScopeObjectQObjectProperty, (const Value &context, QQmlAccessors *accessors)); - RUNTIME_METHOD(ReturnedValue, accessQmlScopeObjectIntProperty, (const Value &context, QQmlAccessors *accessors)); - RUNTIME_METHOD(ReturnedValue, accessQmlScopeObjectBoolProperty, (const Value &context, QQmlAccessors *accessors)); - RUNTIME_METHOD(ReturnedValue, accessQmlScopeObjectQStringProperty, (ExecutionEngine *engine, const Value &context, QQmlAccessors *accessors)); }; #undef RUNTIME_METHOD diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 99aa0ef652..b83bae8a38 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -519,26 +519,6 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code STOREVALUE(instr.result, engine->runtime.getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); MOTH_END_INSTR(LoadQObjectProperty) - MOTH_BEGIN_INSTR(LoadQRealQObjectPropertyDirectly) - STOREVALUE(instr.result, engine->runtime.accessQObjectQRealProperty(engine, VALUE(instr.base), instr.accessors, instr.coreIndex, instr.notifyIndex)); - MOTH_END_INSTR(LoadQRealQObjectPropertyDirectly) - - MOTH_BEGIN_INSTR(LoadQObjectQObjectPropertyDirectly) - STOREVALUE(instr.result, engine->runtime.accessQObjectQObjectProperty(engine, VALUE(instr.base), instr.accessors, instr.coreIndex, instr.notifyIndex)); - MOTH_END_INSTR(LoadQObjectQObjectPropertyDirectly) - - MOTH_BEGIN_INSTR(LoadIntQObjectPropertyDirectly) - STOREVALUE(instr.result, engine->runtime.accessQObjectIntProperty(engine, VALUE(instr.base), instr.accessors, instr.coreIndex, instr.notifyIndex)); - MOTH_END_INSTR(LoadIntQObjectPropertyDirectly) - - MOTH_BEGIN_INSTR(LoadBoolQObjectPropertyDirectly) - STOREVALUE(instr.result, engine->runtime.accessQObjectBoolProperty(engine, VALUE(instr.base), instr.accessors, instr.coreIndex, instr.notifyIndex)); - MOTH_END_INSTR(LoadQRealQObjectPropertyDirectly) - - MOTH_BEGIN_INSTR(LoadQStringQObjectPropertyDirectly) - STOREVALUE(instr.result, engine->runtime.accessQObjectQStringProperty(engine, VALUE(instr.base), instr.accessors, instr.coreIndex, instr.notifyIndex)); - MOTH_END_INSTR(LoadQStringQObjectPropertyDirectly) - MOTH_BEGIN_INSTR(StoreScopeObjectProperty) engine->runtime.setQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); CHECK_EXCEPTION; @@ -548,26 +528,6 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code STOREVALUE(instr.result, engine->runtime.getQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex)); MOTH_END_INSTR(LoadScopeObjectProperty) - MOTH_BEGIN_INSTR(LoadScopeObjectQRealPropertyDirectly) - STOREVALUE(instr.result, engine->runtime.accessQmlScopeObjectQRealProperty(VALUE(instr.base), instr.accessors)); - MOTH_END_INSTR(LoadScopeObjectQRealPropertyDirectly) - - MOTH_BEGIN_INSTR(LoadScopeObjectQObjectPropertyDirectly) - STOREVALUE(instr.result, engine->runtime.accessQmlScopeObjectQObjectProperty(VALUE(instr.base), instr.accessors)); - MOTH_END_INSTR(LoadScopeObjectQObjectPropertyDirectly) - - MOTH_BEGIN_INSTR(LoadScopeObjectIntPropertyDirectly) - STOREVALUE(instr.result, engine->runtime.accessQmlScopeObjectIntProperty(VALUE(instr.base), instr.accessors)); - MOTH_END_INSTR(LoadScopeObjectIntPropertyDirectly) - - MOTH_BEGIN_INSTR(LoadScopeObjectBoolPropertyDirectly) - STOREVALUE(instr.result, engine->runtime.accessQmlScopeObjectBoolProperty(VALUE(instr.base), instr.accessors)); - MOTH_END_INSTR(LoadScopeObjectBoolPropertyDirectly) - - MOTH_BEGIN_INSTR(LoadScopeObjectQStringPropertyDirectly) - STOREVALUE(instr.result, engine->runtime.accessQmlScopeObjectQStringProperty(engine, VALUE(instr.base), instr.accessors)); - MOTH_END_INSTR(LoadScopeObjectQStringPropertyDirectly) - MOTH_BEGIN_INSTR(StoreContextObjectProperty) engine->runtime.setQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); CHECK_EXCEPTION; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 3d2c143295..7cb7047b04 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -54,6 +54,7 @@ #include #include #include +#include QT_USE_NAMESPACE -- cgit v1.2.3 From 91ed06b767aa4993d28c8b2db4900c319098b035 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Jul 2016 11:58:14 +0200 Subject: Fix logic bug when deleting properties of JS objects The code used the size of the internal class in an inconsistent way. It should simply compute and work with the old internal class size, as that reflects the old object layout. [ChangeLog][QtQml] Fix assertion when deleting properties of JS objects Task-number: QTBUG-54589 Change-Id: Ie3db70437e780215d08a1a96491db75f8b859754 Reviewed-by: Robin Burchell Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4internalclass.cpp | 8 ++++---- tests/auto/qml/qqmlecmascript/data/qtbug_54589.qml | 16 ++++++++++++++++ tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 11 +++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/qtbug_54589.qml diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 8f0b1776d7..0bc4b9a7fc 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -155,8 +155,8 @@ static void insertHoleIntoPropertyData(Object *object, int idx) static void removeFromPropertyData(Object *object, int idx, bool accessor = false) { int inlineSize = object->d()->inlineMemberSize; - int icSize = object->internalClass()->size; int delta = (accessor ? 2 : 1); + int oldSize = object->internalClass()->size + delta; int to = idx; int from = to + delta; if (from < inlineSize) { @@ -164,15 +164,15 @@ static void removeFromPropertyData(Object *object, int idx, bool accessor = fals to = inlineSize - delta; from = inlineSize; } - if (to < inlineSize && from < icSize) { + if (to < inlineSize && from < oldSize) { Q_ASSERT(from >= inlineSize); memcpy(object->propertyData(to), object->d()->propertyData(from), (inlineSize - to)*sizeof(Value)); to = inlineSize; from = inlineSize + delta; } - if (from < icSize + delta) { + if (from < oldSize) { Q_ASSERT(to >= inlineSize && from > to); - memmove(object->propertyData(to), object->d()->propertyData(from), (icSize + delta - to)*sizeof(Value)); + memmove(object->propertyData(to), object->d()->propertyData(from), (oldSize - to)*sizeof(Value)); } } diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_54589.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_54589.qml new file mode 100644 index 0000000000..8f7d5f2a70 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_54589.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 + +QtObject { + function checkPropertyDeletion() { + var o = { + x: 1, + y: 2 + }; + o.z = 3 + delete o.y; + + return (o.x === 1 && o.y === undefined && o.z === 3) + } + + property bool result: checkPropertyDeletion() +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 08ebfbbbcf..2ec296ae66 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -328,6 +328,7 @@ private slots: void switchExpression(); void qtbug_46022(); void qtbug_52340(); + void qtbug_54589(); private: // static void propertyVarWeakRefCallback(v8::Persistent object, void* parameter); @@ -7922,6 +7923,16 @@ void tst_qqmlecmascript::qtbug_52340() QVERIFY(returnValue.toBool()); } +void tst_qqmlecmascript::qtbug_54589() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_54589.qml")); + + QScopedPointer obj(component.create()); + QVERIFY(obj != 0); + QCOMPARE(obj->property("result").toBool(), true); +} + + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" -- cgit v1.2.3 From 44e9a4dbdf4cd6e444ab7ab2ec137e196fcc087c Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 14 Jul 2016 11:25:59 +0200 Subject: QQuickPointerEvent::touchPointById: capture pointId by value It's a scalar, so capturing by reference is inefficient. Change-Id: Ieb6ba8f0ebeb630063e044a27363b2af50ab01ac Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 9c32c9781c..ef7d0218a5 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -584,7 +584,7 @@ const QTouchEvent::TouchPoint *QQuickPointerEvent::touchPointById(int pointId) c return nullptr; const QList &tps = ev->touchPoints(); auto it = std::find_if(tps.constBegin(), tps.constEnd(), - [&pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } ); + [pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } ); // return the pointer to the actual TP in QTouchEvent::_touchPoints return (it == tps.end() ? nullptr : it.operator->()); } -- cgit v1.2.3 From f21c8b2cfd424f5448da8c2332774a0e7a9d4ff0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 13 Jul 2016 14:57:33 +0200 Subject: Minor simplification of QQuickWindowPrivate::deliverMouseEvent Change-Id: I5334961b52714621638a0b3fcd137d5dfb232a25 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 7a9a096c34..af72ff3af2 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1654,8 +1654,7 @@ bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) me->accept(); q->sendEvent(mouseGrabberItem, me.data()); event->setAccepted(me->isAccepted()); - if (me->isAccepted()) - return true; + return me->isAccepted(); } return false; -- cgit v1.2.3 From f091351e5d4c116ffdd16768fec60bb07efa049f Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Wed, 29 Jun 2016 14:24:39 +0200 Subject: Enable building Qt Quick Sprite classes with QT_NO_OPENGL Change-Id: Iaea1fa3bd17acee326ae8e299cb0c60d371ffb5b Reviewed-by: Laszlo Agocs --- src/quick/items/items.pri | 22 +++++++++++----------- src/quick/items/qquickanimatedsprite.cpp | 12 +++++++++--- src/quick/items/qquickitemsmodule.cpp | 12 ++++++------ src/quick/items/qquickspritesequence.cpp | 12 +++++++++--- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 0c7dc97a2c..3f1e438c2b 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -79,7 +79,11 @@ HEADERS += \ $$PWD/qquickrendercontrol.h \ $$PWD/qquickrendercontrol_p.h \ $$PWD/qquickgraphicsinfo_p.h \ - $$PWD/qquickitemgrabresult.h + $$PWD/qquickitemgrabresult.h \ + $$PWD/qquickspriteengine_p.h \ + $$PWD/qquicksprite_p.h \ + $$PWD/qquickspritesequence_p.h \ + $$PWD/qquickanimatedsprite_p.h SOURCES += \ $$PWD/qquickevents.cpp \ @@ -134,7 +138,11 @@ SOURCES += \ $$PWD/qquickgenericshadereffect.cpp \ $$PWD/qquickrendercontrol.cpp \ $$PWD/qquickgraphicsinfo.cpp \ - $$PWD/qquickitemgrabresult.cpp + $$PWD/qquickitemgrabresult.cpp \ + $$PWD/qquickspriteengine.cpp \ + $$PWD/qquicksprite.cpp \ + $$PWD/qquickspritesequence.cpp \ + $$PWD/qquickanimatedsprite.cpp # Items that depend on OpenGL Renderer contains(QT_CONFIG, opengl(es1|es2)?) { @@ -142,18 +150,10 @@ contains(QT_CONFIG, opengl(es1|es2)?) { $$PWD/qquickopenglinfo.cpp \ $$PWD/qquickopenglshadereffect.cpp \ $$PWD/qquickopenglshadereffectnode.cpp \ - $$PWD/qquickframebufferobject.cpp \ - $$PWD/qquickspriteengine.cpp \ - $$PWD/qquicksprite.cpp \ - $$PWD/qquickspritesequence.cpp \ - $$PWD/qquickanimatedsprite.cpp + $$PWD/qquickframebufferobject.cpp HEADERS += \ $$PWD/qquickopenglinfo_p.h \ - $$PWD/qquickspriteengine_p.h \ - $$PWD/qquicksprite_p.h \ - $$PWD/qquickspritesequence_p.h \ - $$PWD/qquickanimatedsprite_p.h \ $$PWD/qquickopenglshadereffect_p.h \ $$PWD/qquickopenglshadereffectnode_p.h \ $$PWD/qquickframebufferobject.h diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp index 77c7ae106b..aaa0487afd 100644 --- a/src/quick/items/qquickanimatedsprite.cpp +++ b/src/quick/items/qquickanimatedsprite.cpp @@ -96,6 +96,7 @@ QQuickAnimatedSpriteMaterial::~QQuickAnimatedSpriteMaterial() delete texture; } +#ifndef QT_NO_OPENGL class AnimatedSpriteMaterialData : public QSGMaterialShader { public: @@ -140,10 +141,15 @@ public: int m_animData_id; int m_animPos_id; }; +#endif QSGMaterialShader *QQuickAnimatedSpriteMaterial::createShader() const { +#ifndef QT_NO_OPENGL return new AnimatedSpriteMaterialData; +#else + return nullptr; +#endif } struct AnimatedSpriteVertex { @@ -448,8 +454,8 @@ void QQuickAnimatedSprite::createEngine() } static QSGGeometry::Attribute AnimatedSprite_Attributes[] = { - QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // pos - QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // tex + QSGGeometry::Attribute::create(0, 2, QSGGeometry::TypeFloat, true), // pos + QSGGeometry::Attribute::create(1, 2, QSGGeometry::TypeFloat), // tex }; static QSGGeometry::AttributeSet AnimatedSprite_AttributeSet = @@ -508,7 +514,7 @@ QSGGeometryNode* QQuickAnimatedSprite::buildNode() int vCount = 4; int iCount = 6; QSGGeometry *g = new QSGGeometry(AnimatedSprite_AttributeSet, vCount, iCount); - g->setDrawingMode(GL_TRIANGLES); + g->setDrawingMode(QSGGeometry::DrawTriangles); AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) g->vertexData(); diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 8ed7f2bb05..fb60331ce2 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -73,11 +73,11 @@ //#include #include #include -# include "qquickitemgrabresult.h" +#include "qquickitemgrabresult.h" +#include "qquicksprite_p.h" +#include "qquickspritesequence_p.h" +#include "qquickanimatedsprite_p.h" #ifndef QT_NO_OPENGL -# include "qquicksprite_p.h" -# include "qquickspritesequence_p.h" -# include "qquickanimatedsprite_p.h" # include "qquickopenglinfo_p.h" #endif #include "qquickgraphicsinfo_p.h" @@ -218,11 +218,11 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterUncreatableType("QtQuick", 2, 0, "PaintedItem", QQuickPaintedItem::tr("Cannot create instance of abstract class PaintedItem")); qmlRegisterType("QtQuick", 2, 0, "Canvas"); -#ifndef QT_NO_OPENGL + qmlRegisterType("QtQuick", 2, 0, "Sprite"); qmlRegisterType("QtQuick", 2, 0, "AnimatedSprite"); qmlRegisterType("QtQuick", 2, 0, "SpriteSequence"); -#endif + qmlRegisterType(uri, major, minor,"ParentChange"); qmlRegisterType(uri, major, minor,"AnchorChanges"); qmlRegisterType(); diff --git a/src/quick/items/qquickspritesequence.cpp b/src/quick/items/qquickspritesequence.cpp index f32e1afd50..6654c7a964 100644 --- a/src/quick/items/qquickspritesequence.cpp +++ b/src/quick/items/qquickspritesequence.cpp @@ -95,6 +95,7 @@ QQuickSpriteSequenceMaterial::~QQuickSpriteSequenceMaterial() delete texture; } +#ifndef QT_NO_OPENGL class SpriteSequenceMaterialData : public QSGMaterialShader { public: @@ -139,10 +140,15 @@ public: int m_animData_id; int m_animPos_id; }; +#endif QSGMaterialShader *QQuickSpriteSequenceMaterial::createShader() const { +#ifndef QT_NO_OPENGL return new SpriteSequenceMaterialData; +#else + return nullptr; +#endif } struct SpriteVertex { @@ -275,8 +281,8 @@ void QQuickSpriteSequence::createEngine() } static QSGGeometry::Attribute SpriteSequence_Attributes[] = { - QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // pos - QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // tex + QSGGeometry::Attribute::create(0, 2, QSGGeometry::TypeFloat, true), // pos + QSGGeometry::Attribute::create(1, 2, QSGGeometry::TypeFloat), // tex }; static QSGGeometry::AttributeSet SpriteSequence_AttributeSet = @@ -341,7 +347,7 @@ QSGGeometryNode* QQuickSpriteSequence::buildNode() int vCount = 4; int iCount = 6; QSGGeometry *g = new QSGGeometry(SpriteSequence_AttributeSet, vCount, iCount); - g->setDrawingMode(GL_TRIANGLES); + g->setDrawingMode(QSGGeometry::DrawTriangles); SpriteVertices *p = (SpriteVertices *) g->vertexData(); QRectF texRect = m_material->texture->normalizedTextureSubRect(); -- cgit v1.2.3 From 9d4f11a500b2d478edd7d92391bbf3031103bced Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Thu, 30 Jun 2016 16:58:55 +0200 Subject: Add QSGSpriteNode to the Scenegraph Adaptation Layer Most core Qt Quick items use one of the nodes provided by the Scenegraph Adaptation Layer, however the two items that provide support for Sprites created their own custom nodes. There was significant redundancy in this, and it made it only possible to use the OpenGL adaptation. The AnimatedSprite and SpriteSequence items have been cleaned up, and now use the new QSGSpriteNode. Change-Id: Idc20b9c5da9dc1c94f6368021785382cdf7cec5a Reviewed-by: Laszlo Agocs --- src/plugins/scenegraph/d3d12/qsgd3d12context.cpp | 5 + src/plugins/scenegraph/d3d12/qsgd3d12context_p.h | 2 + .../scenegraph/d3d12/qsgd3d12rendercontext.cpp | 6 + .../scenegraph/d3d12/qsgd3d12rendercontext_p.h | 1 + src/quick/items/items.pri | 8 +- src/quick/items/items.qrc | 4 - src/quick/items/qquickanimatedsprite.cpp | 692 ++++++++++++--------- src/quick/items/qquickanimatedsprite_p.h | 286 ++------- src/quick/items/qquickanimatedsprite_p_p.h | 96 +++ src/quick/items/qquicksprite_p.h | 2 +- src/quick/items/qquickspriteengine.cpp | 14 +- src/quick/items/qquickspriteengine_p.h | 2 +- src/quick/items/qquickspritesequence.cpp | 408 ++++-------- src/quick/items/qquickspritesequence_p.h | 68 +- src/quick/items/qquickspritesequence_p_p.h | 90 +++ src/quick/items/shaders/sprite.frag | 12 - src/quick/items/shaders/sprite.vert | 23 - src/quick/items/shaders/sprite_core.frag | 16 - src/quick/items/shaders/sprite_core.vert | 24 - .../adaptations/software/qsgsoftwarecontext.cpp | 11 + .../adaptations/software/qsgsoftwarecontext_p.h | 2 + .../software/qsgsoftwarerenderablenode.cpp | 11 + .../software/qsgsoftwarerenderablenode_p.h | 5 +- .../software/qsgsoftwarerenderablenodeupdater.cpp | 10 + .../software/qsgsoftwarerenderablenodeupdater_p.h | 2 + .../software/qsgsoftwarerenderlistbuilder.cpp | 10 + .../software/qsgsoftwarerenderlistbuilder_p.h | 2 + .../adaptations/software/qsgsoftwarespritenode.cpp | 139 +++++ .../adaptations/software/qsgsoftwarespritenode_p.h | 92 +++ .../scenegraph/adaptations/software/software.pri | 6 +- src/quick/scenegraph/qsgadaptationlayer_p.h | 20 + src/quick/scenegraph/qsgcontext_p.h | 4 + src/quick/scenegraph/qsgdefaultcontext.cpp | 6 + src/quick/scenegraph/qsgdefaultcontext_p.h | 1 + src/quick/scenegraph/qsgdefaultrendercontext_p.h | 2 +- src/quick/scenegraph/qsgdefaultspritenode.cpp | 303 +++++++++ src/quick/scenegraph/qsgdefaultspritenode_p.h | 87 +++ src/quick/scenegraph/scenegraph.pri | 2 + src/quick/scenegraph/scenegraph.qrc | 4 + src/quick/scenegraph/shaders/sprite.frag | 12 + src/quick/scenegraph/shaders/sprite.vert | 23 + src/quick/scenegraph/shaders/sprite_core.frag | 16 + src/quick/scenegraph/shaders/sprite_core.vert | 24 + 43 files changed, 1579 insertions(+), 974 deletions(-) create mode 100644 src/quick/items/qquickanimatedsprite_p_p.h create mode 100644 src/quick/items/qquickspritesequence_p_p.h delete mode 100644 src/quick/items/shaders/sprite.frag delete mode 100644 src/quick/items/shaders/sprite.vert delete mode 100644 src/quick/items/shaders/sprite_core.frag delete mode 100644 src/quick/items/shaders/sprite_core.vert create mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp create mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h create mode 100644 src/quick/scenegraph/qsgdefaultspritenode.cpp create mode 100644 src/quick/scenegraph/qsgdefaultspritenode_p.h create mode 100644 src/quick/scenegraph/shaders/sprite.frag create mode 100644 src/quick/scenegraph/shaders/sprite.vert create mode 100644 src/quick/scenegraph/shaders/sprite_core.frag create mode 100644 src/quick/scenegraph/shaders/sprite_core.vert diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp index 1b29ddd59c..64c6313b42 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp @@ -127,4 +127,9 @@ QSGNinePatchNode *QSGD3D12Context::createNinePatchNode() return new QSGD3D12NinePatchNode; } +QSGSpriteNode *QSGD3D12Context::createSpriteNode() +{ + return nullptr; +} + QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h index f4d9429c7b..70cc606b52 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h @@ -75,6 +75,8 @@ public: QSGRectangleNode *createRectangleNode() override; QSGImageNode *createImageNode() override; QSGNinePatchNode *createNinePatchNode() override; + QSGSpriteNode *createSpriteNode() override; + }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp index 1a63117ec0..e261cb6447 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp @@ -115,6 +115,12 @@ QSGRenderer *QSGD3D12RenderContext::createRenderer() return new QSGD3D12Renderer(this); } +int QSGD3D12RenderContext::maxTextureSize() const +{ + // XXX: Do it the correct way + return 4096; +} + void QSGD3D12RenderContext::renderNextFrame(QSGRenderer *renderer, uint fbo) { static_cast(renderer)->renderScene(fbo); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h index 5cd0b52f4b..ca85aaa46f 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h @@ -68,6 +68,7 @@ public: void renderNextFrame(QSGRenderer *renderer, uint fbo) override; QSGTexture *createTexture(const QImage &image, uint flags) const override; QSGRenderer *createRenderer() override; + int maxTextureSize() const override; void setEngine(QSGD3D12Engine *engine); QSGD3D12Engine *engine() { return m_engine; } diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 3f1e438c2b..beaf3540bc 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -83,7 +83,9 @@ HEADERS += \ $$PWD/qquickspriteengine_p.h \ $$PWD/qquicksprite_p.h \ $$PWD/qquickspritesequence_p.h \ - $$PWD/qquickanimatedsprite_p.h + $$PWD/qquickanimatedsprite_p.h \ + $$PWD/qquickanimatedsprite_p_p.h \ + $$PWD/qquickspritesequence_p_p.h SOURCES += \ $$PWD/qquickevents.cpp \ @@ -159,14 +161,10 @@ contains(QT_CONFIG, opengl(es1|es2)?) { $$PWD/qquickframebufferobject.h OTHER_FILES += \ - $$PWD/shaders/sprite.vert \ - $$PWD/shaders/sprite.frag \ $$PWD/shaders/shadereffect.vert \ $$PWD/shaders/shadereffect.frag \ $$PWD/shaders/shadereffectfallback.vert \ $$PWD/shaders/shadereffectfallback.frag \ - $$PWD/shaders/sprite_core.vert \ - $$PWD/shaders/sprite_core.frag \ $$PWD/shaders/shadereffect_core.vert \ $$PWD/shaders/shadereffect_core.frag \ $$PWD/shaders/shadereffectfallback_core.vert \ diff --git a/src/quick/items/items.qrc b/src/quick/items/items.qrc index 99f9b5224f..6aaf757c29 100644 --- a/src/quick/items/items.qrc +++ b/src/quick/items/items.qrc @@ -1,7 +1,5 @@ - shaders/sprite.frag - shaders/sprite.vert shaders/shadereffect.vert shaders/shadereffect.frag shaders/shadereffectfallback.frag @@ -10,7 +8,5 @@ shaders/shadereffect_core.vert shaders/shadereffectfallback_core.frag shaders/shadereffectfallback_core.vert - shaders/sprite_core.frag - shaders/sprite_core.vert diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp index aaa0487afd..991ca8519f 100644 --- a/src/quick/items/qquickanimatedsprite.cpp +++ b/src/quick/items/qquickanimatedsprite.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qquickanimatedsprite_p.h" +#include "qquickanimatedsprite_p_p.h" #include "qquicksprite_p.h" #include "qquickspriteengine_p.h" #include @@ -55,117 +56,6 @@ QT_BEGIN_NAMESPACE -class QQuickAnimatedSpriteMaterial : public QSGMaterial -{ -public: - QQuickAnimatedSpriteMaterial(); - ~QQuickAnimatedSpriteMaterial(); - QSGMaterialType *type() const Q_DECL_OVERRIDE { static QSGMaterialType type; return &type; } - QSGMaterialShader *createShader() const Q_DECL_OVERRIDE; - int compare(const QSGMaterial *other) const Q_DECL_OVERRIDE - { - return this - static_cast(other); - } - - QSGTexture *texture; - - float animT; - float animX1; - float animY1; - float animX2; - float animY2; - float animW; - float animH; -}; - -QQuickAnimatedSpriteMaterial::QQuickAnimatedSpriteMaterial() - : texture(0) - , animT(0.0f) - , animX1(0.0f) - , animY1(0.0f) - , animX2(0.0f) - , animY2(0.0f) - , animW(1.0f) - , animH(1.0f) -{ - setFlag(Blending, true); -} - -QQuickAnimatedSpriteMaterial::~QQuickAnimatedSpriteMaterial() -{ - delete texture; -} - -#ifndef QT_NO_OPENGL -class AnimatedSpriteMaterialData : public QSGMaterialShader -{ -public: - AnimatedSpriteMaterialData() - : QSGMaterialShader() - { - setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/sprite.vert")); - setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/sprite.frag")); - } - - void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) Q_DECL_OVERRIDE - { - QQuickAnimatedSpriteMaterial *m = static_cast(newEffect); - m->texture->bind(); - - program()->setUniformValue(m_opacity_id, state.opacity()); - program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT); - program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrix_id, state.combinedMatrix()); - } - - void initialize() Q_DECL_OVERRIDE { - m_matrix_id = program()->uniformLocation("qt_Matrix"); - m_opacity_id = program()->uniformLocation("qt_Opacity"); - m_animData_id = program()->uniformLocation("animData"); - m_animPos_id = program()->uniformLocation("animPos"); - } - - char const *const *attributeNames() const Q_DECL_OVERRIDE { - static const char *attr[] = { - "vPos", - "vTex", - 0 - }; - return attr; - } - - int m_matrix_id; - int m_opacity_id; - int m_animData_id; - int m_animPos_id; -}; -#endif - -QSGMaterialShader *QQuickAnimatedSpriteMaterial::createShader() const -{ -#ifndef QT_NO_OPENGL - return new AnimatedSpriteMaterialData; -#else - return nullptr; -#endif -} - -struct AnimatedSpriteVertex { - float x; - float y; - float tx; - float ty; -}; - -struct AnimatedSpriteVertices { - AnimatedSpriteVertex v1; - AnimatedSpriteVertex v2; - AnimatedSpriteVertex v3; - AnimatedSpriteVertex v4; -}; - /*! \qmltype AnimatedSprite \instantiates QQuickAnimatedSprite @@ -321,18 +211,11 @@ struct AnimatedSpriteVertices { //TODO: Implicitly size element to size of sprite QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) : - QQuickItem(parent) - , m_sprite(new QQuickSprite(this)) - , m_spriteEngine(0) - , m_curFrame(0) - , m_pleaseReset(false) - , m_running(true) - , m_paused(false) - , m_interpolate(true) - , m_loops(-1) - , m_curLoop(0) - , m_pauseOffset(0) + QQuickItem(*(new QQuickAnimatedSpritePrivate), parent) { + Q_D(QQuickAnimatedSprite); + d->m_sprite = new QQuickSprite(this); + setFlag(ItemHasContents); connect(this, SIGNAL(widthChanged()), this, SLOT(reset())); @@ -340,6 +223,96 @@ QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) : this, SLOT(reset())); } +bool QQuickAnimatedSprite::running() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_running; +} + +bool QQuickAnimatedSprite::interpolate() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_interpolate; +} + +QUrl QQuickAnimatedSprite::source() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_sprite->source(); +} + +bool QQuickAnimatedSprite::reverse() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_sprite->reverse(); +} + +bool QQuickAnimatedSprite::frameSync() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_sprite->frameSync(); +} + +int QQuickAnimatedSprite::frameCount() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_sprite->frames(); +} + +int QQuickAnimatedSprite::frameHeight() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_sprite->frameHeight(); +} + +int QQuickAnimatedSprite::frameWidth() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_sprite->frameWidth(); +} + +int QQuickAnimatedSprite::frameX() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_sprite->frameX(); +} + +int QQuickAnimatedSprite::frameY() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_sprite->frameY(); +} + +qreal QQuickAnimatedSprite::frameRate() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_sprite->frameRate(); +} + +int QQuickAnimatedSprite::frameDuration() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_sprite->frameDuration(); +} + +int QQuickAnimatedSprite::loops() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_loops; +} + +bool QQuickAnimatedSprite::paused() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_paused; +} + +int QQuickAnimatedSprite::currentFrame() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_curFrame; +} + bool QQuickAnimatedSprite::isCurrentFrameChangedConnected() { IS_SIGNAL_CONNECTED(this, QQuickAnimatedSprite, currentFrameChanged, (int)); @@ -354,23 +327,25 @@ void QQuickAnimatedSprite::reloadImage() void QQuickAnimatedSprite::componentComplete() { + Q_D(const QQuickAnimatedSprite); createEngine(); QQuickItem::componentComplete(); - if (m_running) + if (d->m_running) start(); } void QQuickAnimatedSprite::start() { - m_running = true; + Q_D(QQuickAnimatedSprite); + d->m_running = true; if (!isComponentComplete()) return; - m_curLoop = 0; - m_timestamp.start(); - if (m_spriteEngine) { - m_spriteEngine->stop(0); - m_spriteEngine->updateSprites(0); - m_spriteEngine->start(0); + d->m_curLoop = 0; + d->m_timestamp.start(); + if (d->m_spriteEngine) { + d->m_spriteEngine->stop(0); + d->m_spriteEngine->updateSprites(0); + d->m_spriteEngine->start(0); } emit currentFrameChanged(0); emit runningChanged(true); @@ -379,10 +354,11 @@ void QQuickAnimatedSprite::start() void QQuickAnimatedSprite::stop() { - m_running = false; + Q_D(QQuickAnimatedSprite); + d->m_running = false; if (!isComponentComplete()) return; - m_pauseOffset = 0; + d->m_pauseOffset = 0; emit runningChanged(false); update(); } @@ -394,14 +370,15 @@ void QQuickAnimatedSprite::stop() */ void QQuickAnimatedSprite::advance(int frames) { + Q_D(QQuickAnimatedSprite); if (!frames) return; //TODO-C: May not work when running - only when paused - m_curFrame += frames; - while (m_curFrame < 0) - m_curFrame += m_spriteEngine->maxFrames(); - m_curFrame = m_curFrame % m_spriteEngine->maxFrames(); - emit currentFrameChanged(m_curFrame); + d->m_curFrame += frames; + while (d->m_curFrame < 0) + d->m_curFrame += d->m_spriteEngine->maxFrames(); + d->m_curFrame = d->m_curFrame % d->m_spriteEngine->maxFrames(); + emit currentFrameChanged(d->m_curFrame); update(); } @@ -415,10 +392,12 @@ void QQuickAnimatedSprite::advance(int frames) */ void QQuickAnimatedSprite::pause() { - if (m_paused) + Q_D(QQuickAnimatedSprite); + + if (d->m_paused) return; - m_pauseOffset = m_timestamp.elapsed(); - m_paused = true; + d->m_pauseOffset = d->m_timestamp.elapsed(); + d->m_paused = true; emit pausedChanged(true); update(); } @@ -433,246 +412,363 @@ void QQuickAnimatedSprite::pause() */ void QQuickAnimatedSprite::resume() { - if (!m_paused) + Q_D(QQuickAnimatedSprite); + + if (!d->m_paused) return; - m_pauseOffset = m_pauseOffset - m_timestamp.elapsed(); - m_paused = false; + d->m_pauseOffset = d->m_pauseOffset - d->m_timestamp.elapsed(); + d->m_paused = false; emit pausedChanged(false); update(); } -void QQuickAnimatedSprite::createEngine() +void QQuickAnimatedSprite::setRunning(bool arg) { - if (m_spriteEngine) - delete m_spriteEngine; - QList spriteList; - spriteList << m_sprite; - m_spriteEngine = new QQuickSpriteEngine(QList(spriteList), this); - m_spriteEngine->startAssemblingImage(); - reset(); - update(); + Q_D(QQuickAnimatedSprite); + + if (d->m_running != arg) { + if (d->m_running) + stop(); + else + start(); + } } -static QSGGeometry::Attribute AnimatedSprite_Attributes[] = { - QSGGeometry::Attribute::create(0, 2, QSGGeometry::TypeFloat, true), // pos - QSGGeometry::Attribute::create(1, 2, QSGGeometry::TypeFloat), // tex -}; +void QQuickAnimatedSprite::setPaused(bool arg) +{ + Q_D(const QQuickAnimatedSprite); -static QSGGeometry::AttributeSet AnimatedSprite_AttributeSet = + if (d->m_paused != arg) { + if (d->m_paused) + resume(); + else + pause(); + } +} + +void QQuickAnimatedSprite::setInterpolate(bool arg) +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_interpolate != arg) { + d->m_interpolate = arg; + Q_EMIT interpolateChanged(arg); + } +} + +void QQuickAnimatedSprite::setSource(QUrl arg) +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_sprite->m_source != arg) { + d->m_sprite->setSource(arg); + Q_EMIT sourceChanged(arg); + reloadImage(); + } +} + +void QQuickAnimatedSprite::setReverse(bool arg) +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_sprite->m_reverse != arg) { + d->m_sprite->setReverse(arg); + Q_EMIT reverseChanged(arg); + } +} + +void QQuickAnimatedSprite::setFrameSync(bool arg) +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_sprite->m_frameSync != arg) { + d->m_sprite->setFrameSync(arg); + Q_EMIT frameSyncChanged(arg); + if (d->m_running) + restart(); + } +} + +void QQuickAnimatedSprite::setFrameCount(int arg) { - 2, // Attribute Count - (2+2) * sizeof(float), - AnimatedSprite_Attributes -}; + Q_D(QQuickAnimatedSprite); + + if (d->m_sprite->m_frames != arg) { + d->m_sprite->setFrameCount(arg); + Q_EMIT frameCountChanged(arg); + reloadImage(); + } +} -void QQuickAnimatedSprite::sizeVertices(QSGGeometryNode *node) +void QQuickAnimatedSprite::setFrameHeight(int arg) { - AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) node->geometry()->vertexData(); - p->v1.x = 0; - p->v1.y = 0; + Q_D(QQuickAnimatedSprite); - p->v2.x = width(); - p->v2.y = 0; + if (d->m_sprite->m_frameHeight != arg) { + d->m_sprite->setFrameHeight(arg); + Q_EMIT frameHeightChanged(arg); + reloadImage(); + } +} - p->v3.x = 0; - p->v3.y = height(); +void QQuickAnimatedSprite::setFrameWidth(int arg) +{ + Q_D(QQuickAnimatedSprite); - p->v4.x = width(); - p->v4.y = height(); + if (d->m_sprite->m_frameWidth != arg) { + d->m_sprite->setFrameWidth(arg); + Q_EMIT frameWidthChanged(arg); + reloadImage(); + } } -QSGGeometryNode* QQuickAnimatedSprite::buildNode() +void QQuickAnimatedSprite::setFrameX(int arg) { - if (!m_spriteEngine) { + Q_D(QQuickAnimatedSprite); + + if (d->m_sprite->m_frameX != arg) { + d->m_sprite->setFrameX(arg); + Q_EMIT frameXChanged(arg); + reloadImage(); + } +} + +void QQuickAnimatedSprite::setFrameY(int arg) +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_sprite->m_frameY != arg) { + d->m_sprite->setFrameY(arg); + Q_EMIT frameYChanged(arg); + reloadImage(); + } +} + +void QQuickAnimatedSprite::setFrameRate(qreal arg) +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_sprite->m_frameRate != arg) { + d->m_sprite->setFrameRate(arg); + Q_EMIT frameRateChanged(arg); + if (d->m_running) + restart(); + } +} + +void QQuickAnimatedSprite::setFrameDuration(int arg) +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_sprite->m_frameDuration != arg) { + d->m_sprite->setFrameDuration(arg); + Q_EMIT frameDurationChanged(arg); + if (d->m_running) + restart(); + } +} + +void QQuickAnimatedSprite::resetFrameRate() +{ + setFrameRate(-1.0); +} + +void QQuickAnimatedSprite::resetFrameDuration() +{ + setFrameDuration(-1); +} + +void QQuickAnimatedSprite::setLoops(int arg) +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_loops != arg) { + d->m_loops = arg; + Q_EMIT loopsChanged(arg); + } +} + +void QQuickAnimatedSprite::setCurrentFrame(int arg) //TODO-C: Probably only works when paused +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_curFrame != arg) { + d->m_curFrame = arg; + Q_EMIT currentFrameChanged(arg); //TODO-C Only emitted on manual advance! + update(); + } +} + +void QQuickAnimatedSprite::createEngine() +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_spriteEngine) + delete d->m_spriteEngine; + QList spriteList; + spriteList << d->m_sprite; + d->m_spriteEngine = new QQuickSpriteEngine(QList(spriteList), this); + d->m_spriteEngine->startAssemblingImage(); + reset(); + update(); +} + +QSGSpriteNode* QQuickAnimatedSprite::initNode() +{ + Q_D(QQuickAnimatedSprite); + + if (!d->m_spriteEngine) { qmlInfo(this) << "No sprite engine..."; - return 0; - } else if (m_spriteEngine->status() == QQuickPixmap::Null) { - m_spriteEngine->startAssemblingImage(); + return nullptr; + } else if (d->m_spriteEngine->status() == QQuickPixmap::Null) { + d->m_spriteEngine->startAssemblingImage(); update();//Schedule another update, where we will check again - return 0; - } else if (m_spriteEngine->status() == QQuickPixmap::Loading) { + return nullptr; + } else if (d->m_spriteEngine->status() == QQuickPixmap::Loading) { update();//Schedule another update, where we will check again - return 0; + return nullptr; } - QQuickAnimatedSpriteMaterial *material = new QQuickAnimatedSpriteMaterial(); - - QImage image = m_spriteEngine->assembledImage(); //Engine prints errors if there are any + QImage image = d->m_spriteEngine->assembledImage(d->sceneGraphRenderContext()->maxTextureSize()); //Engine prints errors if there are any if (image.isNull()) - return 0; - m_sheetSize = QSizeF(image.size()); - material->texture = window()->createTextureFromImage(image); - m_spriteEngine->start(0); - material->animT = 0; - material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width(); - material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height(); - material->animX2 = material->animX1; - material->animY2 = material->animY1; - material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width(); - material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height(); - - int vCount = 4; - int iCount = 6; - QSGGeometry *g = new QSGGeometry(AnimatedSprite_AttributeSet, vCount, iCount); - g->setDrawingMode(QSGGeometry::DrawTriangles); - - AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) g->vertexData(); - - QRectF texRect = material->texture->normalizedTextureSubRect(); - - p->v1.tx = texRect.topLeft().x(); - p->v1.ty = texRect.topLeft().y(); - - p->v2.tx = texRect.topRight().x(); - p->v2.ty = texRect.topRight().y(); - - p->v3.tx = texRect.bottomLeft().x(); - p->v3.ty = texRect.bottomLeft().y(); - - p->v4.tx = texRect.bottomRight().x(); - p->v4.ty = texRect.bottomRight().y(); - - quint16 *indices = g->indexDataAsUShort(); - indices[0] = 0; - indices[1] = 1; - indices[2] = 2; - indices[3] = 1; - indices[4] = 3; - indices[5] = 2; - - - QSGGeometryNode *node = new QSGGeometryNode(); - node->setGeometry(g); - node->setMaterial(material); - node->setFlag(QSGGeometryNode::OwnsMaterial); - node->setFlag(QSGGeometryNode::OwnsGeometry); - sizeVertices(node); + return nullptr; + + QSGSpriteNode *node = d->sceneGraphContext()->createSpriteNode(); + + d->m_sheetSize = QSize(image.size()); + node->setTexture(window()->createTextureFromImage(image)); + d->m_spriteEngine->start(0); + node->setTime(0.0f); + node->setSourceA(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY())); + node->setSourceB(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY())); + node->setSpriteSize(QSize(d->m_spriteEngine->spriteWidth(), d->m_spriteEngine->spriteHeight())); + node->setSheetSize(d->m_sheetSize); + node->setSize(QSizeF(width(), height())); return node; } void QQuickAnimatedSprite::reset() { - m_pleaseReset = true; + Q_D(QQuickAnimatedSprite); + d->m_pleaseReset = true; update(); } QSGNode *QQuickAnimatedSprite::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { - if (m_pleaseReset) { + Q_D(QQuickAnimatedSprite); + + if (d->m_pleaseReset) { delete oldNode; - oldNode = 0; - m_pleaseReset = false; + oldNode = nullptr; + d->m_pleaseReset = false; } - QSGGeometryNode *node = static_cast(oldNode); + QSGSpriteNode *node = static_cast(oldNode); if (!node) - node = buildNode(); + node = initNode(); if (node) prepareNextFrame(node); - if (m_running) { - if (!m_paused) - update(); - - if (node) { - node->markDirty(QSGNode::DirtyMaterial); - } - } + if (d->m_running && !d->m_paused) + update(); return node; } -void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node) +void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node) { - int timeInt = m_timestamp.elapsed() + m_pauseOffset; + Q_D(QQuickAnimatedSprite); + + int timeInt = d->m_timestamp.elapsed() + d->m_pauseOffset; qreal time = timeInt / 1000.; int frameAt; qreal progress = 0.0; - int lastFrame = m_curFrame; - if (m_running && !m_paused) { - const int nColumns = int(m_sheetSize.width()) / m_spriteEngine->spriteWidth(); + int lastFrame = d->m_curFrame; + if (d->m_running && !d->m_paused) { + const int nColumns = d->m_sheetSize.width() / d->m_spriteEngine->spriteWidth(); //Advance State (keeps time for psuedostates) - m_spriteEngine->updateSprites(timeInt); + d->m_spriteEngine->updateSprites(timeInt); //Advance AnimatedSprite - qreal animT = m_spriteEngine->spriteStart()/1000.0; - const int frameCountInRow = m_spriteEngine->spriteFrames(); - const qreal frameDuration = m_spriteEngine->spriteDuration()/frameCountInRow; + qreal animT = d->m_spriteEngine->spriteStart()/1000.0; + const int frameCountInRow = d->m_spriteEngine->spriteFrames(); + const qreal frameDuration = d->m_spriteEngine->spriteDuration() / frameCountInRow; if (frameDuration > 0) { qreal frame = (time - animT)/(frameDuration / 1000.0); - bool lastLoop = m_loops > 0 && m_curLoop == m_loops-1; + bool lastLoop = d->m_loops > 0 && d->m_curLoop == d->m_loops-1; //don't visually interpolate for the last frame of the last loop const int max = lastLoop ? frameCountInRow - 1 : frameCountInRow; frame = qBound(qreal(0.0), frame, qreal(max)); double intpart; progress = std::modf(frame,&intpart); frameAt = (int)intpart; - const int rowIndex = m_spriteEngine->spriteY()/frameHeight(); + const int rowIndex = d->m_spriteEngine->spriteY()/frameHeight(); const int newFrame = rowIndex * nColumns + frameAt; - if (m_curFrame > newFrame) //went around - m_curLoop++; - m_curFrame = newFrame; + if (d->m_curFrame > newFrame) //went around + d->m_curLoop++; + d->m_curFrame = newFrame; } else { - m_curFrame++; - if (m_curFrame >= m_spriteEngine->maxFrames()) { // maxFrames: total number of frames including all rows - m_curFrame = 0; - m_curLoop++; + d->m_curFrame++; + if (d->m_curFrame >= d->m_spriteEngine->maxFrames()) { // maxFrames: total number of frames including all rows + d->m_curFrame = 0; + d->m_curLoop++; } - frameAt = m_curFrame % nColumns; + frameAt = d->m_curFrame % nColumns; if (frameAt == 0) - m_spriteEngine->advance(); + d->m_spriteEngine->advance(); progress = 0; } - if (m_loops > 0 && m_curLoop >= m_loops) { + if (d->m_loops > 0 && d->m_curLoop >= d->m_loops) { frameAt = 0; - m_running = false; + d->m_running = false; emit runningChanged(false); update(); } } else { - frameAt = m_curFrame; + frameAt = d->m_curFrame; } - if (m_curFrame != lastFrame) { + if (d->m_curFrame != lastFrame) { if (isCurrentFrameChangedConnected()) - emit currentFrameChanged(m_curFrame); + emit currentFrameChanged(d->m_curFrame); update(); } - qreal frameCount = m_spriteEngine->spriteFrames(); - bool reverse = m_spriteEngine->sprite()->reverse(); + qreal frameCount = d->m_spriteEngine->spriteFrames(); + bool reverse = d->m_spriteEngine->sprite()->reverse(); if (reverse) frameAt = (frameCount - 1) - frameAt; - qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width(); - qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height(); - qreal x1; - qreal y1; - if (m_paused) { - int spriteY = m_spriteEngine->spriteY(); + int w = d->m_spriteEngine->spriteWidth(); + int h = d->m_spriteEngine->spriteHeight(); + int x1; + int y1; + if (d->m_paused) { + int spriteY = d->m_spriteEngine->spriteY(); if (reverse) { - int rows = m_spriteEngine->maxFrames() * m_spriteEngine->spriteWidth() / m_sheetSize.width(); - spriteY -= rows * m_spriteEngine->spriteHeight(); + int rows = d->m_spriteEngine->maxFrames() * d->m_spriteEngine->spriteWidth() / d->m_sheetSize.width(); + spriteY -= rows * d->m_spriteEngine->spriteHeight(); frameAt = (frameCount - 1) - frameAt; } - int position = frameAt * m_spriteEngine->spriteWidth() + m_spriteEngine->spriteX(); - int row = position / m_sheetSize.width(); + int position = frameAt * d->m_spriteEngine->spriteWidth() + d->m_spriteEngine->spriteX(); + int row = position / d->m_sheetSize.width(); - x1 = (position - (row * m_sheetSize.width())) / m_sheetSize.width(); - y1 = (row * m_spriteEngine->spriteHeight() + spriteY) / m_sheetSize.height(); + x1 = (position - (row * d->m_sheetSize.width())); + y1 = (row * d->m_spriteEngine->spriteHeight() + spriteY); } else { - x1 = m_spriteEngine->spriteX() / m_sheetSize.width() + frameAt * w; - y1 = m_spriteEngine->spriteY() / m_sheetSize.height(); + x1 = d->m_spriteEngine->spriteX() + frameAt * w; + y1 = d->m_spriteEngine->spriteY(); } //### hard-coded 0/1 work because we are the only // images in the sprite sheet (without this we cannot assume // where in the sheet we begin/end). - qreal x2; - qreal y2; + int x2; + int y2; if (reverse) { if (frameAt > 0) { x2 = x1 - w; @@ -682,9 +778,9 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node) y2 = y1 - h; if (y2 < 0.0) { //the last row may not fill the entire width - int maxRowFrames = m_sheetSize.width() / m_spriteEngine->spriteWidth(); - if (m_spriteEngine->maxFrames() % maxRowFrames) - x2 = ((m_spriteEngine->maxFrames() % maxRowFrames) - 1) * w; + int maxRowFrames = d->m_sheetSize.width() / d->m_spriteEngine->spriteWidth(); + if (d->m_spriteEngine->maxFrames() % maxRowFrames) + x2 = ((d->m_spriteEngine->maxFrames() % maxRowFrames) - 1) * w; y2 = 1.0 - h; } @@ -701,15 +797,13 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node) } } - QQuickAnimatedSpriteMaterial *material = static_cast(node->material()); - material->animX1 = x1; - material->animY1 = y1; - material->animX2 = x2; - material->animY2 = y2; - material->animW = w; - material->animH = h; - material->animT = m_interpolate ? progress : 0.0; - material->texture->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); + node->setSourceA(QPoint(x1, y1)); + node->setSourceB(QPoint(x2, y2)); + node->setSpriteSize(QSize(w, h)); + node->setTime(d->m_interpolate ? progress : 0.0); + node->setSize(QSizeF(width(), height())); + node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); + node->update(); } QT_END_NAMESPACE diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h index 1e5981c1a4..cbd6524c40 100644 --- a/src/quick/items/qquickanimatedsprite_p.h +++ b/src/quick/items/qquickanimatedsprite_p.h @@ -62,6 +62,8 @@ class QQuickSprite; class QQuickSpriteEngine; class QSGGeometryNode; class QQuickAnimatedSpriteMaterial; +class QQuickAnimatedSpritePrivate; +class QSGSpriteNode; class Q_AUTOTEST_EXPORT QQuickAnimatedSprite : public QQuickItem { Q_OBJECT @@ -94,80 +96,21 @@ public: }; Q_ENUM(LoopParameters) - bool running() const - { - return m_running; - } - - bool interpolate() const - { - return m_interpolate; - } - - QUrl source() const - { - return m_sprite->source(); - } - - bool reverse() const - { - return m_sprite->reverse(); - } - - bool frameSync() const - { - return m_sprite->frameSync(); - } - - int frameCount() const - { - return m_sprite->frames(); - } - - int frameHeight() const - { - return m_sprite->frameHeight(); - } - - int frameWidth() const - { - return m_sprite->frameWidth(); - } - - int frameX() const - { - return m_sprite->frameX(); - } - - int frameY() const - { - return m_sprite->frameY(); - } - - qreal frameRate() const - { - return m_sprite->frameRate(); - } - - int frameDuration() const - { - return m_sprite->frameDuration(); - } - - int loops() const - { - return m_loops; - } - - bool paused() const - { - return m_paused; - } - - int currentFrame() const - { - return m_curFrame; - } + bool running() const; + bool interpolate() const; + QUrl source() const; + bool reverse() const; + bool frameSync() const; + int frameCount() const; + int frameHeight() const; + int frameWidth() const; + int frameX() const; + int frameY() const; + qreal frameRate() const; + int frameDuration() const; + int loops() const; + bool paused() const; + int currentFrame() const; Q_SIGNALS: @@ -176,27 +119,16 @@ Q_SIGNALS: void interpolateChanged(bool arg); void sourceChanged(QUrl arg); - void reverseChanged(bool arg); - void frameSyncChanged(bool arg); - void frameCountChanged(int arg); - void frameHeightChanged(int arg); - void frameWidthChanged(int arg); - void frameXChanged(int arg); - void frameYChanged(int arg); - void frameRateChanged(qreal arg); - void frameDurationChanged(int arg); - void loopsChanged(int arg); - void currentFrameChanged(int arg); public Q_SLOTS: @@ -207,157 +139,27 @@ public Q_SLOTS: void pause(); void resume(); - void setRunning(bool arg) - { - if (m_running != arg) { - if (m_running) - stop(); - else - start(); - } - } - - void setPaused(bool arg) - { - if (m_paused != arg) { - if (m_paused) - resume(); - else - pause(); - } - } - - void setInterpolate(bool arg) - { - if (m_interpolate != arg) { - m_interpolate = arg; - Q_EMIT interpolateChanged(arg); - } - } - - void setSource(QUrl arg) - { - if (m_sprite->m_source != arg) { - m_sprite->setSource(arg); - Q_EMIT sourceChanged(arg); - reloadImage(); - } - } - - void setReverse(bool arg) - { - if (m_sprite->m_reverse != arg) { - m_sprite->setReverse(arg); - Q_EMIT reverseChanged(arg); - } - } - - void setFrameSync(bool arg) - { - if (m_sprite->m_frameSync != arg) { - m_sprite->setFrameSync(arg); - Q_EMIT frameSyncChanged(arg); - if (m_running) - restart(); - } - } - - void setFrameCount(int arg) - { - if (m_sprite->m_frames != arg) { - m_sprite->setFrameCount(arg); - Q_EMIT frameCountChanged(arg); - reloadImage(); - } - } - - void setFrameHeight(int arg) - { - if (m_sprite->m_frameHeight != arg) { - m_sprite->setFrameHeight(arg); - Q_EMIT frameHeightChanged(arg); - reloadImage(); - } - } - - void setFrameWidth(int arg) - { - if (m_sprite->m_frameWidth != arg) { - m_sprite->setFrameWidth(arg); - Q_EMIT frameWidthChanged(arg); - reloadImage(); - } - } - - void setFrameX(int arg) - { - if (m_sprite->m_frameX != arg) { - m_sprite->setFrameX(arg); - Q_EMIT frameXChanged(arg); - reloadImage(); - } - } - - void setFrameY(int arg) - { - if (m_sprite->m_frameY != arg) { - m_sprite->setFrameY(arg); - Q_EMIT frameYChanged(arg); - reloadImage(); - } - } - - void setFrameRate(qreal arg) - { - if (m_sprite->m_frameRate != arg) { - m_sprite->setFrameRate(arg); - Q_EMIT frameRateChanged(arg); - if (m_running) - restart(); - } - } - - void setFrameDuration(int arg) - { - if (m_sprite->m_frameDuration != arg) { - m_sprite->setFrameDuration(arg); - Q_EMIT frameDurationChanged(arg); - if (m_running) - restart(); - } - } - - void resetFrameRate() - { - setFrameRate(-1.0); - } - - void resetFrameDuration() - { - setFrameDuration(-1); - } - - void setLoops(int arg) - { - if (m_loops != arg) { - m_loops = arg; - Q_EMIT loopsChanged(arg); - } - } - - void setCurrentFrame(int arg) //TODO-C: Probably only works when paused - { - if (m_curFrame != arg) { - m_curFrame = arg; - Q_EMIT currentFrameChanged(arg); //TODO-C Only emitted on manual advance! - update(); - } - } + void setRunning(bool arg); + void setPaused(bool arg); + void setInterpolate(bool arg); + void setSource(QUrl arg); + void setReverse(bool arg); + void setFrameSync(bool arg); + void setFrameCount(int arg); + void setFrameHeight(int arg); + void setFrameWidth(int arg); + void setFrameX(int arg); + void setFrameY(int arg); + void setFrameRate(qreal arg); + void setFrameDuration(int arg); + void resetFrameRate(); + void resetFrameDuration(); + void setLoops(int arg); + void setCurrentFrame(int arg); private Q_SLOTS: void createEngine(); - void sizeVertices(QSGGeometryNode *node); protected Q_SLOTS: void reset(); @@ -367,21 +169,13 @@ protected: QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; private: bool isCurrentFrameChangedConnected(); - void prepareNextFrame(QSGGeometryNode *node); + void prepareNextFrame(QSGSpriteNode *node); void reloadImage(); - QSGGeometryNode* buildNode(); - QQuickSprite* m_sprite; - QQuickSpriteEngine* m_spriteEngine; - QElapsedTimer m_timestamp; - int m_curFrame; - bool m_pleaseReset; - bool m_running; - bool m_paused; - bool m_interpolate; - QSizeF m_sheetSize; - int m_loops; - int m_curLoop; - int m_pauseOffset; + QSGSpriteNode* initNode(); + +private: + Q_DISABLE_COPY(QQuickAnimatedSprite) + Q_DECLARE_PRIVATE(QQuickAnimatedSprite) }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickanimatedsprite_p_p.h b/src/quick/items/qquickanimatedsprite_p_p.h new file mode 100644 index 0000000000..0e4a1e9066 --- /dev/null +++ b/src/quick/items/qquickanimatedsprite_p_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QQUICKANIMATEDSPRITE_P_P_H +#define QQUICKANIMATEDSPRITE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem_p.h" +#include "qquicksprite_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickAnimatedSprite; + +class QQuickAnimatedSpritePrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickAnimatedSprite) + +public: + QQuickAnimatedSpritePrivate() + : m_sprite(nullptr) + , m_spriteEngine(nullptr) + , m_curFrame(0) + , m_pleaseReset(false) + , m_running(true) + , m_paused(false) + , m_interpolate(true) + , m_loops(-1) + , m_curLoop(0) + , m_pauseOffset(0) + { + } + + QQuickSprite* m_sprite; + QQuickSpriteEngine* m_spriteEngine; + QElapsedTimer m_timestamp; + int m_curFrame; + bool m_pleaseReset; + bool m_running; + bool m_paused; + bool m_interpolate; + QSize m_sheetSize; + int m_loops; + int m_curLoop; + int m_pauseOffset; +}; + +QT_END_NAMESPACE + +#endif // QQUICKANIMATEDSPRITE_P_P_H diff --git a/src/quick/items/qquicksprite_p.h b/src/quick/items/qquicksprite_p.h index 1b8c8cee84..684d432f39 100644 --- a/src/quick/items/qquicksprite_p.h +++ b/src/quick/items/qquicksprite_p.h @@ -300,7 +300,7 @@ private Q_SLOTS: private: friend class QQuickImageParticle; - friend class QQuickSpriteSequence; + //friend class QQuickSpriteSequence; friend class QQuickAnimatedSprite; friend class QQuickSpriteEngine; friend class QQuickStochasticEngine; diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index 2137c8d8d5..083e02fe3f 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -372,7 +372,7 @@ void QQuickSpriteEngine::startAssemblingImage() m_startedImageAssembly = true; } -QImage QQuickSpriteEngine::assembledImage() +QImage QQuickSpriteEngine::assembledImage(int maxSize) { QQuickPixmap::Status stat = status(); if (!m_errorsPrinted && stat == QQuickPixmap::Error) { @@ -389,19 +389,7 @@ QImage QQuickSpriteEngine::assembledImage() int w = 0; m_maxFrames = 0; m_imageStateCount = 0; - int maxSize = 0; -#ifndef QT_NO_OPENGL - //If there is no current OpenGL Context - if (!QOpenGLContext::currentContext()) - return QImage(); - QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); -#else - maxSize = 2048; -#endif -#ifdef SPRITE_IMAGE_DEBUG - qDebug() << "MAX TEXTURE SIZE" << maxSize; -#endif foreach (QQuickSprite* state, m_sprites){ if (state->frames() > m_maxFrames) m_maxFrames = state->frames(); diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h index 65f58fafcb..1bdcc9a741 100644 --- a/src/quick/items/qquickspriteengine_p.h +++ b/src/quick/items/qquickspriteengine_p.h @@ -295,7 +295,7 @@ public: bool isError() { return status() == QQuickPixmap::Error; } QQuickPixmap::Status status();//Composed status of all Sprites void startAssemblingImage(); - QImage assembledImage(); + QImage assembledImage(int maxSize = 2048); private: int pseudospriteProgress(int,int,int*rd=0); diff --git a/src/quick/items/qquickspritesequence.cpp b/src/quick/items/qquickspritesequence.cpp index 6654c7a964..25a39f951a 100644 --- a/src/quick/items/qquickspritesequence.cpp +++ b/src/quick/items/qquickspritesequence.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qquickspritesequence_p.h" +#include "qquickspritesequence_p_p.h" #include "qquicksprite_p.h" #include "qquickspriteengine_p.h" #include @@ -54,117 +55,6 @@ QT_BEGIN_NAMESPACE -class QQuickSpriteSequenceMaterial : public QSGMaterial -{ -public: - QQuickSpriteSequenceMaterial(); - ~QQuickSpriteSequenceMaterial(); - QSGMaterialType *type() const Q_DECL_OVERRIDE{ static QSGMaterialType type; return &type; } - QSGMaterialShader *createShader() const Q_DECL_OVERRIDE; - int compare(const QSGMaterial *other) const Q_DECL_OVERRIDE - { - return this - static_cast(other); - } - - QSGTexture *texture; - - float animT; - float animX1; - float animY1; - float animX2; - float animY2; - float animW; - float animH; -}; - -QQuickSpriteSequenceMaterial::QQuickSpriteSequenceMaterial() - : texture(0) - , animT(0.0f) - , animX1(0.0f) - , animY1(0.0f) - , animX2(0.0f) - , animY2(0.0f) - , animW(1.0f) - , animH(1.0f) -{ - setFlag(Blending, true); -} - -QQuickSpriteSequenceMaterial::~QQuickSpriteSequenceMaterial() -{ - delete texture; -} - -#ifndef QT_NO_OPENGL -class SpriteSequenceMaterialData : public QSGMaterialShader -{ -public: - SpriteSequenceMaterialData() - : QSGMaterialShader() - { - setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/sprite.vert")); - setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/sprite.frag")); - } - - void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) Q_DECL_OVERRIDE - { - QQuickSpriteSequenceMaterial *m = static_cast(newEffect); - m->texture->bind(); - - program()->setUniformValue(m_opacity_id, state.opacity()); - program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT); - program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrix_id, state.combinedMatrix()); - } - - void initialize() Q_DECL_OVERRIDE { - m_matrix_id = program()->uniformLocation("qt_Matrix"); - m_opacity_id = program()->uniformLocation("qt_Opacity"); - m_animData_id = program()->uniformLocation("animData"); - m_animPos_id = program()->uniformLocation("animPos"); - } - - char const *const *attributeNames() const Q_DECL_OVERRIDE { - static const char *attr[] = { - "vPos", - "vTex", - 0 - }; - return attr; - } - - int m_matrix_id; - int m_opacity_id; - int m_animData_id; - int m_animPos_id; -}; -#endif - -QSGMaterialShader *QQuickSpriteSequenceMaterial::createShader() const -{ -#ifndef QT_NO_OPENGL - return new SpriteSequenceMaterialData; -#else - return nullptr; -#endif -} - -struct SpriteVertex { - float x; - float y; - float tx; - float ty; -}; - -struct SpriteVertices { - SpriteVertex v1; - SpriteVertex v2; - SpriteVertex v3; - SpriteVertex v4; -}; - /*! \qmltype SpriteSequence \instantiates QQuickSpriteSequence @@ -224,213 +114,185 @@ struct SpriteVertices { //TODO: Implicitly size element to size of first sprite? QQuickSpriteSequence::QQuickSpriteSequence(QQuickItem *parent) : - QQuickItem(parent) - , m_node(0) - , m_material(0) - , m_spriteEngine(0) - , m_curFrame(0) - , m_pleaseReset(false) - , m_running(true) - , m_interpolate(true) - , m_curStateIdx(0) + QQuickItem(*(new QQuickSpriteSequencePrivate), parent) { setFlag(ItemHasContents); connect(this, SIGNAL(runningChanged(bool)), this, SLOT(update())); - connect(this, SIGNAL(widthChanged()), - this, SLOT(sizeVertices())); - connect(this, SIGNAL(heightChanged()), - this, SLOT(sizeVertices())); } void QQuickSpriteSequence::jumpTo(const QString &sprite) { - if (!m_spriteEngine) + Q_D(QQuickSpriteSequence); + if (!d->m_spriteEngine) return; - m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true); + d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(sprite), 0, true); } void QQuickSpriteSequence::setGoalSprite(const QString &sprite) { - if (m_goalState != sprite){ - m_goalState = sprite; + Q_D(QQuickSpriteSequence); + if (d->m_goalState != sprite){ + d->m_goalState = sprite; emit goalSpriteChanged(sprite); - if (m_spriteEngine) - m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite)); + if (d->m_spriteEngine) + d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(sprite)); } } -QQmlListProperty QQuickSpriteSequence::sprites() +void QQuickSpriteSequence::setRunning(bool arg) { - return QQmlListProperty(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); + Q_D(QQuickSpriteSequence); + if (d->m_running != arg) { + d->m_running = arg; + Q_EMIT runningChanged(arg); + } } -void QQuickSpriteSequence::createEngine() +void QQuickSpriteSequence::setInterpolate(bool arg) { - //TODO: delay until component complete - if (m_spriteEngine) - delete m_spriteEngine; - if (m_sprites.count()) { - m_spriteEngine = new QQuickSpriteEngine(m_sprites, this); - if (!m_goalState.isEmpty()) - m_spriteEngine->setGoal(m_spriteEngine->stateIndex(m_goalState)); - } else { - m_spriteEngine = 0; + Q_D(QQuickSpriteSequence); + if (d->m_interpolate != arg) { + d->m_interpolate = arg; + Q_EMIT interpolateChanged(arg); } - reset(); } -static QSGGeometry::Attribute SpriteSequence_Attributes[] = { - QSGGeometry::Attribute::create(0, 2, QSGGeometry::TypeFloat, true), // pos - QSGGeometry::Attribute::create(1, 2, QSGGeometry::TypeFloat), // tex -}; - -static QSGGeometry::AttributeSet SpriteSequence_AttributeSet = +QQmlListProperty QQuickSpriteSequence::sprites() { - 2, // Attribute Count - (2+2) * sizeof(float), - SpriteSequence_Attributes -}; + Q_D(QQuickSpriteSequence); + return QQmlListProperty(this, &d->m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); +} -void QQuickSpriteSequence::sizeVertices() +bool QQuickSpriteSequence::running() const { - if (!m_node) - return; + Q_D(const QQuickSpriteSequence); + return d->m_running; +} - SpriteVertices *p = (SpriteVertices *) m_node->geometry()->vertexData(); - p->v1.x = 0; - p->v1.y = 0; +bool QQuickSpriteSequence::interpolate() const +{ + Q_D(const QQuickSpriteSequence); + return d->m_interpolate; +} - p->v2.x = width(); - p->v2.y = 0; +QString QQuickSpriteSequence::goalSprite() const +{ + Q_D(const QQuickSpriteSequence); + return d->m_goalState; +} - p->v3.x = 0; - p->v3.y = height(); +QString QQuickSpriteSequence::currentSprite() const +{ + Q_D(const QQuickSpriteSequence); + return d->m_curState; +} - p->v4.x = width(); - p->v4.y = height(); +void QQuickSpriteSequence::createEngine() +{ + Q_D(QQuickSpriteSequence); + //TODO: delay until component complete + if (d->m_spriteEngine) + delete d->m_spriteEngine; + if (d->m_sprites.count()) { + d->m_spriteEngine = new QQuickSpriteEngine(d->m_sprites, this); + if (!d->m_goalState.isEmpty()) + d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(d->m_goalState)); + } else { + d->m_spriteEngine = 0; + } + reset(); } -QSGGeometryNode* QQuickSpriteSequence::buildNode() +QSGSpriteNode *QQuickSpriteSequence::initNode() { - if (!m_spriteEngine) { + Q_D(QQuickSpriteSequence); + + if (!d->m_spriteEngine) { qmlInfo(this) << "No sprite engine..."; - return 0; - } else if (m_spriteEngine->status() == QQuickPixmap::Null) { - m_spriteEngine->startAssemblingImage(); + return nullptr; + } else if (d->m_spriteEngine->status() == QQuickPixmap::Null) { + d->m_spriteEngine->startAssemblingImage(); update();//Schedule another update, where we will check again - return 0; - } else if (m_spriteEngine->status() == QQuickPixmap::Loading) { + return nullptr; + } else if (d->m_spriteEngine->status() == QQuickPixmap::Loading) { update();//Schedule another update, where we will check again - return 0; + return nullptr; } - m_material = new QQuickSpriteSequenceMaterial(); - - QImage image = m_spriteEngine->assembledImage(); + QImage image = d->m_spriteEngine->assembledImage(d->sceneGraphRenderContext()->maxTextureSize()); if (image.isNull()) - return 0; - m_sheetSize = QSizeF(image.size()); - m_material->texture = window()->createTextureFromImage(image); - m_material->texture->setFiltering(QSGTexture::Linear); - m_spriteEngine->start(0); - m_material->animT = 0; - m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width(); - m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height(); - m_material->animX2 = m_material->animX1; - m_material->animY2 = m_material->animY1; - m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width(); - m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height(); - m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name(); - emit currentSpriteChanged(m_curState); - - int vCount = 4; - int iCount = 6; - QSGGeometry *g = new QSGGeometry(SpriteSequence_AttributeSet, vCount, iCount); - g->setDrawingMode(QSGGeometry::DrawTriangles); - - SpriteVertices *p = (SpriteVertices *) g->vertexData(); - QRectF texRect = m_material->texture->normalizedTextureSubRect(); - - p->v1.tx = texRect.topLeft().x(); - p->v1.ty = texRect.topLeft().y(); - - p->v2.tx = texRect.topRight().x(); - p->v2.ty = texRect.topRight().y(); - - p->v3.tx = texRect.bottomLeft().x(); - p->v3.ty = texRect.bottomLeft().y(); - - p->v4.tx = texRect.bottomRight().x(); - p->v4.ty = texRect.bottomRight().y(); - - quint16 *indices = g->indexDataAsUShort(); - indices[0] = 0; - indices[1] = 1; - indices[2] = 2; - indices[3] = 1; - indices[4] = 3; - indices[5] = 2; - - - m_timestamp.start(); - m_node = new QSGGeometryNode(); - m_node->setGeometry(g); - m_node->setMaterial(m_material); - m_node->setFlag(QSGGeometryNode::OwnsMaterial); - sizeVertices(); - return m_node; + return nullptr; + + QSGSpriteNode *node = d->sceneGraphContext()->createSpriteNode(); + + d->m_sheetSize = QSize(image.size()); + node->setTexture(window()->createTextureFromImage(image)); + d->m_spriteEngine->start(0); + node->setTime(0.0f); + node->setSourceA(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY())); + node->setSourceB(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY())); + node->setSpriteSize(QSize(d->m_spriteEngine->spriteWidth(), d->m_spriteEngine->spriteHeight())); + node->setSheetSize(d->m_sheetSize); + node->setSize(QSizeF(width(), height())); + + d->m_curState = d->m_spriteEngine->state(d->m_spriteEngine->curState())->name(); + emit currentSpriteChanged(d->m_curState); + d->m_timestamp.start(); + return node; } void QQuickSpriteSequence::reset() { - m_pleaseReset = true; + Q_D(QQuickSpriteSequence); + d->m_pleaseReset = true; } -QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *, UpdatePaintNodeData *) +QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { - if (m_pleaseReset) { - delete m_node; + Q_D(QQuickSpriteSequence); - m_node = 0; - m_material = 0; - m_pleaseReset = false; + if (d->m_pleaseReset) { + delete oldNode; + + oldNode = nullptr; + d->m_pleaseReset = false; } - prepareNextFrame(); + QSGSpriteNode *node = static_cast(oldNode); + if (!node) + node = initNode(); + + if (node) + prepareNextFrame(node); - if (m_running) { + if (d->m_running) { update(); - if (m_node) - m_node->markDirty(QSGNode::DirtyMaterial); } - return m_node; + return node; } -void QQuickSpriteSequence::prepareNextFrame() +void QQuickSpriteSequence::prepareNextFrame(QSGSpriteNode *node) { - if (m_node == 0) - m_node = buildNode(); - if (m_node == 0) //error creating node - return; + Q_D(QQuickSpriteSequence); - uint timeInt = m_timestamp.elapsed(); + uint timeInt = d->m_timestamp.elapsed(); qreal time = timeInt / 1000.; //Advance State - m_spriteEngine->updateSprites(timeInt); - if (m_curStateIdx != m_spriteEngine->curState()) { - m_curStateIdx = m_spriteEngine->curState(); - m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name(); - emit currentSpriteChanged(m_curState); - m_curFrame= -1; + d->m_spriteEngine->updateSprites(timeInt); + if (d->m_curStateIdx != d->m_spriteEngine->curState()) { + d->m_curStateIdx = d->m_spriteEngine->curState(); + d->m_curState = d->m_spriteEngine->state(d->m_spriteEngine->curState())->name(); + emit currentSpriteChanged(d->m_curState); + d->m_curFrame= -1; } //Advance Sprite - qreal animT = m_spriteEngine->spriteStart()/1000.0; - qreal frameCount = m_spriteEngine->spriteFrames(); - qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount; + qreal animT = d->m_spriteEngine->spriteStart()/1000.0; + qreal frameCount = d->m_spriteEngine->spriteFrames(); + qreal frameDuration = d->m_spriteEngine->spriteDuration()/frameCount; double frameAt; qreal progress; if (frameDuration > 0) { @@ -438,32 +300,32 @@ void QQuickSpriteSequence::prepareNextFrame() frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation progress = std::modf(frame,&frameAt); } else { - m_curFrame++; - if (m_curFrame >= frameCount){ - m_curFrame = 0; - m_spriteEngine->advance(); + d->m_curFrame++; + if (d->m_curFrame >= frameCount){ + d->m_curFrame = 0; + d->m_spriteEngine->advance(); } - frameAt = m_curFrame; + frameAt = d->m_curFrame; progress = 0; } - if (m_spriteEngine->sprite()->reverse()) - frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt; - qreal y = m_spriteEngine->spriteY() / m_sheetSize.height(); - qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width(); - qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height(); - qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width(); + if (d->m_spriteEngine->sprite()->reverse()) + frameAt = (d->m_spriteEngine->spriteFrames() - 1) - frameAt; + int y = d->m_spriteEngine->spriteY(); + int w = d->m_spriteEngine->spriteWidth(); + int h = d->m_spriteEngine->spriteHeight(); + int x1 = d->m_spriteEngine->spriteX(); x1 += frameAt * w; - qreal x2 = x1; + int x2 = x1; if (frameAt < (frameCount-1)) x2 += w; - m_material->animX1 = x1; - m_material->animY1 = y; - m_material->animX2 = x2; - m_material->animY2 = y; - m_material->animW = w; - m_material->animH = h; - m_material->animT = m_interpolate ? progress : 0.0; + node->setSourceA(QPoint(x1, y)); + node->setSourceB(QPoint(x2, y)); + node->setSpriteSize(QSize(w, h)); + node->setTime(d->m_interpolate ? progress : 0.0); + node->setSize(QSizeF(width(), height())); + node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); + node->update(); } QT_END_NAMESPACE diff --git a/src/quick/items/qquickspritesequence_p.h b/src/quick/items/qquickspritesequence_p.h index b4cc133821..34af110a98 100644 --- a/src/quick/items/qquickspritesequence_p.h +++ b/src/quick/items/qquickspritesequence_p.h @@ -59,8 +59,8 @@ QT_BEGIN_NAMESPACE class QSGContext; class QQuickSprite; class QQuickSpriteEngine; -class QSGGeometryNode; -class QQuickSpriteSequenceMaterial; +class QQuickSpriteSequencePrivate; +class QSGSpriteNode; class Q_AUTOTEST_EXPORT QQuickSpriteSequence : public QQuickItem { Q_OBJECT @@ -77,25 +77,10 @@ public: QQmlListProperty sprites(); - bool running() const - { - return m_running; - } - - bool interpolate() const - { - return m_interpolate; - } - - QString goalSprite() const - { - return m_goalState; - } - - QString currentSprite() const - { - return m_curState; - } + bool running() const; + bool interpolate() const; + QString goalSprite() const; + QString currentSprite() const; Q_SIGNALS: @@ -108,46 +93,23 @@ public Q_SLOTS: void jumpTo(const QString &sprite); void setGoalSprite(const QString &sprite); - - void setRunning(bool arg) - { - if (m_running != arg) { - m_running = arg; - Q_EMIT runningChanged(arg); - } - } - - void setInterpolate(bool arg) - { - if (m_interpolate != arg) { - m_interpolate = arg; - Q_EMIT interpolateChanged(arg); - } - } + void setRunning(bool arg); + void setInterpolate(bool arg); private Q_SLOTS: void createEngine(); - void sizeVertices(); protected: void reset(); QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; private: - void prepareNextFrame(); - QSGGeometryNode* buildNode(); - QSGGeometryNode *m_node; - QQuickSpriteSequenceMaterial *m_material; - QList m_sprites; - QQuickSpriteEngine* m_spriteEngine; - QTime m_timestamp; - int m_curFrame; - bool m_pleaseReset; - bool m_running; - bool m_interpolate; - QString m_goalState; - QString m_curState; - int m_curStateIdx; - QSizeF m_sheetSize; + void prepareNextFrame(QSGSpriteNode *node); + QSGSpriteNode* initNode(); + + +private: + Q_DISABLE_COPY(QQuickSpriteSequence) + Q_DECLARE_PRIVATE(QQuickSpriteSequence) }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickspritesequence_p_p.h b/src/quick/items/qquickspritesequence_p_p.h new file mode 100644 index 0000000000..4f352b5b69 --- /dev/null +++ b/src/quick/items/qquickspritesequence_p_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QQUICKSPRITESEQUENCE_P_P_H +#define QQUICKSPRITESEQUENCE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem_p.h" +#include "qquicksprite_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickSpriteSequence; + +class QQuickSpriteSequencePrivate :public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickSpriteSequence) +public: + QQuickSpriteSequencePrivate() + : m_spriteEngine(nullptr) + , m_curFrame(0) + , m_pleaseReset(false) + , m_running(true) + , m_interpolate(true) + , m_curStateIdx(0) + { + + } + QList m_sprites; + QQuickSpriteEngine* m_spriteEngine; + QTime m_timestamp; + int m_curFrame; + bool m_pleaseReset; + bool m_running; + bool m_interpolate; + QString m_goalState; + QString m_curState; + int m_curStateIdx; + QSize m_sheetSize; +}; + +QT_END_NAMESPACE + +#endif // QQUICKSPRITESEQUENCE_P_P_H diff --git a/src/quick/items/shaders/sprite.frag b/src/quick/items/shaders/sprite.frag deleted file mode 100644 index e1fcb0f006..0000000000 --- a/src/quick/items/shaders/sprite.frag +++ /dev/null @@ -1,12 +0,0 @@ -uniform sampler2D _qt_texture; -uniform lowp float qt_Opacity; - -varying highp vec4 fTexS; -varying lowp float progress; - -void main() -{ - gl_FragColor = mix(texture2D(_qt_texture, fTexS.xy), - texture2D(_qt_texture, fTexS.zw), - progress) * qt_Opacity; -} \ No newline at end of file diff --git a/src/quick/items/shaders/sprite.vert b/src/quick/items/shaders/sprite.vert deleted file mode 100644 index fc826f60b4..0000000000 --- a/src/quick/items/shaders/sprite.vert +++ /dev/null @@ -1,23 +0,0 @@ -attribute highp vec2 vPos; -attribute highp vec2 vTex; - -uniform highp vec3 animData;// w,h(premultiplied of anim), interpolation progress -uniform highp vec4 animPos;//x,y, x,y (two frames for interpolation) - -uniform highp mat4 qt_Matrix; - -varying highp vec4 fTexS; -varying lowp float progress; - -void main() -{ - progress = animData.z; - - // Calculate frame location in texture - fTexS.xy = animPos.xy + vTex.xy * animData.xy; - - // Next frame is also passed, for interpolation - fTexS.zw = animPos.zw + vTex.xy * animData.xy; - - gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0, 1); -} \ No newline at end of file diff --git a/src/quick/items/shaders/sprite_core.frag b/src/quick/items/shaders/sprite_core.frag deleted file mode 100644 index c1087a8754..0000000000 --- a/src/quick/items/shaders/sprite_core.frag +++ /dev/null @@ -1,16 +0,0 @@ -#version 150 core - -in vec4 fTexS; -in float progress; - -out vec4 fragColor; - -uniform sampler2D _qt_texture; -uniform float qt_Opacity; - -void main() -{ - fragColor = mix(texture(_qt_texture, fTexS.xy), - texture(_qt_texture, fTexS.zw), - progress) * qt_Opacity; -} \ No newline at end of file diff --git a/src/quick/items/shaders/sprite_core.vert b/src/quick/items/shaders/sprite_core.vert deleted file mode 100644 index 5027bf03fc..0000000000 --- a/src/quick/items/shaders/sprite_core.vert +++ /dev/null @@ -1,24 +0,0 @@ -#version 150 core - -in vec2 vPos; -in vec2 vTex; - -out vec4 fTexS; -out float progress; - -uniform vec3 animData; // w,h(premultiplied of anim), interpolation progress -uniform vec4 animPos; // x,y, x,y (two frames for interpolation) -uniform mat4 qt_Matrix; - -void main() -{ - progress = animData.z; - - // Calculate frame location in texture - fTexS.xy = animPos.xy + vTex.xy * animData.xy; - - // Next frame is also passed, for interpolation - fTexS.zw = animPos.zw + vTex.xy * animData.xy; - - gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0, 1); -} \ No newline at end of file diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp index d9a298f855..94e9c81f70 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp @@ -47,6 +47,7 @@ #include "qsgsoftwarepublicnodes_p.h" #include "qsgsoftwarelayer_p.h" #include "qsgsoftwarerenderer_p.h" +#include "qsgsoftwarespritenode_p.h" #include #include @@ -154,6 +155,11 @@ void QSGSoftwareRenderContext::renderNextFrame(QSGRenderer *renderer, uint fbo) renderer->renderScene(fbo); } +int QSGSoftwareRenderContext::maxTextureSize() const +{ + return 2048; +} + QSGRendererInterface *QSGSoftwareContext::rendererInterface(QSGRenderContext *renderContext) { Q_UNUSED(renderContext); @@ -175,6 +181,11 @@ QSGNinePatchNode *QSGSoftwareContext::createNinePatchNode() return new QSGSoftwareNinePatchNode; } +QSGSpriteNode *QSGSoftwareContext::createSpriteNode() +{ + return new QSGSoftwareSpriteNode; +} + QSGRendererInterface::GraphicsApi QSGSoftwareContext::graphicsApi() const { return Software; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h index 9a939a0948..3c3686c268 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h @@ -75,6 +75,7 @@ public: void renderNextFrame(QSGRenderer *renderer, uint fbo) override; QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const override; QSGRenderer *createRenderer() override; + int maxTextureSize() const override; bool m_initialized; }; @@ -96,6 +97,7 @@ public: QSGRectangleNode *createRectangleNode() override; QSGImageNode *createImageNode() override; QSGNinePatchNode *createNinePatchNode() override; + QSGSpriteNode *createSpriteNode() override; GraphicsApi graphicsApi() const override; ShaderType shaderType() const override; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 385b257e44..1b2a836dfa 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -45,6 +45,7 @@ #include "qsgsoftwarepublicnodes_p.h" #include "qsgsoftwarepainternode_p.h" #include "qsgsoftwarepixmaptexture_p.h" +#include "qsgsoftwarespritenode_p.h" #include #include @@ -89,6 +90,9 @@ QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *nod case QSGSoftwareRenderableNode::SimpleImage: m_handle.simpleImageNode = static_cast(node); break; + case QSGSoftwareRenderableNode::SpriteNode: + m_handle.spriteNode = static_cast(node); + break; case QSGSoftwareRenderableNode::Invalid: m_handle.simpleRectNode = nullptr; break; @@ -174,6 +178,10 @@ void QSGSoftwareRenderableNode::update() boundingRect = m_handle.simpleImageNode->rect().toRect(); break; + case QSGSoftwareRenderableNode::SpriteNode: + m_isOpaque = m_handle.spriteNode->isOpaque(); + boundingRect = m_handle.spriteNode->rect().toRect(); + break; default: break; } @@ -256,6 +264,9 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu case QSGSoftwareRenderableNode::SimpleImage: static_cast(m_handle.simpleImageNode)->paint(painter); break; + case QSGSoftwareRenderableNode::SpriteNode: + static_cast(m_handle.spriteNode)->paint(painter); + break; default: break; } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h index fcbea7391a..dc224be2c0 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h @@ -67,6 +67,7 @@ class QSGSoftwarePainterNode; class QSGSoftwareInternalRectangleNode; class QSGSoftwareGlyphNode; class QSGSoftwareNinePatchNode; +class QSGSoftwareSpriteNode; class QSGSoftwareRenderableNode { @@ -81,7 +82,8 @@ public: Glyph, NinePatch, SimpleRectangle, - SimpleImage + SimpleImage, + SpriteNode }; QSGSoftwareRenderableNode(NodeType type, QSGNode *node); @@ -123,6 +125,7 @@ private: QSGSoftwareNinePatchNode *ninePatchNode; QSGRectangleNode *simpleRectangleNode; QSGImageNode *simpleImageNode; + QSGSoftwareSpriteNode *spriteNode; }; const NodeType m_nodeType; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp index f13edb87fb..cb866bec12 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp @@ -180,6 +180,16 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRootNode *) { } +bool QSGSoftwareRenderableNodeUpdater::visit(QSGSpriteNode *node) +{ + return updateRenderableNode(QSGSoftwareRenderableNode::SpriteNode, node); +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGSpriteNode *) +{ + +} + void QSGSoftwareRenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRemoved) { m_opacityState.clear(); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h index d7c12e8b02..5bc241cce1 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h @@ -86,6 +86,8 @@ public: void endVisit(QSGGlyphNode *) override; bool visit(QSGRootNode *) override; void endVisit(QSGRootNode *) override; + bool visit(QSGSpriteNode *) override; + void endVisit(QSGSpriteNode *) override; void updateNodes(QSGNode *node, bool isNodeRemoved = false); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp index ede2005918..ac00127b35 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp @@ -140,6 +140,16 @@ void QSGSoftwareRenderListBuilder::endVisit(QSGRootNode *) { } +bool QSGSoftwareRenderListBuilder::visit(QSGSpriteNode *node) +{ + return addRenderableNode(node); +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGSpriteNode *) +{ + +} + bool QSGSoftwareRenderListBuilder::addRenderableNode(QSGNode *node) { auto renderableNode = m_renderer->renderableNode(node); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h index ce538f835f..e34cc81e23 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h @@ -80,6 +80,8 @@ public: void endVisit(QSGGlyphNode *) override; bool visit(QSGRootNode *) override; void endVisit(QSGRootNode *) override; + bool visit(QSGSpriteNode *) override; + void endVisit(QSGSpriteNode *) override; private: bool addRenderableNode(QSGNode *node); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp new file mode 100644 index 0000000000..ba7bbc2d11 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgsoftwarespritenode_p.h" +#include "qsgsoftwarepixmaptexture_p.h" +#include + +QT_BEGIN_NAMESPACE + +QSGSoftwareSpriteNode::QSGSoftwareSpriteNode() +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void QSGSoftwareSpriteNode::setTexture(QSGTexture *texture) +{ + m_texture = qobject_cast(texture); + markDirty(DirtyMaterial); +} + +void QSGSoftwareSpriteNode::setTime(float time) +{ + if (m_time != time) { + m_time = time; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareSpriteNode::setSourceA(const QPoint &source) +{ + if (m_sourceA != source) { + m_sourceA = source; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareSpriteNode::setSourceB(const QPoint &source) +{ + if (m_sourceB != source) { + m_sourceB = source; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareSpriteNode::setSpriteSize(const QSize &size) +{ + if (m_spriteSize != size) { + m_spriteSize = size; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareSpriteNode::setSheetSize(const QSize &size) +{ + if (m_sheetSize != size) { + m_sheetSize = size; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareSpriteNode::setSize(const QSizeF &size) +{ + if (m_size != size) { + m_size = size; + markDirty(DirtyGeometry); + } +} + +void QSGSoftwareSpriteNode::setFiltering(QSGTexture::Filtering filtering) +{ + Q_UNUSED(filtering); +} + +void QSGSoftwareSpriteNode::update() +{ +} + +void QSGSoftwareSpriteNode::paint(QPainter *painter) +{ + //Get the pixmap handle from the texture + if (!m_texture) + return; + + const QPixmap &pixmap = m_texture->pixmap(); + + // XXX try to do some kind of interpolation between sourceA and sourceB using time + painter->drawPixmap(QRectF(0, 0, m_size.width(), m_size.height()), + pixmap, + QRectF(m_sourceA, m_spriteSize)); +} + +bool QSGSoftwareSpriteNode::isOpaque() const +{ + return false; +} + +QRectF QSGSoftwareSpriteNode::rect() const +{ + return QRectF(0, 0, m_size.width(), m_size.height()); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h new file mode 100644 index 0000000000..284ed3dff5 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARESPRITENODE_H +#define QSGSOFTWARESPRITENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QSGSoftwarePixmapTexture; +class QSGSoftwareSpriteNode : public QSGSpriteNode +{ +public: + QSGSoftwareSpriteNode(); + + void setTexture(QSGTexture *texture) override; + void setTime(float time) override; + void setSourceA(const QPoint &source) override; + void setSourceB(const QPoint &source) override; + void setSpriteSize(const QSize &size) override; + void setSheetSize(const QSize &size) override; + void setSize(const QSizeF &size) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void update() override; + + void paint(QPainter *painter); + bool isOpaque() const; + QRectF rect() const; + +private: + + QSGSoftwarePixmapTexture *m_texture; + float m_time; + QPoint m_sourceA; + QPoint m_sourceB; + QSize m_spriteSize; + QSize m_sheetSize; + QSizeF m_size; + +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWARESPRITENODE_H diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri index a8ba77c147..1933a37d48 100644 --- a/src/quick/scenegraph/adaptations/software/software.pri +++ b/src/quick/scenegraph/adaptations/software/software.pri @@ -18,7 +18,8 @@ SOURCES += \ $$PWD/qsgsoftwarerenderlistbuilder.cpp \ $$PWD/qsgsoftwarerenderloop.cpp \ $$PWD/qsgsoftwarelayer.cpp \ - $$PWD/qsgsoftwareadaptation.cpp + $$PWD/qsgsoftwareadaptation.cpp \ + $$PWD/qsgsoftwarespritenode.cpp HEADERS += \ $$PWD/qsgsoftwarecontext_p.h \ @@ -36,4 +37,5 @@ HEADERS += \ $$PWD/qsgsoftwarerenderlistbuilder_p.h \ $$PWD/qsgsoftwarerenderloop_p.h \ $$PWD/qsgsoftwarelayer_p.h \ - $$PWD/qsgsoftwareadaptation_p.h + $$PWD/qsgsoftwareadaptation_p.h \ + $$PWD/qsgsoftwarespritenode_p.h diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index bdb8ebb0f1..00441e1544 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -81,6 +81,7 @@ class QSGPainterNode; class QSGInternalRectangleNode; class QSGGlyphNode; class QSGRootNode; +class QSGSpriteNode; class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx { @@ -106,6 +107,8 @@ public: virtual void endVisit(QSGGlyphNode *) = 0; virtual bool visit(QSGRootNode *) = 0; virtual void endVisit(QSGRootNode *) = 0; + virtual bool visit(QSGSpriteNode *) = 0; + virtual void endVisit(QSGSpriteNode *) = 0; void visitChildren(QSGNode *node); }; @@ -207,6 +210,23 @@ Q_SIGNALS: void scheduledUpdateCompleted(); }; +class Q_QUICK_PRIVATE_EXPORT QSGSpriteNode : public QSGVisitableNode +{ +public: + virtual void setTexture(QSGTexture *texture) = 0; + virtual void setTime(float time) = 0; + virtual void setSourceA(const QPoint &source) = 0; + virtual void setSourceB(const QPoint &source) = 0; + virtual void setSpriteSize(const QSize &size) = 0; + virtual void setSheetSize(const QSize &size) = 0; + virtual void setSize(const QSizeF &size) = 0; + virtual void setFiltering(QSGTexture::Filtering filtering) = 0; + + virtual void update() = 0; + + virtual void accept(QSGNodeVisitorEx *visitor) { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); } +}; + class Q_QUICK_PRIVATE_EXPORT QSGGuiThreadShaderEffectManager : public QObject { Q_OBJECT diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index e34d2c3ebf..43cf1c28ab 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -87,6 +87,7 @@ class QSGGuiThreadShaderEffectManager; class QSGRectangleNode; class QSGImageNode; class QSGNinePatchNode; +class QSGSpriteNode; Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP) Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION) @@ -126,6 +127,8 @@ public: virtual void setAttachToGraphicsContext(bool attach) { Q_UNUSED(attach); } + virtual int maxTextureSize() const = 0; + void registerFontengineForCleanup(QFontEngine *engine); Q_SIGNALS: @@ -174,6 +177,7 @@ public: virtual QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager(); virtual QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext, QSGGuiThreadShaderEffectManager *mgr); + virtual QSGSpriteNode *createSpriteNode() = 0; virtual QAnimationDriver *createAnimationDriver(QObject *parent); virtual QSize minimumFBOSize() const; diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp index 6324d84883..6964b74dc8 100644 --- a/src/quick/scenegraph/qsgdefaultcontext.cpp +++ b/src/quick/scenegraph/qsgdefaultcontext.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -257,6 +258,11 @@ QSGNinePatchNode *QSGDefaultContext::createNinePatchNode() return new QSGDefaultNinePatchNode; } +QSGSpriteNode *QSGDefaultContext::createSpriteNode() +{ + return new QSGDefaultSpriteNode; +} + QSGRendererInterface::GraphicsApi QSGDefaultContext::graphicsApi() const { return OpenGL; diff --git a/src/quick/scenegraph/qsgdefaultcontext_p.h b/src/quick/scenegraph/qsgdefaultcontext_p.h index 908934d28c..88db5e1e9a 100644 --- a/src/quick/scenegraph/qsgdefaultcontext_p.h +++ b/src/quick/scenegraph/qsgdefaultcontext_p.h @@ -76,6 +76,7 @@ public: QSGRectangleNode *createRectangleNode() override; QSGImageNode *createImageNode() override; QSGNinePatchNode *createNinePatchNode() override; + QSGSpriteNode *createSpriteNode() override; void setDistanceFieldEnabled(bool enabled); bool isDistanceFieldEnabled() const; diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h index bc653565cf..0aed46b658 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h +++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h @@ -93,7 +93,7 @@ public: static QSGDefaultRenderContext *from(QOpenGLContext *context); bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; } - int maxTextureSize() const { return m_maxTextureSize; } + int maxTextureSize() const override { return m_maxTextureSize; } protected: QOpenGLContext *m_gl; diff --git a/src/quick/scenegraph/qsgdefaultspritenode.cpp b/src/quick/scenegraph/qsgdefaultspritenode.cpp new file mode 100644 index 0000000000..89b26f8660 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultspritenode.cpp @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgdefaultspritenode_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +struct SpriteVertex { + float x; + float y; + float tx; + float ty; +}; + +struct SpriteVertices { + SpriteVertex v1; + SpriteVertex v2; + SpriteVertex v3; + SpriteVertex v4; +}; + +class QQuickSpriteMaterial : public QSGMaterial +{ +public: + QQuickSpriteMaterial(); + ~QQuickSpriteMaterial(); + QSGMaterialType *type() const override { static QSGMaterialType type; return &type; } + QSGMaterialShader *createShader() const override; + int compare(const QSGMaterial *other) const override + { + return this - static_cast(other); + } + + QSGTexture *texture; + + float animT; + float animX1; + float animY1; + float animX2; + float animY2; + float animW; + float animH; +}; + +QQuickSpriteMaterial::QQuickSpriteMaterial() + : texture(0) + , animT(0.0f) + , animX1(0.0f) + , animY1(0.0f) + , animX2(0.0f) + , animY2(0.0f) + , animW(1.0f) + , animH(1.0f) +{ + setFlag(Blending, true); +} + +QQuickSpriteMaterial::~QQuickSpriteMaterial() +{ + delete texture; +} + +class SpriteMaterialData : public QSGMaterialShader +{ +public: + SpriteMaterialData() + : QSGMaterialShader() + { + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/sprite.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/sprite.frag")); + } + + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) Q_DECL_OVERRIDE + { + QQuickSpriteMaterial *m = static_cast(newEffect); + m->texture->bind(); + + program()->setUniformValue(m_opacity_id, state.opacity()); + program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT); + program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + } + + void initialize() Q_DECL_OVERRIDE { + m_matrix_id = program()->uniformLocation("qt_Matrix"); + m_opacity_id = program()->uniformLocation("qt_Opacity"); + m_animData_id = program()->uniformLocation("animData"); + m_animPos_id = program()->uniformLocation("animPos"); + } + + char const *const *attributeNames() const Q_DECL_OVERRIDE { + static const char *attr[] = { + "vPos", + "vTex", + 0 + }; + return attr; + } + + int m_matrix_id; + int m_opacity_id; + int m_animData_id; + int m_animPos_id; +}; + +QSGMaterialShader *QQuickSpriteMaterial::createShader() const +{ + return new SpriteMaterialData; +} + +static QSGGeometry::Attribute Sprite_Attributes[] = { + QSGGeometry::Attribute::create(0, 2, QSGGeometry::TypeFloat, true), // pos + QSGGeometry::Attribute::create(1, 2, QSGGeometry::TypeFloat), // tex +}; + +static QSGGeometry::AttributeSet Sprite_AttributeSet = +{ + 2, // Attribute Count + (2+2) * sizeof(float), + Sprite_Attributes +}; + +QSGDefaultSpriteNode::QSGDefaultSpriteNode() + : m_material(new QQuickSpriteMaterial) + , m_geometryDirty(true) + , m_sheetSize(QSize(64, 64)) +{ + // Setup geometry data + m_geometry = new QSGGeometry(Sprite_AttributeSet, 4, 6); + m_geometry->setDrawingMode(QSGGeometry::DrawTriangles); + quint16 *indices = m_geometry->indexDataAsUShort(); + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 1; + indices[4] = 3; + indices[5] = 2; + + setGeometry(m_geometry); + setMaterial(m_material); + setFlag(OwnsGeometry, true); + setFlag(OwnsMaterial, true); +} + +void QSGDefaultSpriteNode::setTexture(QSGTexture *texture) +{ + m_material->texture = texture; + m_geometryDirty = true; + markDirty(DirtyMaterial); +} + +void QSGDefaultSpriteNode::setTime(float time) +{ + m_material->animT = time; + markDirty(DirtyMaterial); +} + +void QSGDefaultSpriteNode::setSourceA(const QPoint &source) +{ + if (m_sourceA != source) { + m_sourceA = source; + m_material->animX1 = static_cast(source.x()) / m_sheetSize.width(); + m_material->animY1 = static_cast(source.y()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGDefaultSpriteNode::setSourceB(const QPoint &source) +{ + if (m_sourceB != source) { + m_sourceB = source; + m_material->animX2 = static_cast(source.x()) / m_sheetSize.width(); + m_material->animY2 = static_cast(source.y()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGDefaultSpriteNode::setSpriteSize(const QSize &size) +{ + if (m_spriteSize != size) { + m_spriteSize = size; + m_material->animW = static_cast(size.width()) / m_sheetSize.width(); + m_material->animH = static_cast(size.height()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } + +} + +void QSGDefaultSpriteNode::setSheetSize(const QSize &size) +{ + if (m_sheetSize != size) { + m_sheetSize = size; + + // Update all dependent properties + m_material->animX1 = static_cast(m_sourceA.x()) / m_sheetSize.width(); + m_material->animY1 = static_cast(m_sourceA.y()) / m_sheetSize.height(); + m_material->animX2 = static_cast(m_sourceB.x()) / m_sheetSize.width(); + m_material->animY2 = static_cast(m_sourceB.y()) / m_sheetSize.height(); + m_material->animW = static_cast(m_spriteSize.width()) / m_sheetSize.width(); + m_material->animH = static_cast(m_spriteSize.height()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGDefaultSpriteNode::setSize(const QSizeF &size) +{ + if (m_size != size) { + m_size = size; + m_geometryDirty = true; + } +} + +void QSGDefaultSpriteNode::setFiltering(QSGTexture::Filtering filtering) +{ + m_material->texture->setFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGDefaultSpriteNode::update() +{ + if (m_geometryDirty) { + updateGeometry(); + m_geometryDirty = false; + } +} + +void QSGDefaultSpriteNode::updateGeometry() +{ + if (!m_material->texture) + return; + + SpriteVertices *p = (SpriteVertices *) m_geometry->vertexData(); + + QRectF texRect = m_material->texture->normalizedTextureSubRect(); + + p->v1.tx = texRect.topLeft().x(); + p->v1.ty = texRect.topLeft().y(); + + p->v2.tx = texRect.topRight().x(); + p->v2.ty = texRect.topRight().y(); + + p->v3.tx = texRect.bottomLeft().x(); + p->v3.ty = texRect.bottomLeft().y(); + + p->v4.tx = texRect.bottomRight().x(); + p->v4.ty = texRect.bottomRight().y(); + + p->v1.x = 0; + p->v1.y = 0; + + p->v2.x = m_size.width(); + p->v2.y = 0; + + p->v3.x = 0; + p->v3.y = m_size.height(); + + p->v4.x = m_size.width(); + p->v4.y = m_size.height(); + markDirty(DirtyGeometry); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultspritenode_p.h b/src/quick/scenegraph/qsgdefaultspritenode_p.h new file mode 100644 index 0000000000..cb76bf8d83 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultspritenode_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTSPRITENODE_H +#define QSGDEFAULTSPRITENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE +class QQuickSpriteMaterial; +class QSGDefaultSpriteNode : public QSGSpriteNode +{ +public: + QSGDefaultSpriteNode(); + + void setTexture(QSGTexture *texture) override; + void setTime(float time) override; + void setSourceA(const QPoint &source) override; + void setSourceB(const QPoint &source) override; + void setSpriteSize(const QSize &size) override; + void setSheetSize(const QSize &size) override; + void setSize(const QSizeF &size) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void update() override; +private: + void updateGeometry(); + + QQuickSpriteMaterial *m_material; + QSGGeometry *m_geometry; + bool m_geometryDirty; + QPoint m_sourceA; + QPoint m_sourceB; + QSize m_spriteSize; + QSize m_sheetSize; + QSizeF m_size; +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTSPRITENODE_H diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 5b13b7449a..4ef8b0f638 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -115,6 +115,7 @@ contains(QT_CONFIG, opengl(es1|es2)?) { $$PWD/qsgdefaultinternalrectanglenode.cpp \ $$PWD/qsgdefaultrendercontext.cpp \ $$PWD/qsgdefaultcontext.cpp \ + $$PWD/qsgdefaultspritenode.cpp \ $$PWD/util/qsgdefaultpainternode.cpp \ $$PWD/util/qsgdefaultrectanglenode.cpp \ $$PWD/util/qsgdefaultimagenode.cpp \ @@ -130,6 +131,7 @@ contains(QT_CONFIG, opengl(es1|es2)?) { $$PWD/qsgdefaultglyphnode_p_p.h \ $$PWD/qsgdefaultinternalimagenode_p.h \ $$PWD/qsgdefaultinternalrectanglenode_p.h \ + $$PWD/qsgdefaultspritenode_p.h \ $$PWD/qsgdefaultrendercontext_p.h \ $$PWD/qsgdefaultcontext_p.h \ $$PWD/util/qsgdefaultpainternode_p.h \ diff --git a/src/quick/scenegraph/scenegraph.qrc b/src/quick/scenegraph/scenegraph.qrc index ef6da71334..0687530be1 100644 --- a/src/quick/scenegraph/scenegraph.qrc +++ b/src/quick/scenegraph/scenegraph.qrc @@ -68,5 +68,9 @@ shaders/vertexcolor_core.vert shaders/visualization.vert shaders/visualization.frag + shaders/sprite.frag + shaders/sprite.vert + shaders/sprite_core.frag + shaders/sprite_core.vert diff --git a/src/quick/scenegraph/shaders/sprite.frag b/src/quick/scenegraph/shaders/sprite.frag new file mode 100644 index 0000000000..e1fcb0f006 --- /dev/null +++ b/src/quick/scenegraph/shaders/sprite.frag @@ -0,0 +1,12 @@ +uniform sampler2D _qt_texture; +uniform lowp float qt_Opacity; + +varying highp vec4 fTexS; +varying lowp float progress; + +void main() +{ + gl_FragColor = mix(texture2D(_qt_texture, fTexS.xy), + texture2D(_qt_texture, fTexS.zw), + progress) * qt_Opacity; +} \ No newline at end of file diff --git a/src/quick/scenegraph/shaders/sprite.vert b/src/quick/scenegraph/shaders/sprite.vert new file mode 100644 index 0000000000..fc826f60b4 --- /dev/null +++ b/src/quick/scenegraph/shaders/sprite.vert @@ -0,0 +1,23 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; + +uniform highp vec3 animData;// w,h(premultiplied of anim), interpolation progress +uniform highp vec4 animPos;//x,y, x,y (two frames for interpolation) + +uniform highp mat4 qt_Matrix; + +varying highp vec4 fTexS; +varying lowp float progress; + +void main() +{ + progress = animData.z; + + // Calculate frame location in texture + fTexS.xy = animPos.xy + vTex.xy * animData.xy; + + // Next frame is also passed, for interpolation + fTexS.zw = animPos.zw + vTex.xy * animData.xy; + + gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0, 1); +} \ No newline at end of file diff --git a/src/quick/scenegraph/shaders/sprite_core.frag b/src/quick/scenegraph/shaders/sprite_core.frag new file mode 100644 index 0000000000..c1087a8754 --- /dev/null +++ b/src/quick/scenegraph/shaders/sprite_core.frag @@ -0,0 +1,16 @@ +#version 150 core + +in vec4 fTexS; +in float progress; + +out vec4 fragColor; + +uniform sampler2D _qt_texture; +uniform float qt_Opacity; + +void main() +{ + fragColor = mix(texture(_qt_texture, fTexS.xy), + texture(_qt_texture, fTexS.zw), + progress) * qt_Opacity; +} \ No newline at end of file diff --git a/src/quick/scenegraph/shaders/sprite_core.vert b/src/quick/scenegraph/shaders/sprite_core.vert new file mode 100644 index 0000000000..5027bf03fc --- /dev/null +++ b/src/quick/scenegraph/shaders/sprite_core.vert @@ -0,0 +1,24 @@ +#version 150 core + +in vec2 vPos; +in vec2 vTex; + +out vec4 fTexS; +out float progress; + +uniform vec3 animData; // w,h(premultiplied of anim), interpolation progress +uniform vec4 animPos; // x,y, x,y (two frames for interpolation) +uniform mat4 qt_Matrix; + +void main() +{ + progress = animData.z; + + // Calculate frame location in texture + fTexS.xy = animPos.xy + vTex.xy * animData.xy; + + // Next frame is also passed, for interpolation + fTexS.zw = animPos.zw + vTex.xy * animData.xy; + + gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0, 1); +} \ No newline at end of file -- cgit v1.2.3 From b37d6969f1dfb83fd2e1285230d116e0cf076f7f Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 12 Jul 2016 14:54:54 +0200 Subject: D3D12: Sprite node Change-Id: I5ef2fb20beed372996642caf2cc5e02eaa886d79 Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/d3d12.pro | 6 +- src/plugins/scenegraph/d3d12/qsgd3d12context.cpp | 3 +- .../scenegraph/d3d12/qsgd3d12rendercontext.cpp | 3 +- .../scenegraph/d3d12/qsgd3d12spritenode.cpp | 314 +++++++++++++++++++++ .../scenegraph/d3d12/qsgd3d12spritenode_p.h | 90 ++++++ src/plugins/scenegraph/d3d12/shaders/shaders.pri | 11 + src/plugins/scenegraph/d3d12/shaders/sprite.hlsl | 43 +++ 7 files changed, 465 insertions(+), 5 deletions(-) create mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp create mode 100644 src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h create mode 100644 src/plugins/scenegraph/d3d12/shaders/sprite.hlsl diff --git a/src/plugins/scenegraph/d3d12/d3d12.pro b/src/plugins/scenegraph/d3d12/d3d12.pro index 5d1b7a4946..7f1c615de3 100644 --- a/src/plugins/scenegraph/d3d12/d3d12.pro +++ b/src/plugins/scenegraph/d3d12/d3d12.pro @@ -26,7 +26,8 @@ SOURCES += \ $$PWD/qsgd3d12layer.cpp \ $$PWD/qsgd3d12shadereffectnode.cpp \ $$PWD/qsgd3d12painternode.cpp \ - $$PWD/qsgd3d12publicnodes.cpp + $$PWD/qsgd3d12publicnodes.cpp \ + $$PWD/qsgd3d12spritenode.cpp NO_PCH_SOURCES += \ $$PWD/qsgd3d12engine.cpp @@ -50,7 +51,8 @@ HEADERS += \ $$PWD/qsgd3d12layer_p.h \ $$PWD/qsgd3d12shadereffectnode_p.h \ $$PWD/qsgd3d12painternode_p.h \ - $$PWD/qsgd3d12publicnodes_p.h + $$PWD/qsgd3d12publicnodes_p.h \ + $$PWD/qsgd3d12spritenode_p.h LIBS += -ldxgi -ld3d12 -ld3dcompiler diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp index 64c6313b42..f54ee6a053 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp @@ -46,6 +46,7 @@ #include "qsgd3d12shadereffectnode_p.h" #include "qsgd3d12painternode_p.h" #include "qsgd3d12publicnodes_p.h" +#include "qsgd3d12spritenode_p.h" QT_BEGIN_NAMESPACE @@ -129,7 +130,7 @@ QSGNinePatchNode *QSGD3D12Context::createNinePatchNode() QSGSpriteNode *QSGD3D12Context::createSpriteNode() { - return nullptr; + return new QSGD3D12SpriteNode; } QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp index e261cb6447..09129c954d 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp @@ -117,8 +117,7 @@ QSGRenderer *QSGD3D12RenderContext::createRenderer() int QSGD3D12RenderContext::maxTextureSize() const { - // XXX: Do it the correct way - return 4096; + return 16384; // D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION } void QSGD3D12RenderContext::renderNextFrame(QSGRenderer *renderer, uint fbo) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp new file mode 100644 index 0000000000..bad222dbaa --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgd3d12spritenode_p.h" +#include "qsgd3d12material_p.h" + +#include "vs_sprite.hlslh" +#include "ps_sprite.hlslh" + +QT_BEGIN_NAMESPACE + +struct SpriteVertex +{ + float x; + float y; + float tx; + float ty; +}; + +struct SpriteVertices +{ + SpriteVertex v1; + SpriteVertex v2; + SpriteVertex v3; + SpriteVertex v4; +}; + +class QSGD3D12SpriteMaterial : public QSGD3D12Material +{ +public: + QSGD3D12SpriteMaterial(); + ~QSGD3D12SpriteMaterial(); + + QSGMaterialType *type() const override { static QSGMaterialType type; return &type; } + + int compare(const QSGMaterial *other) const override + { + return this - static_cast(other); + } + + int constantBufferSize() const override; + void preparePipeline(QSGD3D12PipelineState *pipelineState) override; + UpdateResults updatePipeline(const QSGD3D12MaterialRenderState &state, + QSGD3D12PipelineState *pipelineState, + ExtraState *extraState, + quint8 *constantBuffer) override; + + QSGTexture *texture; + + float animT; + float animX1; + float animY1; + float animX2; + float animY2; + float animW; + float animH; +}; + +QSGD3D12SpriteMaterial::QSGD3D12SpriteMaterial() + : texture(nullptr), + animT(0.0f), + animX1(0.0f), + animY1(0.0f), + animX2(0.0f), + animY2(0.0f), + animW(1.0f), + animH(1.0f) +{ + setFlag(Blending, true); +} + +QSGD3D12SpriteMaterial::~QSGD3D12SpriteMaterial() +{ + delete texture; +} + +static const int SPRITE_CB_SIZE_0 = 16 * sizeof(float); // float4x4 +static const int SPRITE_CB_SIZE_1 = 4 * sizeof(float); // float4 +static const int SPRITE_CB_SIZE_2 = 3 * sizeof(float); // float3 +static const int SPRITE_CB_SIZE_3 = sizeof(float); // float +static const int SPRITE_CB_SIZE = SPRITE_CB_SIZE_0 + SPRITE_CB_SIZE_1 + SPRITE_CB_SIZE_2 + SPRITE_CB_SIZE_3; + +int QSGD3D12SpriteMaterial::constantBufferSize() const +{ + return QSGD3D12Engine::alignedConstantBufferSize(SPRITE_CB_SIZE); +} + +void QSGD3D12SpriteMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState) +{ + pipelineState->shaders.vs = g_VS_Sprite; + pipelineState->shaders.vsSize = sizeof(g_VS_Sprite); + pipelineState->shaders.ps = g_PS_Sprite; + pipelineState->shaders.psSize = sizeof(g_PS_Sprite); + + pipelineState->shaders.rootSig.textureViewCount = 1; +} + +QSGD3D12Material::UpdateResults QSGD3D12SpriteMaterial::updatePipeline(const QSGD3D12MaterialRenderState &state, + QSGD3D12PipelineState *, + ExtraState *, + quint8 *constantBuffer) +{ + QSGD3D12Material::UpdateResults r = UpdatedConstantBuffer; + quint8 *p = constantBuffer; + + if (state.isMatrixDirty()) + memcpy(p, state.combinedMatrix().constData(), SPRITE_CB_SIZE_0); + p += SPRITE_CB_SIZE_0; + + { + const float v[] = { animX1, animY1, animX2, animY2 }; + memcpy(p, v, SPRITE_CB_SIZE_1); + } + p += SPRITE_CB_SIZE_1; + + { + const float v[] = { animW, animH, animT }; + memcpy(p, v, SPRITE_CB_SIZE_2); + } + p += SPRITE_CB_SIZE_2; + + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(p, &opacity, SPRITE_CB_SIZE_3); + } + + texture->bind(); + + return r; +} + +static QSGGeometry::Attribute Sprite_Attributes[] = { + QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION), + QSGGeometry::Attribute::createWithSemantic(1, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD), +}; + +static QSGGeometry::AttributeSet Sprite_AttributeSet = { 2, 4 * sizeof(float), Sprite_Attributes }; + +QSGD3D12SpriteNode::QSGD3D12SpriteNode() + : m_material(new QSGD3D12SpriteMaterial) + , m_geometryDirty(true) + , m_sheetSize(QSize(64, 64)) +{ + m_geometry = new QSGGeometry(Sprite_AttributeSet, 4, 6); + m_geometry->setDrawingMode(QSGGeometry::DrawTriangles); + + quint16 *indices = m_geometry->indexDataAsUShort(); + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 1; + indices[4] = 3; + indices[5] = 2; + + setGeometry(m_geometry); + setMaterial(m_material); + setFlag(OwnsGeometry, true); + setFlag(OwnsMaterial, true); +} + +void QSGD3D12SpriteNode::setTexture(QSGTexture *texture) +{ + m_material->texture = texture; + m_geometryDirty = true; + markDirty(DirtyMaterial); +} + +void QSGD3D12SpriteNode::setTime(float time) +{ + m_material->animT = time; + markDirty(DirtyMaterial); +} + +void QSGD3D12SpriteNode::setSourceA(const QPoint &source) +{ + if (m_sourceA != source) { + m_sourceA = source; + m_material->animX1 = static_cast(source.x()) / m_sheetSize.width(); + m_material->animY1 = static_cast(source.y()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGD3D12SpriteNode::setSourceB(const QPoint &source) +{ + if (m_sourceB != source) { + m_sourceB = source; + m_material->animX2 = static_cast(source.x()) / m_sheetSize.width(); + m_material->animY2 = static_cast(source.y()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGD3D12SpriteNode::setSpriteSize(const QSize &size) +{ + if (m_spriteSize != size) { + m_spriteSize = size; + m_material->animW = static_cast(size.width()) / m_sheetSize.width(); + m_material->animH = static_cast(size.height()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGD3D12SpriteNode::setSheetSize(const QSize &size) +{ + if (m_sheetSize != size) { + m_sheetSize = size; + + // Update all dependent properties + m_material->animX1 = static_cast(m_sourceA.x()) / m_sheetSize.width(); + m_material->animY1 = static_cast(m_sourceA.y()) / m_sheetSize.height(); + m_material->animX2 = static_cast(m_sourceB.x()) / m_sheetSize.width(); + m_material->animY2 = static_cast(m_sourceB.y()) / m_sheetSize.height(); + m_material->animW = static_cast(m_spriteSize.width()) / m_sheetSize.width(); + m_material->animH = static_cast(m_spriteSize.height()) / m_sheetSize.height(); + + markDirty(DirtyMaterial); + } +} + +void QSGD3D12SpriteNode::setSize(const QSizeF &size) +{ + if (m_size != size) { + m_size = size; + m_geometryDirty = true; + } +} + +void QSGD3D12SpriteNode::setFiltering(QSGTexture::Filtering filtering) +{ + m_material->texture->setFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGD3D12SpriteNode::update() +{ + if (m_geometryDirty) { + m_geometryDirty = false; + updateGeometry(); + } +} + +void QSGD3D12SpriteNode::updateGeometry() +{ + if (!m_material->texture) + return; + + SpriteVertices *p = static_cast(m_geometry->vertexData()); + const QRectF texRect = m_material->texture->normalizedTextureSubRect(); + + p->v1.tx = texRect.topLeft().x(); + p->v1.ty = texRect.topLeft().y(); + + p->v2.tx = texRect.topRight().x(); + p->v2.ty = texRect.topRight().y(); + + p->v3.tx = texRect.bottomLeft().x(); + p->v3.ty = texRect.bottomLeft().y(); + + p->v4.tx = texRect.bottomRight().x(); + p->v4.ty = texRect.bottomRight().y(); + + p->v1.x = 0; + p->v1.y = 0; + + p->v2.x = m_size.width(); + p->v2.y = 0; + + p->v3.x = 0; + p->v3.y = m_size.height(); + + p->v4.x = m_size.width(); + p->v4.y = m_size.height(); + + markDirty(DirtyGeometry); +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h new file mode 100644 index 0000000000..265bec7c78 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGD3D12SPRITENODE_H +#define QSGD3D12SPRITENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QSGD3D12SpriteMaterial; + +class QSGD3D12SpriteNode : public QSGSpriteNode +{ +public: + QSGD3D12SpriteNode(); + + void setTexture(QSGTexture *texture) override; + void setTime(float time) override; + void setSourceA(const QPoint &source) override; + void setSourceB(const QPoint &source) override; + void setSpriteSize(const QSize &size) override; + void setSheetSize(const QSize &size) override; + void setSize(const QSizeF &size) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void update() override; + +private: + void updateGeometry(); + + QSGD3D12SpriteMaterial *m_material; + QSGGeometry *m_geometry; + bool m_geometryDirty; + QPoint m_sourceA; + QPoint m_sourceB; + QSize m_spriteSize; + QSize m_sheetSize; + QSizeF m_size; +}; + +QT_END_NAMESPACE + +#endif // QSGD3D12SPRITENODE_H diff --git a/src/plugins/scenegraph/d3d12/shaders/shaders.pri b/src/plugins/scenegraph/d3d12/shaders/shaders.pri index 41f0ee80f7..963f4c5d8c 100644 --- a/src/plugins/scenegraph/d3d12/shaders/shaders.pri +++ b/src/plugins/scenegraph/d3d12/shaders/shaders.pri @@ -108,6 +108,16 @@ shadereffectdefault_pshader.header = ps_shadereffectdefault.hlslh shadereffectdefault_pshader.entry = PS_DefaultShaderEffect shadereffectdefault_pshader.type = ps_5_0 +sprite_VSPS = $$PWD/sprite.hlsl +sprite_vshader.input = sprite_VSPS +sprite_vshader.header = vs_sprite.hlslh +sprite_vshader.entry = VS_Sprite +sprite_vshader.type = vs_5_0 +sprite_pshader.input = sprite_VSPS +sprite_pshader.header = ps_sprite.hlslh +sprite_pshader.entry = PS_Sprite +sprite_pshader.type = ps_5_0 + tdr_CS = $$PWD/tdr.hlsl tdr_cshader.input = tdr_CS tdr_cshader.header = cs_tdr.hlslh @@ -125,6 +135,7 @@ HLSL_SHADERS = \ textmask_vshader textmask_pshader24 textmask_pshader32 textmask_pshader8 \ styledtext_vshader styledtext_pshader outlinedtext_vshader outlinedtext_pshader \ shadereffectdefault_vshader shadereffectdefault_pshader \ + sprite_vshader sprite_pshader \ tdr_cshader load(hlsl_bytecode_header) diff --git a/src/plugins/scenegraph/d3d12/shaders/sprite.hlsl b/src/plugins/scenegraph/d3d12/shaders/sprite.hlsl new file mode 100644 index 0000000000..d4e3b066ee --- /dev/null +++ b/src/plugins/scenegraph/d3d12/shaders/sprite.hlsl @@ -0,0 +1,43 @@ +struct VSInput +{ + float4 position : POSITION; + float2 coord : TEXCOORD0; +}; + +cbuffer ConstantBuffer : register(b0) +{ + float4x4 mvp; + float4 animPos; + float3 animData; + float opacity; +}; + +struct PSInput +{ + float4 position : SV_POSITION; + float4 fTexS : TEXCOORD0; + float progress : TEXCOORD1; +}; + +Texture2D tex : register(t0); +SamplerState samp : register(s0); + +PSInput VS_Sprite(VSInput input) +{ + PSInput result; + + result.position = mul(mvp, input.position); + result.progress = animData.z; + + // Calculate frame location in texture + result.fTexS.xy = animPos.xy + input.coord.xy * animData.xy; + // Next frame is also passed, for interpolation + result.fTexS.zw = animPos.zw + input.coord.xy * animData.xy; + + return result; +} + +float4 PS_Sprite(PSInput input) : SV_TARGET +{ + return lerp(tex.Sample(samp, input.fTexS.xy), tex.Sample(samp, input.fTexS.zw), input.progress) * opacity; +} -- cgit v1.2.3 From ff8c0b053cc2085670a3013c58191d4886fda5ca Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 13 Jul 2016 12:03:13 +0200 Subject: Doc cleanup for D3D12 and SW Fix formatting, wrong class names, and remove sprites as an unsupported feature. Add a note about the glslcore file selector. Change-Id: I2caffc4485157d053bb0fffa47fed8ee1d506774 Reviewed-by: Andy Nichols --- src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc | 15 ++++++++------- src/quick/items/qquickshadereffect.cpp | 4 +++- src/quick/scenegraph/coreapi/qsgrendererinterface.cpp | 16 ++++++++-------- src/quick/scenegraph/coreapi/qsgrendernode.cpp | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index 3053196692..55c8c2c535 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -127,9 +127,6 @@ It is not possible to render particle effects with the Software adaptation. When possible, remove particles completely from the scene. Otherwise they will still require some processing, even though they are not visible. -\section2 Sprites -The Sprite item depends on OpenGL functions and will not be visible. - \section2 Rendering Text The text rendering with the Software adaptation is based on software rasterization and does not respond as well to transformations such as scaling @@ -243,8 +240,8 @@ GraphicsInfo.shaderCompilationType and GraphicsInfo.shaderSourceType. Unlike OpenGL, there is a QFileSelector with the extra selector \c hlsl used whenever opening a file. This allows easy creation of ShaderEffect items that are functional across both backends, for example by placing the GLSL source -code into "shaders/effect.frag", the HLSL source code or - preferably - -pre-compiled bytecode into "shaders/+hlsl/effect.frag", while simply writing +code into \c{shaders/effect.frag}, the HLSL source code or - preferably - +pre-compiled bytecode into \c{shaders/+hlsl/effect.frag}, while simply writing \c{fragmentShader: "qrc:shaders/effect.frag"} in QML. See the ShaderEffect documentation for more details. @@ -277,13 +274,17 @@ conversion on the CPU first. \section2 Unsupported Features -Particles, sprites, and other OpenGL-dependent tools like -QQuickFramebufferObject are not currently supported. +Particles and some other OpenGL-dependent utilities, like +QQuickFramebufferObject, are not currently supported. Like with the \l{qtquick-visualcanvas-adaptations-software.html}{Software adaptation}, text is always rendered using the native method. Distance field-based text rendering is not currently implemented. +The shader sources in the \l {Qt Graphical Effects} module have not been ported +to any format other than the OpenGL 2.0 compatible one, meaning the QML types +provided by that module are not currently functional with the D3D12 backend. + Texture atlases are not currently in use. The renderer may lack support for certain minor features, for example drawing diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index 57462219f9..5670696ce2 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -432,7 +432,9 @@ QT_BEGIN_NAMESPACE do its job. The selector-less version is the GLSL source, while the \c hlsl selector is used when running on the D3D12 backend. The file under \c{+hlsl} can then contain either HLSL source code or compiled bytecode - from the \c fxc tool. + from the \c fxc tool. Additionally, when using a version 3.2 or newer core + profile context with OpenGL, GLSL sources with a core profile compatible + syntax can be placed under \c{+glslcore}. \qml import QtQuick 2.8 // for GraphicsInfo diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp index e818d35dd7..04e71441f6 100644 --- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp @@ -88,27 +88,27 @@ QT_BEGIN_NAMESPACE /*! \enum QSGRendererInterface::ShaderType - \value UnknownShadingLanguage - Not yet known due to no window and scenegraph associated - \value GLSL - GLSL or GLSL ES - \value HLSL - HLSL + \value UnknownShadingLanguage Not yet known due to no window and scenegraph associated + \value GLSL GLSL or GLSL ES + \value HLSL HLSL */ /*! \enum QSGRendererInterface::ShaderCompilationType - \value RuntimeCompilation - Runtime compilation of shader source code is supported - \value OfflineCompilation - Pre-compiled bytecode supported + \value RuntimeCompilation Runtime compilation of shader source code is supported + \value OfflineCompilation Pre-compiled bytecode supported */ /*! \enum QSGRendererInterface::ShaderSourceType - \value ShaderSourceString - Shader source can be provided as a string in + \value ShaderSourceString Shader source can be provided as a string in the corresponding properties of ShaderEffect - \value ShaderSourceFile - Local or resource files containing shader source + \value ShaderSourceFile Local or resource files containing shader source code are supported - \value ShaderByteCode - Local or resource files containing shader bytecode are + \value ShaderByteCode Local or resource files containing shader bytecode are supported */ diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 6c61b35aa1..29e8251cb2 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE /*! \class QSGRenderNode - \brief The QSGMaterialShader class represents a set of custom rendering commands + \brief The QSGRenderNode class represents a set of custom rendering commands targeting the graphics API that is in use by the scenegraph. \inmodule QtQuick \since 5.8 -- cgit v1.2.3 From 989592bf5970471a7ff32a7b740172c8688e2171 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 13 Jul 2016 16:51:12 +0200 Subject: D3D12: Support translucent windows via DirectComposition Change-Id: I1b63db07ade1ae43c67352b4d875d5a3e55105f2 Reviewed-by: Andy Nichols --- config.tests/d3d12/d3d12.pro | 2 +- src/plugins/scenegraph/d3d12/d3d12.pro | 2 +- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 124 ++++++++++++++++----- src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h | 2 +- src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h | 8 +- .../scenegraph/d3d12/qsgd3d12renderloop.cpp | 10 +- .../d3d12/qsgd3d12threadedrenderloop.cpp | 5 +- .../doc/src/concepts/visualcanvas/adaptations.qdoc | 29 ++++- 8 files changed, 141 insertions(+), 41 deletions(-) diff --git a/config.tests/d3d12/d3d12.pro b/config.tests/d3d12/d3d12.pro index 24c5991a4b..451e7427b9 100644 --- a/config.tests/d3d12/d3d12.pro +++ b/config.tests/d3d12/d3d12.pro @@ -1,4 +1,4 @@ SOURCES = d3d12.cpp CONFIG -= qt dylib CONFIG += console -LIBS += -ldxgi -ld3d12 +LIBS += -ldxgi -ld3d12 -ld3dcompiler -ldcomp diff --git a/src/plugins/scenegraph/d3d12/d3d12.pro b/src/plugins/scenegraph/d3d12/d3d12.pro index 7f1c615de3..9cca5458ee 100644 --- a/src/plugins/scenegraph/d3d12/d3d12.pro +++ b/src/plugins/scenegraph/d3d12/d3d12.pro @@ -54,7 +54,7 @@ HEADERS += \ $$PWD/qsgd3d12publicnodes_p.h \ $$PWD/qsgd3d12spritenode_p.h -LIBS += -ldxgi -ld3d12 -ld3dcompiler +LIBS += -ldxgi -ld3d12 -ld3dcompiler -ldcomp include($$PWD/shaders/shaders.pri) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 640c2f0d3c..0800bdb8ad 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -318,14 +318,14 @@ QSGD3D12Engine::~QSGD3D12Engine() delete d; } -bool QSGD3D12Engine::attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples) +bool QSGD3D12Engine::attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples, bool alpha) { if (d->isInitialized()) { qWarning("QSGD3D12Engine: Cannot attach active engine to window"); return false; } - d->initialize(window, size, dpr, surfaceFormatSamples); + d->initialize(window, size, dpr, surfaceFormatSamples, alpha); return d->isInitialized(); } @@ -669,6 +669,11 @@ void QSGD3D12EnginePrivate::releaseResources() commandQueue = nullptr; copyCommandQueue = nullptr; + + dcompTarget = nullptr; + dcompVisual = nullptr; + dcompDevice = nullptr; + swapChain = nullptr; delete presentFence; @@ -681,7 +686,7 @@ void QSGD3D12EnginePrivate::releaseResources() // 'window' must be kept, may just be a device loss } -void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples) +void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples, bool alpha) { if (initialized) return; @@ -690,6 +695,7 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int windowSize = size; windowDpr = dpr; windowSamples = qMax(1, surfaceFormatSamples); // may be -1 or 0, whereas windowSamples is uint and >= 1 + windowAlpha = alpha; swapChainBufferCount = qMin(qEnvironmentVariableIntValue("QT_D3D_BUFFER_COUNT"), MAX_SWAP_CHAIN_BUFFER_COUNT); if (swapChainBufferCount < 2) @@ -764,28 +770,91 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int #ifndef Q_OS_WINRT HWND hwnd = reinterpret_cast(w); - DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; - swapChainDesc.BufferCount = swapChainBufferCount; - swapChainDesc.BufferDesc.Width = windowSize.width() * windowDpr; - swapChainDesc.BufferDesc.Height = windowSize.height() * windowDpr; - swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // D3D12 requires the flip model - swapChainDesc.OutputWindow = hwnd; - swapChainDesc.SampleDesc.Count = 1; // Flip does not support MSAA so no choice here - swapChainDesc.Windowed = TRUE; - if (waitableSwapChainMaxLatency) - swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + if (windowAlpha) { + // Go through DirectComposition for semi-transparent windows since the + // traditional approaches won't fly with flip model swapchains. + HRESULT hr = DCompositionCreateDevice(nullptr, IID_PPV_ARGS(&dcompDevice)); + if (SUCCEEDED(hr)) { + hr = dcompDevice->CreateTargetForHwnd(hwnd, true, &dcompTarget); + if (SUCCEEDED(hr)) { + hr = dcompDevice->CreateVisual(&dcompVisual); + if (FAILED(hr)) { + qWarning("Failed to create DirectComposition visual: 0x%x", hr); + windowAlpha = false; + } + } else { + qWarning("Failed to create DirectComposition target: 0x%x", hr); + windowAlpha = false; + } + } else { + qWarning("Failed to create DirectComposition device: 0x%x", hr); + windowAlpha = false; + } + } - ComPtr baseSwapChain; - HRESULT hr = dev->dxgi()->CreateSwapChain(commandQueue.Get(), &swapChainDesc, &baseSwapChain); - if (FAILED(hr)) { - qWarning("Failed to create swap chain: 0x%x", hr); - return; + if (windowAlpha) { + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; + swapChainDesc.Width = windowSize.width() * windowDpr; + swapChainDesc.Height = windowSize.height() * windowDpr; + swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = swapChainBufferCount; + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; + if (waitableSwapChainMaxLatency) + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + + ComPtr baseSwapChain; + HRESULT hr = dev->dxgi()->CreateSwapChainForComposition(commandQueue.Get(), &swapChainDesc, nullptr, &baseSwapChain); + if (SUCCEEDED(hr)) { + if (SUCCEEDED(baseSwapChain.As(&swapChain))) { + hr = dcompVisual->SetContent(swapChain.Get()); + if (SUCCEEDED(hr)) { + hr = dcompTarget->SetRoot(dcompVisual.Get()); + if (FAILED(hr)) { + qWarning("SetRoot failed for DirectComposition target: 0x%x", hr); + windowAlpha = false; + } + } else { + qWarning("SetContent failed for DirectComposition visual: 0x%x", hr); + windowAlpha = false; + } + } else { + qWarning("Failed to cast swap chain"); + windowAlpha = false; + } + } else { + qWarning("Failed to create swap chain for composition: 0x%x", hr); + windowAlpha = false; + } } - if (FAILED(baseSwapChain.As(&swapChain))) { - qWarning("Failed to cast swap chain"); - return; + + if (!windowAlpha) { + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferCount = swapChainBufferCount; + swapChainDesc.BufferDesc.Width = windowSize.width() * windowDpr; + swapChainDesc.BufferDesc.Height = windowSize.height() * windowDpr; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // D3D12 requires the flip model + swapChainDesc.OutputWindow = hwnd; + swapChainDesc.SampleDesc.Count = 1; // Flip does not support MSAA so no choice here + swapChainDesc.Windowed = TRUE; + if (waitableSwapChainMaxLatency) + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + + ComPtr baseSwapChain; + HRESULT hr = dev->dxgi()->CreateSwapChain(commandQueue.Get(), &swapChainDesc, &baseSwapChain); + if (FAILED(hr)) { + qWarning("Failed to create swap chain: 0x%x", hr); + return; + } + if (FAILED(baseSwapChain.As(&swapChain))) { + qWarning("Failed to cast swap chain"); + return; + } } dev->dxgi()->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER); @@ -810,7 +879,7 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int return; } if (FAILED(baseSwapChain.As(&swapChain))) { - qWarning("Failed to case swap chain"); + qWarning("Failed to cast swap chain"); return; } @@ -1240,7 +1309,7 @@ void QSGD3D12EnginePrivate::updateBuffer(Buffer *buf) void QSGD3D12EnginePrivate::ensureDevice() { if (!initialized && window) - initialize(window, windowSize, windowDpr, windowSamples); + initialize(window, windowSize, windowDpr, windowSamples, windowAlpha); } void QSGD3D12EnginePrivate::beginFrame() @@ -2092,6 +2161,11 @@ void QSGD3D12EnginePrivate::present() return; } +#ifndef Q_OS_WINRT + if (dcompDevice) + dcompDevice->Commit(); +#endif + ++presentFrameIndex; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h index ad89696bbe..72007b96db 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h @@ -287,7 +287,7 @@ public: QSGD3D12Engine(); ~QSGD3D12Engine(); - bool attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples); + bool attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples, bool alpha); void releaseResources(); bool hasResources() const; void setWindowSize(const QSize &size, float dpr); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h index 6cd7cbd24e..2b1e3dc7b7 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h @@ -56,6 +56,7 @@ #include #include +#include #include using namespace Microsoft::WRL; @@ -130,7 +131,7 @@ struct QSGD3D12CPUWaitableFence class QSGD3D12EnginePrivate : public QSGD3D12DeviceManager::DeviceLossObserver { public: - void initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples); + void initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples, bool alpha); bool isInitialized() const { return initialized; } void releaseResources(); void setWindowSize(const QSize &size, float dpr); @@ -269,6 +270,7 @@ private: QSize windowSize; float windowDpr; uint windowSamples; + bool windowAlpha; int swapChainBufferCount; int frameInFlightCount; int waitableSwapChainMaxLatency; @@ -432,6 +434,10 @@ private: }; DeviceLossTester devLossTest; + + ComPtr dcompDevice; + ComPtr dcompTarget; + ComPtr dcompVisual; }; inline uint qHash(const QSGD3D12EnginePrivate::PersistentFrameData::PendingRelease &pr, uint seed = 0) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 551133e7bb..f17ed10c94 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -164,12 +164,13 @@ void QSGD3D12RenderLoop::exposeWindow(QQuickWindow *window) m_windows[window] = data; const int samples = window->format().samples(); + const bool alpha = window->format().alphaBufferSize() > 0; const qreal dpr = window->effectiveDevicePixelRatio(); if (Q_UNLIKELY(debug_loop())) - qDebug() << "initializing D3D12 engine" << window << window->size() << dpr << samples; + qDebug() << "initializing D3D12 engine" << window << window->size() << dpr << samples << alpha; - data.engine->attachToWindow(window->winId(), window->size(), dpr, samples); + data.engine->attachToWindow(window->winId(), window->size(), dpr, samples, alpha); } void QSGD3D12RenderLoop::obscureWindow(QQuickWindow *window) @@ -437,10 +438,11 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) if (needsWindow) { // Must only ever get here when there is no window or releaseResources() has been called. const int samples = window->format().samples(); + const bool alpha = window->format().alphaBufferSize() > 0; const qreal dpr = window->effectiveDevicePixelRatio(); if (Q_UNLIKELY(debug_loop())) - qDebug() << "sync - reinitializing D3D12 engine" << window << window->size() << dpr << samples; - data.engine->attachToWindow(window->winId(), window->size(), dpr, samples); + qDebug() << "sync - reinitializing D3D12 engine" << window << window->size() << dpr << samples << alpha; + data.engine->attachToWindow(window->winId(), window->size(), dpr, samples, alpha); } // Recover from device loss. diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp index 26efa171f5..11b88cfd34 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp @@ -331,10 +331,11 @@ bool QSGD3D12RenderThread::event(QEvent *e) if (needsWindow) { // Must only ever get here when there is no window or releaseResources() has been called. const int samples = wme->window->format().samples(); + const bool alpha = wme->window->format().alphaBufferSize() > 0; if (Q_UNLIKELY(debug_loop())) qDebug() << "RT - WM_RequestSync - initializing D3D12 engine" << wme->window - << wme->size << wme->dpr << samples; - engine->attachToWindow(wme->window->winId(), wme->size, wme->dpr, samples); + << wme->size << wme->dpr << samples << alpha; + engine->attachToWindow(wme->window->winId(), wme->size, wme->dpr, samples, alpha); } exposedWindow = wme->window; engine->setWindowSize(wme->size, wme->dpr); diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index 55c8c2c535..57f1495c8e 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -249,12 +249,29 @@ See the ShaderEffect documentation for more details. \section2 Multisample Render Targets The Direct3D 12 adaptation ignores the QSurfaceFormat set on the QQuickWindow -or QQuickView (or set via QSurfaceFormat::setDefaultFormat()), with one -exception: QSurfaceFormat::samples() is still taken into account. When the -value is greater than 1, multisample offscreen render targets will be created -with the specified sample count and a quality of the maximum supported quality -level. The backend automatically performs resolving into the non-multisample -swapchain buffers after each frame. +or QQuickView (or set via QSurfaceFormat::setDefaultFormat()), with two +exceptions: QSurfaceFormat::samples() and QSurfaceFormat::alphaBufferSize() are +still taken into account. When the samples value is greater than 1, multisample +offscreen render targets will be created with the specified sample count and a +quality of the maximum supported quality level. The backend automatically +performs resolving into the non-multisample swapchain buffers after each frame. + +\section2 Semi-transparent windows + +When the alpha channel is enabled either via +QQuickWindow::setDefaultAlphaBuffer() or by setting alphaBufferSize to a +non-zero value in the window's QSurfaceFormat or in the global format managed +by QSurfaceFormat::setDefaultFormat(), the D3D12 backend will create a +swapchain for composition and go through DirectComposition since the flip model +swapchain (which is mandatory) would not support transparency otherwise. + +It is therefore important not to unneccessarily request an alpha channel. When +the alphaBufferSize is 0 or the default -1, all these extra steps can be +avoided and the traditional window-based swapchain is sufficient. + +This is not relevant on WinRT because there the backend always uses a +composition swapchain which is associated with the ISwapChainPanel that backs +QWindow on that platform. \section2 Mipmaps -- cgit v1.2.3 From 6761da79633a8838c4b79b335a58773203ecc559 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 29 Jun 2016 16:03:04 +0200 Subject: QML: QQmlPropertyCache: prevent repeated QMeta* calls By "caching" the (constant) results in the methods. Change-Id: I17e99c4fb902619cc1fe21ef772c8d1772ba9428 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 614a485df6..502557fa9f 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -230,9 +230,10 @@ void QQmlPropertyData::lazyLoad(const QMetaMethod &m) flags |= NotFullyResolved; } - if (m.parameterCount()) { + const int paramCount = m.parameterCount(); + if (paramCount) { flags |= HasArguments; - if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { + if ((paramCount == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { flags |= IsV4Function; } } @@ -509,11 +510,12 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, bool hasFastProperty = false; for (int ii = 0; ii < classInfoCount; ++ii) { int idx = ii + classInfoOffset; - - if (0 == qstrcmp(metaObject->classInfo(idx).name(), "qt_HasQmlAccessors")) { + QMetaClassInfo mci = metaObject->classInfo(idx); + const char *name = mci.name(); + if (0 == qstrcmp(name, "qt_HasQmlAccessors")) { hasFastProperty = true; - } else if (0 == qstrcmp(metaObject->classInfo(idx).name(), "DefaultProperty")) { - _defaultPropertyName = QString::fromUtf8(metaObject->classInfo(idx).value()); + } else if (0 == qstrcmp(name, "DefaultProperty")) { + _defaultPropertyName = QString::fromUtf8(mci.value()); } } @@ -804,11 +806,11 @@ inline bool contextHasNoExtensions(QQmlContextData *context) return (!context->parent || !context->parent->imports); } -inline int maximumIndexForProperty(QQmlPropertyData *prop, const QQmlVMEMetaObject *vmemo) +inline int maximumIndexForProperty(QQmlPropertyData *prop, const int methodCount, const int signalCount, const int propertyCount) { - return (prop->isFunction() ? vmemo->methodCount() - : prop->isSignalHandler() ? vmemo->signalCount() - : vmemo->propertyCount()); + return prop->isFunction() ? methodCount + : prop->isSignalHandler() ? signalCount + : propertyCount; } } @@ -835,11 +837,15 @@ QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, } if (vmemo) { + const int methodCount = vmemo->methodCount(); + const int signalCount = vmemo->signalCount(); + const int propertyCount = vmemo->propertyCount(); + // Ensure that the property we resolve to is accessible from this meta-object do { const StringCache::mapped_type &property(it.value()); - if (property.first < maximumIndexForProperty(property.second, vmemo)) { + if (property.first < maximumIndexForProperty(property.second, methodCount, signalCount, propertyCount)) { // This property is available in the specified context if (property.second->isFunction() || property.second->isSignalHandler()) { // Prefer the earlier resolution -- cgit v1.2.3 From a1f6409d1c34238405bbd73d067a39091bf844f7 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 7 Jul 2016 13:21:05 +0200 Subject: Fix missing closing parenthesis in TextEdit docs Change-Id: Ib1872e6353a49bc12a6a714a79873e8c0a7032f7 Reviewed-by: Robin Burchell --- src/quick/items/qquicktextedit.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 8282c09770..3ab7f358ff 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1435,8 +1435,8 @@ void QQuickTextEdit::setSelectByKeyboard(bool on) If true, the user can use the mouse to select text in some platform-specific way. Note that for some platforms this may - not be an appropriate interaction (eg. may conflict with how - the text needs to behave inside a Flickable. + not be an appropriate interaction; it may conflict with how + the text needs to behave inside a Flickable, for example. */ bool QQuickTextEdit::selectByMouse() const { -- cgit v1.2.3 From c29ec2f509580a82747f71b8767c14b29aa91e50 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 14 Jul 2016 14:42:11 +0200 Subject: QQuickPointerEvent::touchPointById: check iterator against constEnd for consistency with how we started it... Change-Id: I73669a263a1e9c2a77dd0bb217f5163ed0fc2561 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index ef7d0218a5..ad5a007b37 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -586,7 +586,7 @@ const QTouchEvent::TouchPoint *QQuickPointerEvent::touchPointById(int pointId) c auto it = std::find_if(tps.constBegin(), tps.constEnd(), [pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } ); // return the pointer to the actual TP in QTouchEvent::_touchPoints - return (it == tps.end() ? nullptr : it.operator->()); + return (it == tps.constEnd() ? nullptr : it.operator->()); } /*! -- cgit v1.2.3 From 62a90bcd8304fde9072c96f7f81cb09b4fa878dc Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 14 Jul 2016 16:21:13 +0200 Subject: QQuickPointerDevice::CapabilityFlag: enforce sync with QTouchDevice The first few values in QQuickPointerDevice::CapabilityFlag must be kept in sync with QTouchDevice::CapabilityFlag, so make it explicit. Change-Id: I0c1147319e057df26a8a732a4e494b762a1f5301 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents_p_p.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index e5f2ac3fb7..0a306e8b46 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -262,10 +262,10 @@ public: Q_FLAG(PointerTypes) enum CapabilityFlag { - Position = 0x0001, - Area = 0x0002, - Pressure = 0x0004, - Velocity = 0x0008, + Position = QTouchDevice::Position, + Area = QTouchDevice::Area, + Pressure = QTouchDevice::Pressure, + Velocity = QTouchDevice::Velocity, // some bits reserved in case we need more of QTouchDevice::Capabilities Scroll = 0x0100, // mouse has a wheel, or there is OS-level scroll gesture recognition (dubious?) Hover = 0x0200, -- cgit v1.2.3 From be2fe9b60c2c6c27ce144123dec6d284171c6fec Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Jul 2016 16:30:24 +0200 Subject: Remove some dead code in the JIT Some internal API in the JIT that explicitly uses TrustedImmPtr is not used anymore. To prevent future accidental usage and thus introduce non-relocatable code, let's remove the convenience API. Change-Id: I7f1ed23808449991b5765467845c66fe116ca8a0 Reviewed-by: Lars Knoll --- src/qml/jit/qv4assembler_p.h | 56 +------------------------------------------- 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index d53e0b0454..ae9302316d 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -340,11 +340,6 @@ public: IR::Expr *value; }; - struct ReentryBlock { - ReentryBlock(IR::BasicBlock *b) : block(b) {} - IR::BasicBlock *block; - }; - void callAbsolute(const char* /*functionName*/, const LookupCall &lookupCall) { call(lookupCall.addr); @@ -433,13 +428,6 @@ public: move(source, dest); } - void loadArgumentInRegister(TrustedImmPtr ptr, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - move(TrustedImmPtr(ptr), dest); - } - void loadArgumentInRegister(const Pointer& ptr, RegisterID dest, int argumentNumber) { Q_UNUSED(argumentNumber); @@ -449,7 +437,7 @@ public: void loadArgumentInRegister(PointerToValue temp, RegisterID dest, int argumentNumber) { if (!temp.value) { - loadArgumentInRegister(TrustedImmPtr(0), dest, argumentNumber); + move(TrustedImmPtr(0), dest); } else { Pointer addr = toAddress(dest, temp.value, argumentNumber); loadArgumentInRegister(addr, dest, argumentNumber); @@ -468,15 +456,6 @@ public: loadArgumentInRegister(addr, dest, argumentNumber); } - void loadArgumentInRegister(ReentryBlock block, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - Q_ASSERT(block.block); - DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest); - addPatch(patch, block.block); - } - #ifdef VALUE_FITS_IN_REGISTER void loadArgumentInRegister(IR::Temp* temp, RegisterID dest, int argumentNumber) { @@ -536,11 +515,6 @@ public: } #endif - void loadArgumentInRegister(QV4::String* string, RegisterID dest, int argumentNumber) - { - loadArgumentInRegister(TrustedImmPtr(string), dest, argumentNumber); - } - void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest, int argumentNumber) { Q_UNUSED(argumentNumber); @@ -697,34 +671,6 @@ public: loadArgumentOnStack(ptr, argumentNumber); } - template - void loadArgumentOnStack(ReentryBlock block, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - Q_ASSERT(block.block); - DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister); - poke(ScratchRegister, StackSlot); - addPatch(patch, block.block); - } - - template - void loadArgumentOnStack(TrustedImmPtr ptr, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - move(TrustedImmPtr(ptr), ScratchRegister); - poke(ScratchRegister, StackSlot); - } - - template - void loadArgumentOnStack(QV4::String* name, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - poke(TrustedImmPtr(name), StackSlot); - } - void loadDouble(IR::Expr *source, FPRegisterID dest) { IR::Temp *sourceTemp = source->asTemp(); -- cgit v1.2.3 From a385ea0813e8d8ea834e1d2403509777cd52d3e9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Jul 2016 17:12:21 +0200 Subject: Remove another instance of TrustedImmPtr usage in the JIT Use of TrustedImmPtr means the code becomes unrelocatable. Replace the usage with constant table use. This does not eliminate the absolute addressing right now as the constant table still uses patches, but that needs to be ported separately anyway. Change-Id: I469520fba9c9068f041157e39bc57552558e3355 Reviewed-by: Lars Knoll --- src/qml/jit/qv4assembler_p.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index ae9302316d..9373821082 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -1092,9 +1092,8 @@ public: // it's not in signed int range, so load it as a double, and truncate it down loadDouble(addr, FPGpr0); - static const double magic = double(INT_MAX) + 1; - move(TrustedImmPtr(&magic), scratchReg); - subDouble(Address(scratchReg, 0), FPGpr0); + Address inversionAddress = constantTable().loadValueAddress(QV4::Primitive::fromDouble(double(INT_MAX) + 1), scratchReg); + subDouble(inversionAddress, FPGpr0); Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg); canNeverHappen.link(this); or32(TrustedImm32(1 << 31), scratchReg); -- cgit v1.2.3 From 3190478663b68c275bb967618e98f4d9fe03f302 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Jul 2016 11:06:34 +0200 Subject: Remove unused fields in CompiledData::Function The table for referencing the inner functions is not needed. This also allows making the function offsets table a local variable. Change-Id: I3f1a1d6ab8c4f417edeafdc174e5b41d2d2b611b Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata_p.h | 1 - src/qml/compiler/qv4compiler.cpp | 10 ++-------- src/qml/compiler/qv4compiler_p.h | 3 +-- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 616ac6da0e..b6c0e6e704 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -215,7 +215,6 @@ struct Function LEUInt32 nLocals; LEUInt32 localsOffset; LEUInt32 nInnerFunctions; - LEUInt32 innerFunctionsOffset; Location location; // Qml Extensions Begin diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 8499f9b9aa..80de3beeff 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -219,6 +219,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO int unitSize = QV4::CompiledData::Unit::calculateSize(irModule->functions.size(), regexps.size(), constants.size(), lookups.size(), jsClasses.count()); + QHash functionOffsets; uint functionDataSize = 0; for (int i = 0; i < irModule->functions.size(); ++i) { QV4::IR::Function *f = irModule->functions.at(i); @@ -313,7 +314,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO return unit; } -int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) +int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) const { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; @@ -340,8 +341,6 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *ir currentOffset += function->nLocals * sizeof(quint32); function->nInnerFunctions = irFunction->nestedFunctions.size(); - function->innerFunctionsOffset = currentOffset; - currentOffset += function->nInnerFunctions * sizeof(quint32); function->nDependingIdObjects = 0; function->nDependingContextProperties = 0; @@ -381,11 +380,6 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *ir for (int i = 0; i < irFunction->locals.size(); ++i) locals[i] = getStringId(*irFunction->locals.at(i)); - // write inner functions - quint32 *innerFunctions = (quint32 *)(f + function->innerFunctionsOffset); - for (int i = 0; i < irFunction->nestedFunctions.size(); ++i) - innerFunctions[i] = functionOffsets.value(irFunction->nestedFunctions.at(i)); - // write QML dependencies quint32 *writtenDeps = (quint32 *)(f + function->dependingIdObjectsOffset); for (int id : irFunction->idObjectDependencies) { diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index df10e9919b..ef50945981 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -114,13 +114,12 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable); // Returns bytes written - int writeFunction(char *f, IR::Function *irFunction); + int writeFunction(char *f, IR::Function *irFunction) const; StringTableGenerator stringTable; private: IR::Module *irModule; - QHash functionOffsets; QList lookups; QVector regexps; QVector constants; -- cgit v1.2.3 From 50cc3c20047b88e763dc21ec3c5d8b28c7ecb2d4 Mon Sep 17 00:00:00 2001 From: Joni Poikelin Date: Thu, 14 Jul 2016 08:55:08 +0300 Subject: Avoid crashing when failing to query GL_EXTENSIONS Change-Id: Ice542d53c4eb9f34745e2d06dd03c32de7a9817e Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/util/qsgatlastexture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp index 832510148c..1a1f0d37f7 100644 --- a/src/quick/scenegraph/util/qsgatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgatlastexture.cpp @@ -151,13 +151,13 @@ Atlas::Atlas(const QSize &size) wrongfullyReportsBgra8888Support = false; const char *ext = (const char *) QOpenGLContext::currentContext()->functions()->glGetString(GL_EXTENSIONS); - if (!wrongfullyReportsBgra8888Support + if (ext && !wrongfullyReportsBgra8888Support && (strstr(ext, "GL_EXT_bgra") || strstr(ext, "GL_EXT_texture_format_BGRA8888") || strstr(ext, "GL_IMG_texture_format_BGRA8888"))) { m_internalFormat = m_externalFormat = GL_BGRA; #ifdef Q_OS_IOS - } else if (strstr(ext, "GL_APPLE_texture_format_BGRA8888")) { + } else if (ext && strstr(ext, "GL_APPLE_texture_format_BGRA8888")) { m_internalFormat = GL_RGBA; m_externalFormat = GL_BGRA; #endif // IOS -- cgit v1.2.3 From afa665317a2eb137aa5e4586c5a5ad093b33b279 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Jul 2016 15:38:43 +0200 Subject: Clean up JS internal class serialization We can serialize the compiled JS class data right when we collect it from the IR. That makes the assembly of the compilation a little simpler later on. Change-Id: I36d7dd2cc74241338fb1c236ce7778405066c8b8 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compiler.cpp | 55 ++++++++++++++++++---------------------- src/qml/compiler/qv4compiler_p.h | 4 +-- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 80de3beeff..6b9f42709f 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -97,7 +97,6 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::IR::Module *module) : irModule(module) - , jsClassDataSize(0) { // Make sure the empty string always gets index 0 registerString(QString()); @@ -179,30 +178,32 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *arg { // ### re-use existing class definitions. - QList members; - members.reserve(count); + const int size = CompiledData::JSClass::calculateSize(count); + jsClassOffsets.append(jsClassData.size()); + const int oldSize = jsClassData.size(); + jsClassData.resize(jsClassData.size() + size); + memset(jsClassData.data() + oldSize, 0, size); - IR::ExprList *it = args; - for (int i = 0; i < count; ++i, it = it->next) { - CompiledData::JSClassMember member; + CompiledData::JSClass *jsClass = reinterpret_cast(jsClassData.data() + oldSize); + jsClass->nMembers = count; + CompiledData::JSClassMember *member = reinterpret_cast(jsClass + 1); + IR::ExprList *it = args; + for (int i = 0; i < count; ++i, it = it->next, ++member) { QV4::IR::Name *name = it->expr->asName(); it = it->next; const bool isData = it->expr->asConst()->value; it = it->next; - member.nameOffset = registerString(*name->id); - member.isAccessor = !isData; - members << member; + member->nameOffset = registerString(*name->id); + member->isAccessor = !isData; if (!isData) it = it->next; } - jsClasses << members; - jsClassDataSize += CompiledData::JSClass::calculateSize(members.count()); - return jsClasses.size() - 1; + return jsClassOffsets.size() - 1; } QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option) @@ -217,7 +218,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO } int unitSize = QV4::CompiledData::Unit::calculateSize(irModule->functions.size(), regexps.size(), - constants.size(), lookups.size(), jsClasses.count()); + constants.size(), lookups.size(), jsClassOffsets.count()); QHash functionOffsets; uint functionDataSize = 0; @@ -230,7 +231,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount); } - const int totalSize = unitSize + functionDataSize + jsClassDataSize + (option == GenerateWithStringTable ? stringTable.sizeOfTableAndData() : 0); + const int totalSize = unitSize + functionDataSize + jsClassData.size() + (option == GenerateWithStringTable ? stringTable.sizeOfTableAndData() : 0); char *data = (char *)malloc(totalSize); memset(data, 0, totalSize); QV4::CompiledData::Unit *unit = (QV4::CompiledData::Unit*)data; @@ -248,11 +249,11 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO unit->offsetToRegexpTable = unit->offsetToLookupTable + unit->lookupTableSize * CompiledData::Lookup::calculateSize(); unit->constantTableSize = constants.size(); unit->offsetToConstantTable = unit->offsetToRegexpTable + unit->regexpTableSize * CompiledData::RegExp::calculateSize(); - unit->jsClassTableSize = jsClasses.count(); + unit->jsClassTableSize = jsClassOffsets.count(); unit->offsetToJSClassTable = unit->offsetToConstantTable + unit->constantTableSize * sizeof(ReturnedValue); if (option == GenerateWithStringTable) { unit->stringTableSize = stringTable.stringCount(); - unit->offsetToStringTable = unitSize + functionDataSize + jsClassDataSize; + unit->offsetToStringTable = unitSize + functionDataSize + jsClassData.size(); } else { unit->stringTableSize = 0; unit->offsetToStringTable = 0; @@ -289,22 +290,14 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO ReturnedValue *constantTable = (ReturnedValue *)(data + unit->offsetToConstantTable); memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue)); - // write js classes and js class lookup table - uint *jsClassTable = (uint*)(data + unit->offsetToJSClassTable); - char *jsClass = data + unitSize + functionDataSize; - for (int i = 0; i < jsClasses.count(); ++i) { - jsClassTable[i] = jsClass - data; - - const QList members = jsClasses.at(i); - - CompiledData::JSClass *c = reinterpret_cast(jsClass); - c->nMembers = members.count(); - - CompiledData::JSClassMember *memberToWrite = reinterpret_cast(jsClass + sizeof(CompiledData::JSClass)); - foreach (const CompiledData::JSClassMember &member, members) - *memberToWrite++ = member; + { + char *jsClassDataPtrToWrite = data + unitSize + functionDataSize; + memcpy(jsClassDataPtrToWrite, jsClassData.constData(), jsClassData.size()); - jsClass += CompiledData::JSClass::calculateSize(members.count()); + // write js classes and js class lookup table + uint *jsClassOffsetTable = (uint*)(data + unit->offsetToJSClassTable); + for (int i = 0; i < jsClassOffsets.count(); ++i) + jsClassOffsetTable[i] = jsClassDataPtrToWrite - data + jsClassOffsets.at(i); } // write strings and string table diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index ef50945981..2ff08d76dd 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -123,8 +123,8 @@ private: QList lookups; QVector regexps; QVector constants; - QList > jsClasses; - uint jsClassDataSize; + QByteArray jsClassData; + QVector jsClassOffsets; }; } -- cgit v1.2.3 From d5bda1e25ea8866851cec109ff630cad44804e53 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Jul 2016 15:43:16 +0200 Subject: More endianness fixes It's necessary to consistently use LEUInt32 when writing the offset tables, as that's also what we're using when reading from them. Change-Id: Idbef310bd6b28a0e6b6b0dff516e0ef178f04999 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compiler.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 6b9f42709f..8c99fdf0ca 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -75,13 +75,13 @@ void QV4::Compiler::StringTableGenerator::clear() void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) { char *dataStart = reinterpret_cast(unit); - uint *stringTable = reinterpret_cast(dataStart + unit->offsetToStringTable); + CompiledData::LEUInt32 *stringTable = reinterpret_cast(dataStart + unit->offsetToStringTable); char *stringData = dataStart + unit->offsetToStringTable + unit->stringTableSize * sizeof(uint); for (int i = 0; i < strings.size(); ++i) { stringTable[i] = stringData - dataStart; const QString &qstr = strings.at(i); - QV4::CompiledData::String *s = (QV4::CompiledData::String*)(stringData); + QV4::CompiledData::String *s = reinterpret_cast(stringData); s->size = qstr.length(); #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN memcpy(s + 1, qstr.constData(), qstr.length()*sizeof(ushort)); @@ -266,9 +266,11 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO unit->offsetToObjects = 0; unit->indexOfRootObject = 0; - uint *functionTable = (uint *)(data + unit->offsetToFunctionTable); - for (int i = 0; i < irModule->functions.size(); ++i) - functionTable[i] = functionOffsets.value(irModule->functions.at(i)); + { + CompiledData::LEUInt32 *functionTable = reinterpret_cast(data + unit->offsetToFunctionTable); + for (int i = 0; i < irModule->functions.size(); ++i) + functionTable[i] = functionOffsets.value(irModule->functions.at(i)); + } char *f = data + unitSize; for (int i = 0; i < irModule->functions.size(); ++i) { @@ -295,7 +297,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO memcpy(jsClassDataPtrToWrite, jsClassData.constData(), jsClassData.size()); // write js classes and js class lookup table - uint *jsClassOffsetTable = (uint*)(data + unit->offsetToJSClassTable); + CompiledData::LEUInt32 *jsClassOffsetTable = reinterpret_cast(data + unit->offsetToJSClassTable); for (int i = 0; i < jsClassOffsets.count(); ++i) jsClassOffsetTable[i] = jsClassDataPtrToWrite - data + jsClassOffsets.at(i); } -- cgit v1.2.3 From 80a024081a8b04bb789a242fc7949af3110105d7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jul 2016 13:11:08 +0200 Subject: Clean up implicit component registration When assigning an object to a property that is a QQmlComponent, we implicitly "wrap" a Component {} around the object declaration. In the QML IR this is only half-heartedly represented. In order to correctly determine dependencies later when saving and to support QML files that use implicit components but don't import QtQml or QtQuick explicitly, we must also extend the file imports accordingly. This is now done (and tested) by emulating a "import QtQml 2.0 as QmlInternals" and then using "QmlInternals.Component". Change-Id: I26f4f53a35675b52d4bd39f23359b0ac8f9678c5 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 28 +++++++++++++++- src/qml/compiler/qqmltypecompiler_p.h | 2 ++ tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 42 ++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index ceff1611c5..42e31ea7f8 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -271,6 +271,28 @@ QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scrip return object->bindingAsString(document, scriptIndex); } +void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion) +{ + const quint32 moduleIdx = registerString(module); + const quint32 qualifierIdx = registerString(qualifier); + + for (int i = 0, count = document->imports.count(); i < count; ++i) { + const QV4::CompiledData::Import *existingImport = document->imports.at(i); + if (existingImport->type == QV4::CompiledData::Import::ImportLibrary + && existingImport->uriIndex == moduleIdx + && existingImport->qualifierIndex == qualifierIdx) + return; + } + auto pool = memoryPool(); + QV4::CompiledData::Import *import = pool->New(); + import->type = QV4::CompiledData::Import::ImportLibrary; + import->majorVersion = majorVersion; + import->minorVersion = minorVersion; + import->uriIndex = moduleIdx; + import->qualifierIndex = qualifierIdx; + document->imports.append(import); +} + QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler) : compiler(typeCompiler) { @@ -803,11 +825,15 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI if (!mo) continue; + // emulate "import Qml 2.0 as QmlInternals" and then wrap the component in "QmlInternals.Component {}" QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); Q_ASSERT(componentType); + const QString qualifier = QStringLiteral("QmlInternals"); + + compiler->addImport(componentType->module(), qualifier, componentType->majorVersion(), componentType->minorVersion()); QmlIR::Object *syntheticComponent = pool->New(); - syntheticComponent->init(pool, compiler->registerString(QString::fromUtf8(componentType->typeName())), compiler->registerString(QString())); + syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType->elementName()), compiler->registerString(QString())); syntheticComponent->location = binding->valueLocation; syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 1692e05450..93ae7d1fad 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -133,6 +133,8 @@ public: QString bindingAsString(const QmlIR::Object *object, int scriptIndex) const; + void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion); + private: QList errors; QQmlEnginePrivate *engine; diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 186a9e7cf7..b4697a4a3b 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -40,6 +40,7 @@ private slots: void initTestCase(); void regenerateAfterChange(); + void registerImportForImplicitComponent(); }; struct TestCompiler @@ -177,6 +178,47 @@ void tst_qmldiskcache::regenerateAfterChange() } } +void tst_qmldiskcache::registerImportForImplicitComponent() +{ + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import QtQuick 2.0\n" + "Loader {\n" + " sourceComponent: Item {}\n" + "}"); + + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); +#ifdef V4_ENABLE_JIT + { + const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); + QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); + + QCOMPARE(quint32(testUnit->nImports), quint32(2)); + QCOMPARE(testUnit->stringAt(testUnit->importAt(0)->uriIndex), QStringLiteral("QtQuick")); + + QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); + + QCOMPARE(testUnit->stringAt(testUnit->importAt(1)->uriIndex), QString(componentType->module())); + QCOMPARE(testUnit->stringAt(testUnit->importAt(1)->qualifierIndex), QStringLiteral("QmlInternals")); + + QCOMPARE(quint32(testUnit->nObjects), quint32(3)); + + const QV4::CompiledData::Object *obj = testUnit->objectAt(0); + QCOMPARE(quint32(obj->nBindings), quint32(1)); + QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Object)); + + const QV4::CompiledData::Object *implicitComponent = testUnit->objectAt(obj->bindingTable()->value.objectIndex); + QCOMPARE(testUnit->stringAt(implicitComponent->inheritedTypeNameIndex), QStringLiteral("QmlInternals.") + componentType->elementName()); + } +#else + QVERIFY(!testCompiler.mapUnit()); + return; +#endif +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" -- cgit v1.2.3 From c8c1d4964ba4af8700d01cefdd80359090479bc0 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 30 May 2016 10:19:20 +0200 Subject: PieChart examples: initialize members Coverity (CID 161680, 161678) pointed out that constructors don't initialize some members; getters would have returned drivel if called before setters. Unimportant but we should set a good example in our example code. Change-Id: Ia0483cfbe2cae379a0e84f10db1d8edc9cb5c52b Reviewed-by: Simon Hausmann --- .../tutorials/extending-qml/chapter4-customPropertyTypes/piechart.cpp | 2 +- .../qml/tutorials/extending-qml/chapter5-listproperties/pieslice.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/piechart.cpp b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/piechart.cpp index 965e5152c1..d963b6d1b4 100644 --- a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/piechart.cpp +++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/piechart.cpp @@ -41,7 +41,7 @@ #include "pieslice.h" PieChart::PieChart(QQuickItem *parent) - : QQuickItem(parent) + : QQuickItem(parent), m_pieSlice(0) { } diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/pieslice.cpp b/examples/qml/tutorials/extending-qml/chapter5-listproperties/pieslice.cpp index ceb0041ec8..50c018e33e 100644 --- a/examples/qml/tutorials/extending-qml/chapter5-listproperties/pieslice.cpp +++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/pieslice.cpp @@ -42,7 +42,7 @@ #include PieSlice::PieSlice(QQuickItem *parent) - : QQuickPaintedItem(parent) + : QQuickPaintedItem(parent), m_fromAngle(0), m_angleSpan(0) { } -- cgit v1.2.3 From ff844b63b9656e5527af126d41a300ecfcc303ff Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 7 Jul 2016 22:35:54 +0200 Subject: Add QQuickPointerEvent::syntheticMouseEvent It makes it easier to send synthetic mouse events for touch. Change-Id: Ibb8e2737e3245ae7438708aa170ec1f888e770d8 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 47 ++++++++++++++++++++++++++++++++++++++ src/quick/items/qquickevents_p_p.h | 6 ++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index ad5a007b37..d09294b515 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qquickevents_p_p.h" +#include #include #include @@ -572,6 +573,52 @@ const QQuickEventPoint *QQuickPointerEvent::point(int i) const { return nullptr; } +/*! + \internal + Populate the reusable synth-mouse event from one touchpoint. + It's required that isTouchEvent() be true when this is called. + If the touchpoint cannot be found, this returns nullptr. + Ownership of the event is NOT transferred to the caller. +*/ +QMouseEvent *QQuickPointerEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const { + const QTouchEvent::TouchPoint *p = touchPointById(pointID); + if (!p) + return nullptr; + QEvent::Type type; + Qt::MouseButton buttons = Qt::LeftButton; + switch (p->state()) { + case Qt::TouchPointPressed: + type = QEvent::MouseButtonPress; + break; + case Qt::TouchPointMoved: + case Qt::TouchPointStationary: + type = QEvent::MouseMove; + break; + case Qt::TouchPointReleased: + type = QEvent::MouseButtonRelease; + buttons = Qt::NoButton; + break; + default: + Q_ASSERT(false); + return nullptr; + } + m_synthMouseEvent = QMouseEvent(type, relativeTo->mapFromScene(p->scenePos()), + p->scenePos(), p->screenPos(), Qt::LeftButton, buttons, m_event->modifiers()); + m_synthMouseEvent.setAccepted(true); + m_synthMouseEvent.setTimestamp(m_event->timestamp()); + // In the future we will try to always have valid velocity in every QQuickEventPoint. + // QQuickFlickablePrivate::handleMouseMoveEvent() checks for QTouchDevice::Velocity + // and if it is set, then it does not need to do its own velocity calculations. + // That's probably the only usecase for this, so far. Some day Flickable should handle + // pointer events, and then passing touchpoint velocity via QMouseEvent will be obsolete. + // Conveniently (by design), QTouchDevice::Velocity == QQuickPointerDevice.Velocity + // so that we don't need to convert m_device->capabilities(). + if (m_device) + QGuiApplicationPrivate::setMouseEventCapsAndVelocity(&m_synthMouseEvent, m_device->capabilities(), p->velocity()); + QGuiApplicationPrivate::setMouseEventSource(&m_synthMouseEvent, Qt::MouseEventSynthesizedByQt); + return &m_synthMouseEvent; +} + /*! \internal Returns a pointer to the original TouchPoint which has the same diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 0a306e8b46..65a9ae190a 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -396,7 +396,8 @@ public: , m_button(Qt::NoButton) , m_pressedButtons(Qt::NoButton) , m_pointCount(0) - , m_mousePoint(nullptr) { } + , m_mousePoint(nullptr) + , m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier) { } public: // property accessors const QQuickPointerDevice *device() const { return m_device; } @@ -422,6 +423,8 @@ public: // helpers for C++ only (during event delivery) QTouchEvent *touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const; + QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; + private: void initFromMouse(QMouseEvent *ev); void initFromTouch(QTouchEvent *ev); @@ -434,6 +437,7 @@ private: int m_pointCount; QVector m_touchPoints; QQuickEventPoint *m_mousePoint; + mutable QMouseEvent m_synthMouseEvent; Q_DISABLE_COPY(QQuickPointerEvent) }; -- cgit v1.2.3 From c586fc4be2428db57ba8579f8280d09893ba796d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Jul 2016 16:12:09 +0200 Subject: Clean up function offset table generation We can replace the intermediate hash with a vector of the final little-endian encoded function offsets. Change-Id: If3f970330ab232a672f7c75b4d32f8ef4d246714 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compiler.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 8c99fdf0ca..4651bc2c8f 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -42,6 +42,7 @@ #include #include #include +#include QV4::Compiler::StringTableGenerator::StringTableGenerator() { @@ -220,11 +221,11 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO int unitSize = QV4::CompiledData::Unit::calculateSize(irModule->functions.size(), regexps.size(), constants.size(), lookups.size(), jsClassOffsets.count()); - QHash functionOffsets; + CompiledData::LEUInt32 *functionOffsets = reinterpret_cast(alloca(irModule->functions.size() * sizeof(CompiledData::LEUInt32))); uint functionDataSize = 0; for (int i = 0; i < irModule->functions.size(); ++i) { QV4::IR::Function *f = irModule->functions.at(i); - functionOffsets.insert(f, functionDataSize + unitSize); + functionOffsets[i] = functionDataSize + unitSize; const int qmlIdDepsCount = f->idObjectDependencies.count(); const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); @@ -266,11 +267,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO unit->offsetToObjects = 0; unit->indexOfRootObject = 0; - { - CompiledData::LEUInt32 *functionTable = reinterpret_cast(data + unit->offsetToFunctionTable); - for (int i = 0; i < irModule->functions.size(); ++i) - functionTable[i] = functionOffsets.value(irModule->functions.at(i)); - } + memcpy(data + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(CompiledData::LEUInt32)); char *f = data + unitSize; for (int i = 0; i < irModule->functions.size(); ++i) { -- cgit v1.2.3 From cf23466f0aa370790bec05bc7764176c194fe5bb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Jul 2016 16:19:04 +0200 Subject: Simplify CompiledData::Unit serialization There are currently two places where we calculate the total size of a CompiledData::Unit including all of its data, once in its calculateSize() function and then when constructing the Unit itself. This is error prone when trying to do changes to the layout, as two places need to be adjusted. Instead we can centralize the populating of the entire CompiledData::Unit header, the calculation of the various offsets as well as the entire size in one shot in a new generateHeader function. Afterwards we can allocate the final amount of memory and copy the remaining bytes across and assemble the final data structure. Change-Id: Ic26d7812e59d2bee2f45cd72742e3195a56950df Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata_p.h | 13 ---- src/qml/compiler/qv4compiler.cpp | 138 ++++++++++++++++++++--------------- src/qml/compiler/qv4compiler_p.h | 5 +- 3 files changed, 83 insertions(+), 73 deletions(-) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index b6c0e6e704..a1d0a6d732 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -146,8 +146,6 @@ struct RegExp QJsonPrivate::qle_bitfield<0, 4> flags; QJsonPrivate::qle_bitfield<4, 28> stringIndex; }; - - static int calculateSize() { return sizeof(RegExp); } }; struct Lookup @@ -166,8 +164,6 @@ struct Lookup }; Lookup() { type_and_flags = 0; nameIndex = 0; } - - static int calculateSize() { return sizeof(Lookup); } }; struct JSClassMember @@ -687,15 +683,6 @@ struct Unit *nMembers = klass->nMembers; return reinterpret_cast(ptr + sizeof(JSClass)); } - - static int calculateSize(uint nFunctions, uint nRegExps, uint nConstants, - uint nLookups, uint nClasses) { - return (sizeof(Unit) - + (nFunctions + nClasses) * sizeof(uint) - + nRegExps * RegExp::calculateSize() - + nConstants * sizeof(QV4::ReturnedValue) - + nLookups * Lookup::calculateSize() - + 7) & ~7; } }; #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 4651bc2c8f..fc462bb1c6 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -218,85 +218,45 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO registerString(*f->locals.at(i)); } - int unitSize = QV4::CompiledData::Unit::calculateSize(irModule->functions.size(), regexps.size(), - constants.size(), lookups.size(), jsClassOffsets.count()); - CompiledData::LEUInt32 *functionOffsets = reinterpret_cast(alloca(irModule->functions.size() * sizeof(CompiledData::LEUInt32))); - uint functionDataSize = 0; - for (int i = 0; i < irModule->functions.size(); ++i) { - QV4::IR::Function *f = irModule->functions.at(i); - functionOffsets[i] = functionDataSize + unitSize; + uint jsClassDataOffset = 0; - const int qmlIdDepsCount = f->idObjectDependencies.count(); - const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); - functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount); - } - - const int totalSize = unitSize + functionDataSize + jsClassData.size() + (option == GenerateWithStringTable ? stringTable.sizeOfTableAndData() : 0); - char *data = (char *)malloc(totalSize); - memset(data, 0, totalSize); - QV4::CompiledData::Unit *unit = (QV4::CompiledData::Unit*)data; - - memcpy(unit->magic, QV4::CompiledData::magic_str, sizeof(unit->magic)); - unit->architecture = 0; // ### - unit->flags = QV4::CompiledData::Unit::IsJavascript; - unit->version = 1; - unit->unitSize = totalSize; - unit->functionTableSize = irModule->functions.size(); - unit->offsetToFunctionTable = sizeof(*unit); - unit->lookupTableSize = lookups.count(); - unit->offsetToLookupTable = unit->offsetToFunctionTable + unit->functionTableSize * sizeof(uint); - unit->regexpTableSize = regexps.size(); - unit->offsetToRegexpTable = unit->offsetToLookupTable + unit->lookupTableSize * CompiledData::Lookup::calculateSize(); - unit->constantTableSize = constants.size(); - unit->offsetToConstantTable = unit->offsetToRegexpTable + unit->regexpTableSize * CompiledData::RegExp::calculateSize(); - unit->jsClassTableSize = jsClassOffsets.count(); - unit->offsetToJSClassTable = unit->offsetToConstantTable + unit->constantTableSize * sizeof(ReturnedValue); - if (option == GenerateWithStringTable) { - unit->stringTableSize = stringTable.stringCount(); - unit->offsetToStringTable = unitSize + functionDataSize + jsClassData.size(); - } else { - unit->stringTableSize = 0; - unit->offsetToStringTable = 0; + char *dataPtr; + CompiledData::Unit *unit; + { + QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset); + dataPtr = reinterpret_cast(malloc(tempHeader.unitSize)); + memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*)); + memcpy(unit, &tempHeader, sizeof(tempHeader)); } - unit->indexOfRootFunction = -1; - unit->sourceFileIndex = getStringId(irModule->fileName); - unit->nImports = 0; - unit->offsetToImports = 0; - unit->nObjects = 0; - unit->offsetToObjects = 0; - unit->indexOfRootObject = 0; - memcpy(data + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(CompiledData::LEUInt32)); + memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(CompiledData::LEUInt32)); - char *f = data + unitSize; for (int i = 0; i < irModule->functions.size(); ++i) { QV4::IR::Function *function = irModule->functions.at(i); if (function == irModule->rootFunction) unit->indexOfRootFunction = i; - const int bytes = writeFunction(f, function); - f += bytes; + writeFunction(dataPtr + functionOffsets[i], function); } - CompiledData::Lookup *lookupsToWrite = (CompiledData::Lookup*)(data + unit->offsetToLookupTable); + CompiledData::Lookup *lookupsToWrite = reinterpret_cast(dataPtr + unit->offsetToLookupTable); foreach (const CompiledData::Lookup &l, lookups) *lookupsToWrite++ = l; - CompiledData::RegExp *regexpTable = (CompiledData::RegExp *)(data + unit->offsetToRegexpTable); + CompiledData::RegExp *regexpTable = reinterpret_cast(dataPtr + unit->offsetToRegexpTable); memcpy(regexpTable, regexps.constData(), regexps.size() * sizeof(*regexpTable)); - ReturnedValue *constantTable = (ReturnedValue *)(data + unit->offsetToConstantTable); + ReturnedValue *constantTable = reinterpret_cast(dataPtr + unit->offsetToConstantTable); memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue)); { - char *jsClassDataPtrToWrite = data + unitSize + functionDataSize; - memcpy(jsClassDataPtrToWrite, jsClassData.constData(), jsClassData.size()); + memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size()); // write js classes and js class lookup table - CompiledData::LEUInt32 *jsClassOffsetTable = reinterpret_cast(data + unit->offsetToJSClassTable); + CompiledData::LEUInt32 *jsClassOffsetTable = reinterpret_cast(dataPtr + unit->offsetToJSClassTable); for (int i = 0; i < jsClassOffsets.count(); ++i) - jsClassOffsetTable[i] = jsClassDataPtrToWrite - data + jsClassOffsets.at(i); + jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i); } // write strings and string table @@ -306,7 +266,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO return unit; } -int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) const +void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) const { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; @@ -390,7 +350,67 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *ir *writtenDeps++ = property.key(); // property index *writtenDeps++ = property.value(); // notify index } +} + +QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian *functionOffsets, uint *jsClassDataOffset) const +{ + CompiledData::Unit unit; + memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic)); + unit.architecture = 0; // ### + unit.flags = QV4::CompiledData::Unit::IsJavascript; + unit.version = 1; + unit.functionTableSize = irModule->functions.size(); + + quint32 nextOffset = sizeof(CompiledData::Unit); + + unit.offsetToFunctionTable = nextOffset; + nextOffset += unit.functionTableSize * sizeof(uint); + + unit.lookupTableSize = lookups.count(); + unit.offsetToLookupTable = nextOffset; + nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup); + + unit.regexpTableSize = regexps.size(); + unit.offsetToRegexpTable = nextOffset; + nextOffset += unit.regexpTableSize * sizeof(CompiledData::RegExp); - return CompiledData::Function::calculateSize(function->nFormals, function->nLocals, function->nInnerFunctions, - function->nDependingIdObjects, function->nDependingContextProperties + function->nDependingScopeProperties); + unit.constantTableSize = constants.size(); + unit.offsetToConstantTable = nextOffset; + nextOffset += unit.constantTableSize * sizeof(ReturnedValue); + + unit.jsClassTableSize = jsClassOffsets.count(); + unit.offsetToJSClassTable = nextOffset; + nextOffset += unit.jsClassTableSize * sizeof(uint); + + *jsClassDataOffset = nextOffset; + nextOffset += jsClassData.size(); + + for (int i = 0; i < irModule->functions.size(); ++i) { + QV4::IR::Function *f = irModule->functions.at(i); + functionOffsets[i] = nextOffset; + + const int qmlIdDepsCount = f->idObjectDependencies.count(); + const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); + nextOffset += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount); + } + + if (option == GenerateWithStringTable) { + unit.stringTableSize = stringTable.stringCount(); + unit.offsetToStringTable = nextOffset; + nextOffset += stringTable.sizeOfTableAndData(); + } else { + unit.stringTableSize = 0; + unit.offsetToStringTable = 0; + } + unit.indexOfRootFunction = -1; + unit.sourceFileIndex = getStringId(irModule->fileName); + unit.nImports = 0; + unit.offsetToImports = 0; + unit.nObjects = 0; + unit.offsetToObjects = 0; + unit.indexOfRootObject = 0; + + unit.unitSize = nextOffset; + + return unit; } diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 2ff08d76dd..4b67fe8600 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -52,6 +52,7 @@ #include #include "qv4jsir_p.h" +#include QT_BEGIN_NAMESPACE @@ -114,10 +115,12 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable); // Returns bytes written - int writeFunction(char *f, IR::Function *irFunction) const; + void writeFunction(char *f, IR::Function *irFunction) const; StringTableGenerator stringTable; private: + CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian *functionOffsets, uint *jsClassDataOffset) const; + IR::Module *irModule; QList lookups; -- cgit v1.2.3 From 20b96e21090fd008cc4a0d39300402fa3865d705 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 15 Jul 2016 10:45:22 +0200 Subject: Relax QQmlProfilerService test for scene graph events The scene graph might decide to do an initial rendering, before the first SceneGraphContextFrame. Change-Id: Ie6d96574b5585cfda4dcd258b6031303f9a37715 Reviewed-by: Laszlo Agocs --- .../qqmlprofilerservice/tst_qqmlprofilerservice.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index 670d58e4fd..61006ad1f1 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -648,11 +648,11 @@ void tst_QQmlProfilerService::scenegraphData() checkTraceReceived(); checkJsHeap(); - // check that at least one frame was rendered - // there should be a SGPolishAndSync + SGRendererFrame + SGRenderLoopFrame sequence - // (though we can't be sure to get the SGRenderLoopFrame in the threaded renderer) + // Check that at least one frame was rendered. + // There should be a SGContextFrame + SGRendererFrame + SGRenderLoopFrame sequence, + // but we can't be sure to get the SGRenderLoopFrame in the threaded renderer. // - // since the rendering happens in a different thread, there could be other unrelated events + // Since the rendering happens in a different thread, there could be other unrelated events // interleaved. Also, events could carry the same time stamps and be sorted in an unexpected way // if the clocks are acting up. qint64 contextFrameTime = -1; @@ -681,8 +681,13 @@ void tst_QQmlProfilerService::scenegraphData() foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { if (msg.detailType == QQmlProfilerClient::SceneGraphRenderLoopFrame) { - QVERIFY(msg.time >= renderFrameTime); - break; + if (msg.time >= contextFrameTime) { + // Make sure SceneGraphRenderLoopFrame is not between SceneGraphContextFrame and + // SceneGraphRendererFrame. A SceneGraphRenderLoopFrame before everything else is + // OK as the scene graph might decide to do an initial rendering. + QVERIFY(msg.time >= renderFrameTime); + break; + } } } } -- cgit v1.2.3 From 12de7bbc1ee2959f4e31a345b4573242d34474cf Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 13 Jul 2016 16:32:15 +0200 Subject: D3D12: Add support for QQuickWindow::setDefaultAlphaBuffer Transparency won't work yet, though. The existing mechanisms are disfunctional with our flip model swapchain. Will have to investigate DirectComposition perhaps. In any case, having the alpha size correct in the window's format() is a good thing. Change-Id: Ia8304f90700d9fb5bd71e3e1bd95d2eda0acab38 Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12context.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp index f54ee6a053..9b88af995d 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp @@ -47,6 +47,7 @@ #include "qsgd3d12painternode_p.h" #include "qsgd3d12publicnodes_p.h" #include "qsgd3d12spritenode_p.h" +#include QT_BEGIN_NAMESPACE @@ -105,7 +106,12 @@ QSize QSGD3D12Context::minimumFBOSize() const QSurfaceFormat QSGD3D12Context::defaultSurfaceFormat() const { - return QSurfaceFormat::defaultFormat(); + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); + + if (QQuickWindow::hasDefaultAlphaBuffer()) + format.setAlphaBufferSize(8); + + return format; } QSGRendererInterface *QSGD3D12Context::rendererInterface(QSGRenderContext *renderContext) -- cgit v1.2.3 From c2414e16aba2ad499a49d3661a2961795c9a9dc9 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 14 Jul 2016 12:43:46 +0200 Subject: Fix rendercontrol grabs with the software backend Calling grabWindow recurses (since that is implemented via QQuickRenderControl::grab...) so it's not an option. Instead, call directly in the software renderer implementation. Fix also the size of the offscreen QQuickWindow when using QQuickWidget in combination with the software backend. Change-Id: I857a2cc0aebbbaa5d52d809aeaec37c15b0787b9 Reviewed-by: Andy Nichols --- src/quick/items/qquickrendercontrol.cpp | 29 ++++++++++++++++++---- .../software/qsgabstractsoftwarerenderer.cpp | 7 +++++- .../software/qsgabstractsoftwarerenderer_p.h | 2 ++ .../adaptations/software/qsgsoftwarerenderer.cpp | 5 ++++ .../adaptations/software/qsgsoftwarerenderer_p.h | 1 + src/quickwidgets/qquickwidget.cpp | 11 ++++---- 6 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index dbe1add345..8c18c58224 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -56,9 +56,9 @@ #include #include +#include #include - QT_BEGIN_NAMESPACE #ifndef QT_NO_OPENGL extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); @@ -369,12 +369,31 @@ QImage QQuickRenderControl::grab() if (!d->window) return QImage(); - render(); + QImage grabContent; + + if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) { #ifndef QT_NO_OPENGL - QImage grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), false, false); -#else - QImage grabContent = d->window->grabWindow(); + render(); + grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), false, false); #endif + } else if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) { + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); + QSGSoftwareRenderer *softwareRenderer = static_cast(cd->renderer); + if (softwareRenderer) { + const qreal dpr = d->window->effectiveDevicePixelRatio(); + const QSize imageSize = d->window->size() * dpr; + grabContent = QImage(imageSize, QImage::Format_ARGB32_Premultiplied); + grabContent.setDevicePixelRatio(dpr); + QPaintDevice *prevDev = softwareRenderer->currentPaintDevice(); + softwareRenderer->setCurrentPaintDevice(&grabContent); + softwareRenderer->markDirty(); + render(); + softwareRenderer->setCurrentPaintDevice(prevDev); + } + } else { + qWarning("QQuickRenderControl: grabs are not supported with the current Qt Quick backend"); + } + return grabContent; } diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp index 26efba5b13..a6cb99ae05 100644 --- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp @@ -236,7 +236,7 @@ void QSGAbstractSoftwareRenderer::setBackgroundSize(const QSize &size) m_background->setRect(0.0f, 0.0f, size.width(), size.height()); renderableNode(m_background)->markGeometryDirty(); // Invalidate the whole scene when the background is resized - m_dirtyRegion = QRegion(m_background->rect().toRect()); + markDirty(); } QColor QSGAbstractSoftwareRenderer::backgroundColor() @@ -322,4 +322,9 @@ void QSGAbstractSoftwareRenderer::nodeOpacityUpdated(QSGNode *node) m_nodeUpdater->updateNodes(node); } +void QSGAbstractSoftwareRenderer::markDirty() +{ + m_dirtyRegion = QRegion(m_background->rect().toRect()); +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h index 73410b09f5..905577b92a 100644 --- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h @@ -75,6 +75,8 @@ public: void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override; + void markDirty(); + protected: QRegion renderNodes(QPainter *painter); void buildRenderList(); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp index 7bf06f8081..257009472e 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp @@ -69,6 +69,11 @@ void QSGSoftwareRenderer::setCurrentPaintDevice(QPaintDevice *device) m_backingStore = nullptr; } +QPaintDevice *QSGSoftwareRenderer::currentPaintDevice() const +{ + return m_paintDevice; +} + void QSGSoftwareRenderer::setBackingStore(QBackingStore *backingStore) { m_backingStore = backingStore; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h index a201e4887e..bb28da4ca5 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h @@ -65,6 +65,7 @@ public: virtual ~QSGSoftwareRenderer(); void setCurrentPaintDevice(QPaintDevice *device); + QPaintDevice *currentPaintDevice() const; void setBackingStore(QBackingStore *backingStore); QRegion flushRegion() const; diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 2a014546e1..b25e2d348d 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -894,6 +894,12 @@ void QQuickWidget::createFramebufferObject() if (size().isEmpty()) return; + // Even though this is just an offscreen window we should set the position on it, as it might be + // useful for an item to know the actual position of the scene. + // Note: The position will be update when we get a move event (see: updatePosition()). + const QPoint &globalPos = mapToGlobal(QPoint(0, 0)); + d->offscreenWindow->setGeometry(globalPos.x(), globalPos.y(), width(), height()); + if (d->useSoftwareRenderer) { const QSize imageSize = size() * devicePixelRatio(); d->softwareImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied); @@ -961,11 +967,6 @@ void QQuickWidget::createFramebufferObject() } #endif - // Even though this is just an offscreen window we should set the position on it, as it might be - // useful for an item to know the actual position of the scene. - // Note: The position will be update when we get a move event (see: updatePosition()). - const QPoint &globalPos = mapToGlobal(QPoint(0, 0)); - d->offscreenWindow->setGeometry(globalPos.x(), globalPos.y(), width(), height()); d->offscreenWindow->setRenderTarget(d->fbo); if (samples > 0) -- cgit v1.2.3 From d7f88158050122c28e078fd407322cbf53a875fa Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 14 Jul 2016 12:58:52 +0200 Subject: Fix up QQuickWidget and QQuickRenderControl docs Change-Id: I4df82c62afc12dace513ccbf6475d980c041867e Reviewed-by: Andy Nichols --- src/quick/items/qquickrendercontrol.cpp | 9 +++++++-- src/quickwidgets/qquickwidget.cpp | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index 8c18c58224..8b69447002 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -124,6 +124,10 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_ To send events, for example mouse or keyboard events, to the scene, use QCoreApplication::sendEvent() with the QQuickWindow instance as the receiver. + \note In general QQuickRenderControl is supported in combination with all Qt + Quick backends. However, some functionality, in particular grab(), may not be + available in all cases. + \inmodule QtQuick */ @@ -209,8 +213,9 @@ void QQuickRenderControl::prepareThread(QThread *targetThread) } /*! - Initializes the scene graph resources. The context \a gl has to - be the current context. + Initializes the scene graph resources. The context \a gl has to be the + current OpenGL context or null if it is not relevant because a Qt Quick + backend other than OpenGL is in use. \note Qt Quick does not take ownership of the context. It is up to the application to destroy it after a call to invalidate() or after the diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index b25e2d348d..ad04c76783 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -468,6 +468,13 @@ QObject *QQuickWidgetPrivate::focusObject() Qt::WA_TranslucentBackground on the top-level window, request an alpha channel, and change the Qt Quick Scenegraph's clear color to Qt::transparent via setClearColor(). + \section1 Support when not using OpenGL + + In addition to OpenGL, the \c software backend of Qt Quick also supports + QQuickWidget. Other backends, for example the Direct 3D 12 one, are not + compatible however and attempting to construct a QQuickWidget will lead to + problems. + \sa {Exposing Attributes of C++ Types to QML}, {Qt Quick Widgets Example}, QQuickView */ -- cgit v1.2.3 From f3200a1f979c4f77d6d5f8960c8f09230cbe622f Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 14 Jul 2016 14:09:28 +0200 Subject: software: Fix potential infinite loop with ShaderEffect With the software backend we entered an infinite loop when having ShaderEffect items. The previous change introducing the usage of polish requests to defer shader processing was a bit too keen on trying over and over again: there is exactly one retry attempt we need, not more. If the second fails too and everything looks initialized and ready, then we know the backend has no shader effect support so it is futile to try again. Change-Id: Ibaaefd45e5b26d0be42dbe7f9c080363b7a939bf Reviewed-by: Andy Nichols --- src/quick/items/qquickgenericshadereffect.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp index 683453d4ee..9714f39663 100644 --- a/src/quick/items/qquickgenericshadereffect.cpp +++ b/src/quick/items/qquickgenericshadereffect.cpp @@ -305,8 +305,16 @@ void QQuickGenericShaderEffect::maybeUpdateShaders() m_vertNeedsUpdate = !updateShader(Vertex, m_vertShader); if (m_fragNeedsUpdate) m_fragNeedsUpdate = !updateShader(Fragment, m_fragShader); - if (m_vertNeedsUpdate || m_fragNeedsUpdate) - m_item->polish(); + if (m_vertNeedsUpdate || m_fragNeedsUpdate) { + // This function is invoked either from componentComplete or in a + // response to a previous invocation's polish() request. If this is + // case #1 then updateShader can fail due to not having a window or + // scenegraph ready. Schedule the polish to try again later. In case #2 + // the backend probably does not have shadereffect support so there is + // nothing to do for us here. + if (!m_item->window() || !m_item->window()->isSceneGraphInitialized()) + m_item->polish(); + } } void QQuickGenericShaderEffect::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) -- cgit v1.2.3 From cba37804135970ef18743b276d71cc8c636ecc01 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 14 Jul 2016 14:19:13 +0200 Subject: D3D12: Mention QQuickWidget in the docs Change-Id: I778003c4110e825dc77aafff64f255ba4c9d7056 Reviewed-by: Andy Nichols --- src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index 57f1495c8e..45162def2b 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -311,6 +311,11 @@ Custom Qt Quick items using custom scenegraph nodes can be problematic. Materials are inherently tied to the graphics API. Therefore only items using the utility rectangle and image nodes are functional across all adaptations. +QQuickWidget and its underlying OpenGL-based compositing architecture is not +supported. If mixing with QWidget-based user interfaces is desired, use +QWidget::createWindowContainer() to embed the native window of the QQuickWindow +or QQuickView. + Finally, rendering via QSGEngine and QSGAbstractRenderer is not feasible with the D3D12 adaptation at the moment. -- cgit v1.2.3 From 0c43995e9427979fc046fe11366b44dc64ea6612 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 15 Jul 2016 11:36:57 +0200 Subject: D3D12: Fix unused var warnings in release builds Change-Id: I83c3582ca7cb9523fb0e90627f2575992e84694c Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp | 2 ++ src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp | 1 + src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp | 3 +-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp index 3ff8ce6e53..fc3ea4e22e 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp @@ -102,6 +102,7 @@ QSGMaterialType *QSGD3D12VertexColorMaterial::type() const int QSGD3D12VertexColorMaterial::compare(const QSGMaterial *other) const { + Q_UNUSED(other); Q_ASSERT(other && type() == other->type()); // As the vertex color material has all its state in the vertex attributes // defined by the geometry, all such materials will be equal. @@ -229,6 +230,7 @@ QSGMaterialType *QSGD3D12SmoothColorMaterial::type() const int QSGD3D12SmoothColorMaterial::compare(const QSGMaterial *other) const { + Q_UNUSED(other); Q_ASSERT(other && type() == other->type()); return 0; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index f17ed10c94..ec3427a9cc 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -290,6 +290,7 @@ void QSGD3D12RenderLoop::releaseResources(QQuickWindow *window) void QSGD3D12RenderLoop::postJob(QQuickWindow *window, QRunnable *job) { + Q_UNUSED(window); Q_ASSERT(job); Q_ASSERT(window); job->run(); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp index d459ec29ab..c423206377 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp @@ -136,8 +136,7 @@ void QSGD3D12ShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &s for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) { const auto &var(shader.shaderInfo.variables.at(i)); if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) { - const auto &vd(shader.varData.at(i)); - Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Unused); + Q_ASSERT(shader.varData.at(i).specialType == QSGShaderEffectNode::VariableData::Unused); samplers.insert(var.bindPoint); } } -- cgit v1.2.3 From 96cc0d7b4cd4de6a4a96af5a5174a4bc9bb4e5cd Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 29 Jun 2016 16:05:12 +0200 Subject: QML: Allow for inlining InternalClass::find This method is used in ExecutionEngine::getProperty, which is called quite often. Change-Id: Ide49d158005ef1d9f51d1e734cf9e3b19f52cf26 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4engine_p.h | 4 +++- src/qml/jsruntime/qv4internalclass.cpp | 26 -------------------------- src/qml/jsruntime/qv4internalclass_p.h | 30 +++++++++++++++++++++++++++++- src/qml/jsruntime/qv4object_p.h | 1 + 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index ada6941381..8743df771c 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -54,7 +54,6 @@ #include "private/qv4isel_p.h" #include "qv4managed_p.h" #include "qv4context_p.h" -#include "qv4internalclass_p.h" #include "qv4runtimeapi_p.h" #include @@ -86,6 +85,9 @@ namespace CompiledData { struct CompilationUnit; } +struct InternalClass; +struct InternalClassPool; + struct Q_QML_EXPORT ExecutionEngine { private: diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 9660a5e76d..f46f581168 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -101,20 +101,6 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) ++d->size; } -uint PropertyHash::lookup(const Identifier *identifier) const -{ - Q_ASSERT(d->entries); - - uint idx = identifier->hashValue % d->alloc; - while (1) { - if (d->entries[idx].identifier == identifier) - return d->entries[idx].index; - if (!d->entries[idx].identifier) - return UINT_MAX; - ++idx; - idx %= d->alloc; - } -} InternalClass::InternalClass(ExecutionEngine *engine) : engine(engine) @@ -360,18 +346,6 @@ void InternalClass::removeMember(Object *object, Identifier *id) Q_ASSERT(t.lookup); } -uint InternalClass::find(const String *string) -{ - engine->identifierTable->identifier(string); - const Identifier *id = string->d()->identifier; - - uint index = propertyTable.lookup(id); - if (index < size) - return index; - - return UINT_MAX; -} - uint InternalClass::find(const Identifier *id) { uint index = propertyTable.lookup(id); diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index c10af4ce01..dcda949c97 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -54,6 +54,8 @@ #include #include +#include +#include QT_BEGIN_NAMESPACE @@ -117,6 +119,20 @@ inline PropertyHash::~PropertyHash() delete d; } +inline uint PropertyHash::lookup(const Identifier *identifier) const +{ + Q_ASSERT(d->entries); + + uint idx = identifier->hashValue % d->alloc; + while (1) { + if (d->entries[idx].identifier == identifier) + return d->entries[idx].index; + if (!d->entries[idx].identifier) + return UINT_MAX; + ++idx; + idx %= d->alloc; + } +} template struct SharedInternalClassData { @@ -245,7 +261,7 @@ struct InternalClass : public QQmlJS::Managed { InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = 0); static void removeMember(Object *object, Identifier *id); - uint find(const String *s); + uint find(const String *string); uint find(const Identifier *id); InternalClass *sealed(); @@ -261,6 +277,18 @@ private: InternalClass(const InternalClass &other); }; +inline uint InternalClass::find(const String *string) +{ + engine->identifierTable->identifier(string); + const Identifier *id = string->d()->identifier; + + uint index = propertyTable.lookup(id); + if (index < size) + return index; + + return UINT_MAX; +} + struct InternalClassPool : public QQmlJS::MemoryPool { void markObjects(ExecutionEngine *engine); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 114f542d18..b68dbc27b7 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -56,6 +56,7 @@ #include "qv4engine_p.h" #include "qv4scopedvalue_p.h" #include "qv4value_p.h" +#include "qv4internalclass_p.h" QT_BEGIN_NAMESPACE -- cgit v1.2.3 From 30e30b98669bc51c77416f8c1ba68e348848be23 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 14 Jul 2016 15:31:53 +0200 Subject: Add QQuickEventPoint *QQuickPointerEvent::pointById(int) Change-Id: Id6a41545036b7fe37d5b486789f77642a4241d9a Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 22 ++++++++++++++++++++++ src/quick/items/qquickevents_p_p.h | 1 + 2 files changed, 23 insertions(+) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index d09294b515..68284843b6 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -619,6 +619,28 @@ QMouseEvent *QQuickPointerEvent::syntheticMouseEvent(int pointID, QQuickItem *re return &m_synthMouseEvent; } +/*! + \internal + Returns a pointer to the QQuickEventPoint which has the \a pointId as + \l {QQuickEventPoint::pointId}{pointId}. + Returns nullptr if there is no point with that ID. +*/ +QQuickEventPoint *QQuickPointerEvent::pointById(quint64 pointId) { + if (isMouseEvent()) { + if (m_mousePoint && pointId == m_mousePoint->pointId()) + return m_mousePoint; + } + if (isTouchEvent()) { + auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(), + [pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } ); + if (it != m_touchPoints.constEnd()) { + return *it; + } + } + // TODO it could alternatively be a tablet point + return nullptr; +} + /*! \internal Returns a pointer to the original TouchPoint which has the same diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 65a9ae190a..303b14b5cf 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -418,6 +418,7 @@ public: // helpers for C++ only (during event delivery) int pointCount() const { return m_pointCount; } const QQuickEventPoint *point(int i) const; + QQuickEventPoint *pointById(quint64 pointId); const QTouchEvent::TouchPoint *touchPointById(int pointId) const; -- cgit v1.2.3 From 882f83f8f58a8f2635b1f38425f5fe13e6ddd1ec Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 14 Jul 2016 17:37:23 +0200 Subject: QQuickEventPoint: keep the timestamps, remember when pressed Some handlers will care for how long the point has been held so far. Change-Id: I390d92988619054918fcdecd4b092ca9b4cfdea0 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 4 ++-- src/quick/items/qquickevents_p_p.h | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 68284843b6..4f677e841c 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -494,7 +494,7 @@ void QQuickPointerEvent::initFromMouse(QMouseEvent *ev) { if (!m_mousePoint) m_mousePoint = new QQuickEventPoint; m_pointCount = 1; - m_mousePoint->reset(state, ev->windowPos(), 0); // mouse is 0 + m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0 } void QQuickPointerEvent::initFromTouch(QTouchEvent *ev) { @@ -510,7 +510,7 @@ void QQuickPointerEvent::initFromTouch(QTouchEvent *ev) { m_touchPoints.insert(i, new QQuickEventTouchPoint); for (int i = 0; i < m_pointCount; ++i) - m_touchPoints.at(i)->reset(tps.at(i)); + m_touchPoints.at(i)->reset(tps.at(i), ev->timestamp()); } /*! diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 303b14b5cf..38c333d2a4 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -314,21 +314,27 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject Q_PROPERTY(QPointF scenePos READ scenePos) Q_PROPERTY(Qt::TouchPointState state READ state) Q_PROPERTY(quint64 pointId READ pointId) + Q_PROPERTY(qreal timeHeld READ timeHeld) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) public: - QQuickEventPoint() : QObject(), m_pointId(0), m_state(Qt::TouchPointReleased), m_valid(false), m_accept(false) + QQuickEventPoint() : QObject(), m_pointId(0), m_timestamp(0), m_pressTimestamp(0), + m_state(Qt::TouchPointReleased), m_valid(false), m_accept(false) { Q_UNUSED(m_reserved); } - void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId) + void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp) { m_scenePos = scenePos; m_pointId = pointId; m_valid = true; m_accept = false; m_state = state; + m_timestamp = timestamp; + if (state == Qt::TouchPointPressed) + m_pressTimestamp = timestamp; + // TODO calculate velocity } void invalidate() { m_valid = false; } @@ -337,12 +343,15 @@ public: Qt::TouchPointState state() const { return m_state; } quint64 pointId() const { return m_pointId; } bool isValid() const { return m_valid; } + qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } bool isAccepted() const { return m_accept; } void setAccepted(bool accepted = true) { m_accept = accepted; } private: QPointF m_scenePos; quint64 m_pointId; + ulong m_timestamp; + ulong m_pressTimestamp; Qt::TouchPointState m_state; bool m_valid : 1; bool m_accept : 1; @@ -359,10 +368,9 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint public: QQuickEventTouchPoint() : QQuickEventPoint(), m_rotation(0), m_pressure(0) { } - void reset(const QTouchEvent::TouchPoint &tp) + void reset(const QTouchEvent::TouchPoint &tp, ulong timestamp) { - QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id()); - // TODO times and velocity + QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp); m_rotation = tp.rotation(); m_pressure = tp.pressure(); m_uniqueId = tp.uniqueId(); -- cgit v1.2.3 From 6ce37a1eed9a41b036872bbbac2a5e73c6cbf9eb Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 14 Jul 2016 11:22:00 +0200 Subject: qquickevents_p_p.h: move declaration of device below QQuickPointerEvent In a following patch, the device will own the event instance by value, so it needs to be defined first. Change-Id: Id1520257115889198c3e9ff20c56028c0b6d4bd2 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents_p_p.h | 173 +++++++++++++++++++------------------ 1 file changed, 87 insertions(+), 86 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 38c333d2a4..b798165861 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -222,92 +222,6 @@ private: bool _accepted; }; -// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent -class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject -{ - Q_OBJECT - Q_PROPERTY(DeviceType type READ type CONSTANT) - Q_PROPERTY(PointerType pointerType READ pointerType CONSTANT) - Q_PROPERTY(Capabilities capabilities READ capabilities CONSTANT) - Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints CONSTANT) - Q_PROPERTY(int buttonCount READ buttonCount CONSTANT) - Q_PROPERTY(QString name READ name CONSTANT) - Q_PROPERTY(qint64 uniqueId READ uniqueId CONSTANT) - -public: - enum DeviceType { - UnknownDevice = 0x0000, - Mouse = 0x0001, - TouchScreen = 0x0002, - TouchPad = 0x0004, - Puck = 0x0008, - Stylus = 0x0010, - Airbrush = 0x0020, - AllDevices = 0x003F - }; - Q_DECLARE_FLAGS(DeviceTypes, DeviceType) - Q_ENUM(DeviceType) - Q_FLAG(DeviceTypes) - - enum PointerType { - GenericPointer = 0x0001, - Finger = 0x0002, - Pen = 0x0004, - Eraser = 0x0008, - Cursor = 0x0010, - AllPointerTypes = 0x001F - }; - Q_DECLARE_FLAGS(PointerTypes, PointerType) - Q_ENUM(PointerType) - Q_FLAG(PointerTypes) - - enum CapabilityFlag { - Position = QTouchDevice::Position, - Area = QTouchDevice::Area, - Pressure = QTouchDevice::Pressure, - Velocity = QTouchDevice::Velocity, - // some bits reserved in case we need more of QTouchDevice::Capabilities - Scroll = 0x0100, // mouse has a wheel, or there is OS-level scroll gesture recognition (dubious?) - Hover = 0x0200, - Rotation = 0x0400, - XTilt = 0x0800, - YTilt = 0x1000 - }; - Q_DECLARE_FLAGS(Capabilities, CapabilityFlag) - Q_ENUM(CapabilityFlag) - Q_FLAG(Capabilities) - - QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) - : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) - , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId) - {} - - ~QQuickPointerDevice() { } - DeviceType type() const { return m_deviceType; } - PointerType pointerType() const { return m_pointerType; } - Capabilities capabilities() const { return m_capabilities; } - bool hasCapability(CapabilityFlag cap) { return m_capabilities & cap; } - int maximumTouchPoints() const { return m_maximumTouchPoints; } - int buttonCount() const { return m_buttonCount; } - QString name() const { return m_name; } - qint64 uniqueId() const { return m_uniqueId; } - -private: - DeviceType m_deviceType; - PointerType m_pointerType; - Capabilities m_capabilities; - int m_maximumTouchPoints; - int m_buttonCount; - QString m_name; - qint64 m_uniqueId; - - Q_DISABLE_COPY(QQuickPointerDevice) -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes) -Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::PointerTypes) -Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::Capabilities) - class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject { Q_OBJECT @@ -387,6 +301,7 @@ private: }; class QQuickItem; +class QQuickPointerDevice; class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject { @@ -451,6 +366,92 @@ private: Q_DISABLE_COPY(QQuickPointerEvent) }; +// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent +class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject +{ + Q_OBJECT + Q_PROPERTY(DeviceType type READ type CONSTANT) + Q_PROPERTY(PointerType pointerType READ pointerType CONSTANT) + Q_PROPERTY(Capabilities capabilities READ capabilities CONSTANT) + Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints CONSTANT) + Q_PROPERTY(int buttonCount READ buttonCount CONSTANT) + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(qint64 uniqueId READ uniqueId CONSTANT) + +public: + enum DeviceType { + UnknownDevice = 0x0000, + Mouse = 0x0001, + TouchScreen = 0x0002, + TouchPad = 0x0004, + Puck = 0x0008, + Stylus = 0x0010, + Airbrush = 0x0020, + AllDevices = 0x003F + }; + Q_DECLARE_FLAGS(DeviceTypes, DeviceType) + Q_ENUM(DeviceType) + Q_FLAG(DeviceTypes) + + enum PointerType { + GenericPointer = 0x0001, + Finger = 0x0002, + Pen = 0x0004, + Eraser = 0x0008, + Cursor = 0x0010, + AllPointerTypes = 0x001F + }; + Q_DECLARE_FLAGS(PointerTypes, PointerType) + Q_ENUM(PointerType) + Q_FLAG(PointerTypes) + + enum CapabilityFlag { + Position = QTouchDevice::Position, + Area = QTouchDevice::Area, + Pressure = QTouchDevice::Pressure, + Velocity = QTouchDevice::Velocity, + // some bits reserved in case we need more of QTouchDevice::Capabilities + Scroll = 0x0100, // mouse has a wheel, or there is OS-level scroll gesture recognition (dubious?) + Hover = 0x0200, + Rotation = 0x0400, + XTilt = 0x0800, + YTilt = 0x1000 + }; + Q_DECLARE_FLAGS(Capabilities, CapabilityFlag) + Q_ENUM(CapabilityFlag) + Q_FLAG(Capabilities) + + QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) + : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) + , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId) + {} + + ~QQuickPointerDevice() { } + DeviceType type() const { return m_deviceType; } + PointerType pointerType() const { return m_pointerType; } + Capabilities capabilities() const { return m_capabilities; } + bool hasCapability(CapabilityFlag cap) { return m_capabilities & cap; } + int maximumTouchPoints() const { return m_maximumTouchPoints; } + int buttonCount() const { return m_buttonCount; } + QString name() const { return m_name; } + qint64 uniqueId() const { return m_uniqueId; } + +private: + DeviceType m_deviceType; + PointerType m_pointerType; + Capabilities m_capabilities; + int m_maximumTouchPoints; + int m_buttonCount; + QString m_name; + qint64 m_uniqueId; + + Q_DISABLE_COPY(QQuickPointerDevice) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::PointerTypes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::Capabilities) + QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickKeyEvent) -- cgit v1.2.3 From 288c1425d6e3ac200dd8e687f13de6ce4a41260b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 14 Jul 2016 11:17:54 +0200 Subject: QQuickWindow: keep a collection of devices which own event instances That will allow the event instances to hold state, even between events. So now every QQuickWindow has its own set of them, per device. Hopefully that means we won't have any trouble delivering events in parallel in case each window has its own thread. Otherwise maybe it's slightly wasteful in multi-window apps. Change-Id: I766b580e1c177255905cc04b5de7d33ae503c6fd Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 4 ++-- src/quick/items/qquickevents_p_p.h | 3 +++ src/quick/items/qquickwindow.cpp | 45 ++++++++++++++++++++++++++++++++------ src/quick/items/qquickwindow_p.h | 8 ++++--- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 4f677e841c..911cc9a52c 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -450,8 +450,8 @@ Item { to expose the event to QML, the same as is done with QQuickMouseEvent, QQuickTouchPoint, QQuickKeyEvent, etc. Since only one event can be delivered at a time, this class is effectively a singleton. We don't worry - about the QObject overhead because we never dynamically create and destroy - objects of this type. + about the QObject overhead because the instances are long-lived: we don't + dynamically create and destroy objects of this type for each event. */ /*! diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index b798165861..624c131683 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -435,6 +435,7 @@ public: int buttonCount() const { return m_buttonCount; } QString name() const { return m_name; } qint64 uniqueId() const { return m_uniqueId; } + QQuickPointerEvent *pointerEvent() const { return &m_event; } private: DeviceType m_deviceType; @@ -444,6 +445,8 @@ private: int m_buttonCount; QString m_name; qint64 m_uniqueId; + // the device-specific event instance which is reused during event delivery + mutable QQuickPointerEvent m_event; Q_DISABLE_COPY(QQuickPointerDevice) }; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index af72ff3af2..22086c577c 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1892,7 +1892,7 @@ void QQuickWindowPrivate::deliverDelayedTouchEvent() // Set delayedTouch to 0 before delivery to avoid redelivery in case of // event loop recursions (e.g if it the touch starts a dnd session). QScopedPointer e(delayedTouch.take()); - deliverPointerEvent(currentPointerEvent.reset(e.data())); + deliverPointerEvent(pointerEventInstance(e.data())); } static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION"); @@ -1971,14 +1971,14 @@ void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) qCDebug(DBG_TOUCH) << event; if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) { - deliverPointerEvent(currentPointerEvent.reset(event)); + deliverPointerEvent(pointerEventInstance(event)); return; } if (!compressTouchEvent(event)) { if (delayedTouch) deliverDelayedTouchEvent(); - deliverPointerEvent(currentPointerEvent.reset(event)); + deliverPointerEvent(pointerEventInstance(event)); } } @@ -2019,7 +2019,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonPress: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(), event->buttons()); - deliverPointerEvent(currentPointerEvent.reset(event)); + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseButtonRelease: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(), @@ -2029,14 +2029,14 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) return; } - deliverPointerEvent(currentPointerEvent.reset(event)); + deliverPointerEvent(pointerEventInstance(event)); if (mouseGrabberItem && !event->buttons()) mouseGrabberItem->ungrabMouse(); break; case QEvent::MouseButtonDblClick: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, event->button(), event->buttons()); - deliverPointerEvent(currentPointerEvent.reset(event)); + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseMove: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, @@ -2063,7 +2063,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) event->setAccepted(accepted); return; } - deliverPointerEvent(currentPointerEvent.reset(event)); + deliverPointerEvent(pointerEventInstance(event)); break; default: Q_ASSERT(false); @@ -2095,6 +2095,37 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() } } +/*! + \internal + Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event. + + There is a unique instance per QQuickPointerDevice, which is determined + from \a event's device. +*/ +QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) +{ + QQuickPointerDevice *dev = nullptr; + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + dev = genericMouseDevice; + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + case QEvent::TouchCancel: + dev = touchDevice(static_cast(event)->device()); + break; + // TODO tablet event types + default: + break; + } + Q_ASSERT(dev); + return dev->pointerEvent()->reset(event); +} + void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) { // If users spin the eventloop as a result of event delivery, we disable diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 7183d17514..ab704b9adf 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -163,6 +163,7 @@ public: void deliverDelayedTouchEvent(); // delivery of pointer events: + QQuickPointerEvent *pointerEventInstance(QEvent *ev); void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerEvent *); bool deliverTouchCancelEvent(QTouchEvent *); @@ -245,9 +246,10 @@ public: QQuickAnimatorController *animationController; QScopedPointer delayedTouch; - // The current touch or mouse event that is delivered. - // This event gets re-used (reset) for every incoming mouse/touch event. - QQuickPointerEvent currentPointerEvent; + // An event instance for each device that we've seen so far. + // One of these gets re-used (reset) for every incoming mouse/touch/tablet event. + // One reason to keep them separate is so that m_touchPoints will be only those from a single device. + QVector pointerEventsByDevice; int pointerEventRecursionGuard; QQuickCustomRenderStage *customRenderStage; -- cgit v1.2.3 From 0ce892a275fd018dd306f87c308c42c46cc768ae Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Fri, 15 Jul 2016 17:26:25 +0300 Subject: Android: Fix crash Make sure all the sturcture fileds are aligned Change-Id: I6f6dad67262e50026f7e86320bcd818a478ebc35 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compileddata_p.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index a1d0a6d732..1e65be38da 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -204,7 +204,6 @@ struct Function HasCatchOrWith = 0x10 }; - quint8 flags; LEUInt32 nameIndex; LEUInt32 nFormals; LEUInt32 formalsOffset; @@ -232,6 +231,9 @@ struct Function // quint32 offsetForInnerFunctions[nInnerFunctions] // Function[nInnerFunctions] + // Keep all unaligned data at the end + quint8 flags; + const LEUInt32 *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } const LEUInt32 *localsTable() const { return reinterpret_cast(reinterpret_cast(this) + localsOffset); } const LEUInt32 *qmlIdObjectDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingIdObjectsOffset); } -- cgit v1.2.3 From 592286d1092e403249a58b52775fac2bcce2cdb1 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 15 Jul 2016 11:56:31 +0200 Subject: D3D12: Enhance the advanced config section in the docs Change-Id: If8531f1b7ed10085fee77c7f1c7d9f3976a10817 Reviewed-by: Andy Nichols --- .../doc/src/concepts/visualcanvas/adaptations.qdoc | 37 +++++++++++++--------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index 45162def2b..bbc64f1484 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -344,7 +344,8 @@ QQuickWindow::createRectangleNode() and QQuickWindow::createImageNode(). The D3D12 adaptation can keep multiple frames in flight, similarly to modern game engines. This is somewhat different from the traditional render - swap - wait for vsync model and allows better GPU utilization at the expense of higher -resource usage. +resource usage. This means that the renderer will be a number of frames ahead +of what is displayed on the screen. For a discussion of flip model swap chains and the typical configuration parameters, refer to @@ -358,25 +359,31 @@ The configuration can be changed by setting the following environment variables: \list -\li \c{QT_D3D_BUFFER_COUNT} - The number of swap chain buffers. Defaults to 3. +\li \c{QT_D3D_BUFFER_COUNT} - The number of swap chain buffers in range 2 - 4. +The default value is 3. -\li \c{QT_D3D_FRAME_COUNT} - The number of frames prepared without blocking. -Defaults to 2. Note that Present will start blocking after queuing 3 frames +\li \c{QT_D3D_FRAME_COUNT} - The number of frames prepared without blocking in +range 1 - 4. Note that Present will start blocking after queuing 3 frames (regardless of \c{QT_D3D_BUFFER_COUNT}), unless the waitable object is in use. Note that every additional frame increases GPU resource usage since geometry and constant buffer data will have to be duplicated, and involves more -bookkeeping on the CPU side. - -\li \c{QT_D3D_WAITABLE_SWAP_CHAIN_MAX_LATENCY} - When set, the frame latency is -set to the specified value (0-16). This changes the limit for Present() and -will trigger a wait for an available swap chain buffer when beginning each -frame. By default this is disabled. Refer to the article above for a detailed -discussion. +bookkeeping on the CPU side. The default value is 2. + +\li \c{QT_D3D_WAITABLE_SWAP_CHAIN_MAX_LATENCY} - When set to a value between 1 +and 16, the frame latency is set to the specified value. This changes the limit +for Present() and will trigger a wait for an available swap chain buffer when +beginning each frame. Refer to the article above for a detailed discussion. +This is considered experimental for now and the default value is 0 (disabled). + +\li \c{QT_D3D_BLOCKING_PRESENT} - When set to a non-zero value, there will be +CPU-side wait for the GPU to finish its work after each call to Present. This +effectively kills all parallelism but makes the behavior resemble the +traditional swap-blocks-for-vsync model, and can therefore be useful in some +special cases. This is not the same as setting the frame count to 1 because +that still avoids blocking after Present, and may block only when starting to +prepare the next frame (or may not block at all depending on the time gap +between the frames). By default blocking present is disabled. \endlist -Note that typical Qt Quick applications are expected to generate fairly low -workloads compared to true 3D applications like games. Therefore this level of -tuning will likely be unnecessary in most cases. - */ -- cgit v1.2.3 From 0308a8379bf9106a9f6ba4c03b337905213a9f0d Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 15 Jul 2016 13:00:04 +0200 Subject: Improve the frameSwapped docs somewhat "frame buffers have been swapped" is a bit underspecified and is quite confusing outside the EGL/GLX/WGL world. Change-Id: Ib4b267c0b74a101e78b9368556d80aeee6ccbc4b Reviewed-by: Andy Nichols --- src/quick/items/qquickwindow.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 296e252c69..af91722432 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3310,7 +3310,9 @@ bool QQuickWindow::isSceneGraphInitialized() const /*! \fn void QQuickWindow::frameSwapped() - This signal is emitted when the frame buffers have been swapped. + This signal is emitted when a frame has been queued for presenting. With + vertical synchronization enabled the signal is emitted at most once per + vsync interval in a continuously animating scene. This signal will be emitted from the scene graph rendering thread. */ -- cgit v1.2.3 From bc2a25dfc18dbb63e404b031c82465c5d21f2775 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 15 Jul 2016 13:15:42 +0200 Subject: Make scenegraph plugin logs and warnings work in release builds Ifdefing with no_debug_output is a bad idea in this case, these are messages we want in any builds. Change-Id: Ic842afe9b48961fb7fb010db868f92f8e85184c3 Reviewed-by: Andy Nichols --- src/quick/scenegraph/qsgcontextplugin.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp index 7569cd2495..3751891455 100644 --- a/src/quick/scenegraph/qsgcontextplugin.cpp +++ b/src/quick/scenegraph/qsgcontextplugin.cpp @@ -136,9 +136,7 @@ QSGAdaptationBackendData *contextFactory() #endif if (!requestedBackend.isEmpty()) { -#ifndef QT_NO_DEBUG qCDebug(QSG_LOG_INFO) << "Loading backend" << requestedBackend; -#endif // First look for a built-in adaptation. for (QSGContextFactoryInterface *builtInBackend : qAsConst(backendData->builtIns)) { @@ -160,14 +158,12 @@ QSGAdaptationBackendData *contextFactory() backendData->name = requestedBackend; backendData->flags = backendData->factory->flags(requestedBackend); } -#ifndef QT_NO_DEBUG if (!backendData->factory) { qWarning("Could not create scene graph context for backend '%s'" " - check that plugins are installed correctly in %s", qPrintable(requestedBackend), qPrintable(QLibraryInfo::location(QLibraryInfo::PluginsPath))); } -#endif } #endif // QT_NO_LIBRARY } -- cgit v1.2.3 From f1736b9a94a8bce40a26efba766e935ae6865530 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 15 Jul 2016 14:49:19 +0200 Subject: D3D12: Change the command list size limit 128 is way too low. Experiments with qmlbench show that there is not much improvement after 4000 so go with 4096. Change-Id: I1fd8f4f55250b5e54d95adb28b4ea1f4fe5ca10a Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 0800bdb8ad..d19dfe2db8 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -75,11 +75,14 @@ DECLARE_DEBUG_VAR(render) Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.general") +// Any changes to the defaults below must be reflected in adaptations.qdoc as +// well and proven by qmlbench or similar. + static const int DEFAULT_SWAP_CHAIN_BUFFER_COUNT = 3; static const int DEFAULT_FRAME_IN_FLIGHT_COUNT = 2; static const int DEFAULT_WAITABLE_SWAP_CHAIN_MAX_LATENCY = 0; -static const int MAX_DRAW_CALLS_PER_LIST = 128; +static const int MAX_DRAW_CALLS_PER_LIST = 4096; static const int MAX_CACHED_ROOTSIG = 16; static const int MAX_CACHED_PSO = 64; -- cgit v1.2.3 From 1b0a6a073e5b79121606312ac3fcfd267ba462ae Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 15 Jul 2016 13:19:22 +0200 Subject: Add QQuickPointerEvent::allPointsAccepted Convenience method to decide if the event is completely accepted or further delivery makes sense. Change-Id: I389a975cca2966962848203f69ec7f2e307a210a Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 15 +++++++++++++++ src/quick/items/qquickevents_p_p.h | 1 + 2 files changed, 16 insertions(+) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 911cc9a52c..d7b1f7573b 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -573,6 +573,21 @@ const QQuickEventPoint *QQuickPointerEvent::point(int i) const { return nullptr; } +bool QQuickPointerEvent::allPointsAccepted() const +{ + Q_ASSERT(m_event && !isTabletEvent()); + if (isMouseEvent()) { + return m_mousePoint->isAccepted(); + } + if (isTouchEvent()) { + for (int i = 0; i < m_pointCount; ++i) { + if (!m_touchPoints.at(i)->isAccepted()) + return false; + } + } + return true; +} + /*! \internal Populate the reusable synth-mouse event from one touchpoint. diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 624c131683..5dea26a2a5 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -338,6 +338,7 @@ public: // helpers for C++ only (during event delivery) bool isTouchEvent() const; bool isTabletEvent() const; bool isValid() const { return m_event != nullptr; } + bool allPointsAccepted() const; int pointCount() const { return m_pointCount; } const QQuickEventPoint *point(int i) const; -- cgit v1.2.3 From 2e8ab387a4aa5aeaf4840cb4255939cb68d9225f Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 15 Jul 2016 13:25:05 +0200 Subject: Be consistent about const The functions should be const, but the returned point not. This allows accepting the points. Change-Id: Iedbe8f3be38d672b33eea352f959b6e2bb6bc4fc Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 4 ++-- src/quick/items/qquickevents_p_p.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index d7b1f7573b..5f6cadaf00 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -563,7 +563,7 @@ bool QQuickPointerEvent::isTabletEvent() const } } -const QQuickEventPoint *QQuickPointerEvent::point(int i) const { +QQuickEventPoint *QQuickPointerEvent::point(int i) const { if (Q_UNLIKELY(i < 0 || i >= m_pointCount)) return nullptr; if (isTouchEvent()) @@ -640,7 +640,7 @@ QMouseEvent *QQuickPointerEvent::syntheticMouseEvent(int pointID, QQuickItem *re \l {QQuickEventPoint::pointId}{pointId}. Returns nullptr if there is no point with that ID. */ -QQuickEventPoint *QQuickPointerEvent::pointById(quint64 pointId) { +QQuickEventPoint *QQuickPointerEvent::pointById(quint64 pointId) const { if (isMouseEvent()) { if (m_mousePoint && pointId == m_mousePoint->pointId()) return m_mousePoint; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 5dea26a2a5..ca929bfab5 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -341,8 +341,8 @@ public: // helpers for C++ only (during event delivery) bool allPointsAccepted() const; int pointCount() const { return m_pointCount; } - const QQuickEventPoint *point(int i) const; - QQuickEventPoint *pointById(quint64 pointId); + QQuickEventPoint *point(int i) const; + QQuickEventPoint *pointById(quint64 pointId) const; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; -- cgit v1.2.3 From bf5f97e6c6900535a7131c6a26415459bfaca774 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Jul 2016 16:31:19 +0200 Subject: Fix alias resolution algorithm The resolution of aliases needs to to be solved in iterations as an alias can refer to another alias, which may not be created yet in the property cache by the time we try to perform the resolution. Therefore the alias resolution code works off a list of pending aliases after the initial pass of resolution. This also requires the ability to detect circular references. Change-Id: Id4e159b9c713aa8a8a095759e22e3fac456a38a0 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 342 ++++++++++++++------- src/qml/compiler/qqmltypecompiler_p.h | 12 +- src/qml/compiler/qv4compileddata_p.h | 4 +- src/qml/qml/qqmlvmemetaobject.cpp | 3 + .../auto/qml/qqmllanguage/data/alias.14.errors.txt | 1 + tests/auto/qml/qqmllanguage/data/alias.14.qml | 17 + .../qqmllanguage/data/invalidAlias.11.errors.txt | 1 + .../auto/qml/qqmllanguage/data/invalidAlias.11.qml | 10 + tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 19 +- 9 files changed, 300 insertions(+), 109 deletions(-) create mode 100644 tests/auto/qml/qqmllanguage/data/alias.14.errors.txt create mode 100644 tests/auto/qml/qqmllanguage/data/alias.14.qml create mode 100644 tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt create mode 100644 tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 42e31ea7f8..83aaba791b 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -993,137 +993,271 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) bool QQmlComponentAndAliasResolver::resolveAliases() { - foreach (int objectIndex, _objectsWithAliases) { - const QmlIR::Object *obj = qmlObjects->at(objectIndex); + if (_objectsWithAliases.isEmpty()) + return true; + bool atLeastOneAliasResolved; + do { + atLeastOneAliasResolved = false; + QVector pendingObjects; - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); - Q_ASSERT(propertyCache); + for (int objectIndex: qAsConst(_objectsWithAliases)) { - int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); - int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); + QQmlCompileError error; + const auto result = resolveAliasesInObject(objectIndex, &error); - int aliasIndex = 0; - for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next, ++aliasIndex) { - const int idIndex = alias->idIndex; - const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); - if (targetObjectIndex == -1) { - recordError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); + if (error.isSet()) { + recordError(error); return false; } - Q_ASSERT(!(alias->flags & QV4::CompiledData::Alias::Resolved)); - alias->flags |= QV4::CompiledData::Alias::Resolved; - const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); - Q_ASSERT(targetObject->id >= 0); - alias->targetObjectId = targetObject->id; - - const QString aliasPropertyValue = stringAt(alias->propertyNameIndex); - - QStringRef property; - QStringRef subProperty; - - const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.')); - if (propertySeparator != -1) { - property = aliasPropertyValue.leftRef(propertySeparator); - subProperty = aliasPropertyValue.midRef(propertySeparator + 1); - } else - property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length()); - - int propIdx = -1; - int type = 0; - bool writable = false; - bool resettable = false; - - quint32 propertyFlags = QQmlPropertyData::IsAlias; - - if (property.isEmpty()) { - const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); - auto *typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - - if (typeRef->type) - type = typeRef->type->typeId(); - else - type = typeRef->compilationUnit->metaTypeId; - - alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; - propertyFlags |= QQmlPropertyData::IsQObjectDerived; + + if (result == AllAliasesResolved) { + addAliasesToPropertyCache(objectIndex); + atLeastOneAliasResolved = true; + } else if (result == SomeAliasesResolved) { + atLeastOneAliasResolved = true; + pendingObjects.append(objectIndex); } else { - QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); - Q_ASSERT(targetCache); - QmlIR::PropertyResolver resolver(targetCache); + pendingObjects.append(objectIndex); + } + } + qSwap(_objectsWithAliases, pendingObjects); + } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved); + + if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) { + const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first()); + for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) { + if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) { + recordError(alias->location, tr("Circular alias reference detected")); + return false; + } + } + } - QQmlPropertyData *targetProperty = resolver.property(property.toString()); - if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) { - recordError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); - return false; - } + return true; +} - propIdx = targetProperty->coreIndex; - type = targetProperty->propType; +QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlCompileError *error) +{ + const QmlIR::Object * const obj = qmlObjects->at(objectIndex); + if (!obj->aliasCount()) + return AllAliasesResolved; - writable = targetProperty->isWritable(); - resettable = targetProperty->isResettable(); + int numResolvedAliases = 0; + bool seenUnresolvedAlias = false; - if (!subProperty.isEmpty()) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type); - if (!valueTypeMetaObject) { - recordError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); - return false; - } + for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) { + if (alias->flags & QV4::CompiledData::Alias::Resolved) + continue; - int valueTypeIndex = - valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); - if (valueTypeIndex == -1) { - recordError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); - return false; - } - Q_ASSERT(valueTypeIndex <= 0x0000FFFF); + seenUnresolvedAlias = true; - propIdx = QQmlPropertyData::encodeValueTypePropertyIndex(propIdx, valueTypeIndex); - if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) - type = QVariant::Int; - else - type = valueTypeMetaObject->property(valueTypeIndex).userType(); + const int idIndex = alias->idIndex; + const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); + if (targetObjectIndex == -1) { + *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); + break; + } - } else { - if (targetProperty->isEnum()) { - type = QVariant::Int; - } else { - // Copy type flags - propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask; + const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); + Q_ASSERT(targetObject->id >= 0); + alias->targetObjectId = targetObject->id; + alias->aliasToLocalAlias = false; - if (targetProperty->isVarProperty()) - propertyFlags |= QQmlPropertyData::IsQVariant; + const QString aliasPropertyValue = stringAt(alias->propertyNameIndex); - if (targetProperty->isQObject()) - alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + QStringRef property; + QStringRef subProperty; + + const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.')); + if (propertySeparator != -1) { + property = aliasPropertyValue.leftRef(propertySeparator); + subProperty = aliasPropertyValue.midRef(propertySeparator + 1); + } else + property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length()); + + int propIdx = -1; + + if (property.isEmpty()) { + alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + } else { + QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); + Q_ASSERT(targetCache); + QmlIR::PropertyResolver resolver(targetCache); + + QQmlPropertyData *targetProperty = resolver.property(property.toString()); + + // If it's an alias that we haven't resolved yet, try again later. + if (!targetProperty) { + bool aliasPointsToOtherAlias = false; + int localAliasIndex = 0; + for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) { + if (stringAt(targetAlias->nameIndex) == property) { + aliasPointsToOtherAlias = true; + break; } } + if (aliasPointsToOtherAlias) { + if (targetObjectIndex == objectIndex) { + alias->localAliasIndex = localAliasIndex; + alias->aliasToLocalAlias = true; + alias->flags |= QV4::CompiledData::Alias::Resolved; + ++numResolvedAliases; + continue; + } + + // Try again later and resolve the target alias first. + _objectsWithAliases.append(objectIndex); + // restore + alias->idIndex = idIndex; + break; + } } - alias->encodedMetaPropertyIndex = propIdx; + if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) { + *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); + break; + } - if (!(alias->flags & QV4::CompiledData::Property::IsReadOnly) && writable) - propertyFlags |= QQmlPropertyData::IsWritable; - else - propertyFlags &= ~QQmlPropertyData::IsWritable; + propIdx = targetProperty->coreIndex; - if (resettable) - propertyFlags |= QQmlPropertyData::IsResettable; - else - propertyFlags &= ~QQmlPropertyData::IsResettable; + if (!subProperty.isEmpty()) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType); + if (!valueTypeMetaObject) { + *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); + break; + } - QString propertyName = stringAt(alias->nameIndex); + int valueTypeIndex = + valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); + if (valueTypeIndex == -1) { + *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); + break; + } + Q_ASSERT(valueTypeIndex <= 0x0000FFFF); - if (obj->defaultPropertyIsAlias && aliasIndex == obj->indexOfDefaultPropertyOrAlias) - propertyCache->_defaultPropertyName = propertyName; + propIdx = QQmlPropertyData::encodeValueTypePropertyIndex(propIdx, valueTypeIndex); + } else { + if (targetProperty->isQObject()) + alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + } + } - propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - type, effectiveSignalIndex++); + alias->encodedMetaPropertyIndex = propIdx; + alias->flags |= QV4::CompiledData::Alias::Resolved; + numResolvedAliases++; + } + if (numResolvedAliases == 0) + return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved; + + return SomeAliasesResolved; +} + +void QQmlComponentAndAliasResolver::propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags) +{ + const auto objectForId = [this](int objectId) { + for (auto idIt = _idToObjectIndex.constBegin(), end = _idToObjectIndex.constEnd(); idIt != end; ++idIt) { + const QmlIR::Object *obj = qmlObjects->at(*idIt); + if (obj->id == objectId) + return *idIt; + } + return -1; + }; + + const int targetObjectIndex = objectForId(alias->targetObjectId); + Q_ASSERT(targetObjectIndex >= 0); + + const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); + + *type = 0; + bool writable = false; + bool resettable = false; + + *propertyFlags = QQmlPropertyData::IsAlias; + + if (alias->aliasToLocalAlias) { + auto targetAlias = targetObject->firstAlias(); + for (uint i = 0; i < alias->localAliasIndex; ++i) + targetAlias = targetAlias->next; + propertyDataForAlias(targetAlias, type, propertyFlags); + return; + } else if (alias->encodedMetaPropertyIndex == -1) { + Q_ASSERT(alias->flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); + auto *typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + + if (typeRef->type) + *type = typeRef->type->typeId(); + else + *type = typeRef->compilationUnit->metaTypeId; + + *propertyFlags |= QQmlPropertyData::IsQObjectDerived; + } else { + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(alias->encodedMetaPropertyIndex, &coreIndex); + + QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); + Q_ASSERT(targetCache); + QQmlPropertyData *targetProperty = targetCache->property(coreIndex); + Q_ASSERT(targetProperty); + + *type = targetProperty->propType; + + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + + if (valueTypeIndex != -1) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); + if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) + *type = QVariant::Int; + else + *type = valueTypeMetaObject->property(valueTypeIndex).userType(); + } else { + if (targetProperty->isEnum()) { + *type = QVariant::Int; + } else { + // Copy type flags + *propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask; + + if (targetProperty->isVarProperty()) + *propertyFlags |= QQmlPropertyData::IsQVariant; + } } } - return true; + + if (!(alias->flags & QV4::CompiledData::Property::IsReadOnly) && writable) + *propertyFlags |= QQmlPropertyData::IsWritable; + else + *propertyFlags &= ~QQmlPropertyData::IsWritable; + + if (resettable) + *propertyFlags |= QQmlPropertyData::IsResettable; + else + *propertyFlags &= ~QQmlPropertyData::IsResettable; +} + +void QQmlComponentAndAliasResolver::addAliasesToPropertyCache(int objectIndex) +{ + const QmlIR::Object * const obj = qmlObjects->at(objectIndex); + QQmlPropertyCache * const propertyCache = propertyCaches.at(objectIndex); + Q_ASSERT(propertyCache); + int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); + int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); + + int aliasIndex = 0; + for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next, ++aliasIndex) { + int type = 0; + quint32 propertyFlags = 0; + propertyDataForAlias(alias, &type, &propertyFlags); + + QString propertyName = stringAt(alias->nameIndex); + + if (obj->defaultPropertyIsAlias && aliasIndex == obj->indexOfDefaultPropertyOrAlias) + propertyCache->_defaultPropertyName = propertyName; + + propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + type, effectiveSignalIndex++); + } } QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler) diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 93ae7d1fad..0b078fd642 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -266,6 +266,16 @@ protected: void findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache); bool collectIdsAndAliases(int objectIndex); bool resolveAliases(); + void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags); + + enum AliasResolutionResult { + NoAliasResolved, + SomeAliasesResolved, + AllAliasesResolved + }; + + AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error); + void addAliasesToPropertyCache(int objectIndex); QQmlEnginePrivate *enginePrivate; QQmlJS::MemoryPool *pool; @@ -278,7 +288,7 @@ protected: int _componentIndex; QHash _idToObjectIndex; - QList _objectsWithAliases; + QVector _objectsWithAliases; QHash *resolvedTypes; QQmlPropertyCacheVector propertyCaches; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 1e65be38da..f1cf365b25 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -446,11 +446,13 @@ struct Alias { }; union { LEUInt32 idIndex; // string index - LEUInt32 targetObjectId; // object id index (in QQmlContextData::idValues) + QJsonPrivate::qle_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues) + QJsonPrivate::qle_bitfield<31, 1> aliasToLocalAlias; }; union { LEUInt32 propertyNameIndex; // string index LEInt32 encodedMetaPropertyIndex; + LEUInt32 localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId) }; Location location; Location referenceLocation; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 0ce43e9383..d7f6c5b3af 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -845,6 +845,9 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (!ctxt) return -1; + while (aliasData->aliasToLocalAlias) + aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex]; + QQmlContext *context = ctxt->asQQmlContext(); QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context); diff --git a/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt b/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt new file mode 100644 index 0000000000..90a3ea4317 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt @@ -0,0 +1 @@ +10:34:References to other aliases within the same object are not supported at the moment diff --git a/tests/auto/qml/qqmllanguage/data/alias.14.qml b/tests/auto/qml/qqmllanguage/data/alias.14.qml new file mode 100644 index 0000000000..ff3c36d990 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.14.qml @@ -0,0 +1,17 @@ +import QtQml 2.0 + +QtObject { + id: root + property bool targetProperty: true + + property QtObject foo: QtObject { + id: otherSubObject + property alias theAliasOrigin: root.targetProperty + property alias theAlias: otherSubObject.theAliasOrigin + } + + property QtObject referencingSubObject: QtObject { + property alias success: otherSubObject.theAlias + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt new file mode 100644 index 0000000000..b79b660c46 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt @@ -0,0 +1 @@ +5:5:Circular alias reference detected diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml new file mode 100644 index 0000000000..9b50b48df8 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml @@ -0,0 +1,10 @@ +import QtQml 2.0 + +QtObject { + id: root + property alias a: subObject.b + property QtObject foo: QtObject { + id: subObject + property alias b: root.a + } +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 1e8e92217c..1df7c4157e 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -494,6 +494,7 @@ void tst_qqmllanguage::errors_data() QTest::newRow("invalidAlias.8") << "invalidAlias.8.qml" << "invalidAlias.8.errors.txt" << false; QTest::newRow("invalidAlias.9") << "invalidAlias.9.qml" << "invalidAlias.9.errors.txt" << false; QTest::newRow("invalidAlias.10") << "invalidAlias.10.qml" << "invalidAlias.10.errors.txt" << false; + QTest::newRow("invalidAlias.11") << "invalidAlias.11.qml" << "invalidAlias.11.errors.txt" << false; QTest::newRow("invalidAttachedProperty.1") << "invalidAttachedProperty.1.qml" << "invalidAttachedProperty.1.errors.txt" << false; QTest::newRow("invalidAttachedProperty.2") << "invalidAttachedProperty.2.qml" << "invalidAttachedProperty.2.errors.txt" << false; @@ -1821,8 +1822,6 @@ void tst_qqmllanguage::aliasProperties() { // This is known to fail at the moment. QQmlComponent component(&engine, testFileUrl("alias.13.qml")); - QVERIFY(!component.errors().isEmpty()); -#if 0 VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -1831,7 +1830,21 @@ void tst_qqmllanguage::aliasProperties() QVERIFY(!subObject.isNull()); QVERIFY(subObject->property("success").toBool()); -#endif + } + + // "Nested" aliases within an object that require iterative resolution + { + // This is known to fail at the moment. + QQmlComponent component(&engine, testFileUrl("alias.14.qml")); + VERIFY_ERRORS(0); + + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QPointer subObject = qvariant_cast(object->property("referencingSubObject")); + QVERIFY(!subObject.isNull()); + + QVERIFY(subObject->property("success").toBool()); } } -- cgit v1.2.3 From 8f19045ef5d308231716ddcb62a80cc43ebb2dd7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 20 Jun 2016 10:26:40 +0200 Subject: Split out creation of alias properties in the property caches Similar to the regular property cache creation code, this also has to become a template function so that it can be run on the compilation units loaded from disk in the future. What is shared between the code path of a fresh compilation vs. re-use of a unit from disk is the code to propagate the CompiledData::Alias entries into entries in the property cache. The code for iterating through the component spaces and resolving the alias references is not shared. Change-Id: I04c2a5575310400156b457ae7b709cffecb7455e Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder_p.h | 2 + src/qml/compiler/qqmlpropertycachecreator_p.h | 234 ++++++++++++++++++++++++++ src/qml/compiler/qqmltypecompiler.cpp | 128 ++------------ src/qml/compiler/qqmltypecompiler_p.h | 4 +- src/qml/compiler/qv4compileddata_p.h | 2 + src/qml/qml/qqmlpropertycache_p.h | 2 + 6 files changed, 252 insertions(+), 120 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 4e0d4e563b..821a6bba6d 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -400,6 +400,8 @@ public: FixedPoolArray runtimeFunctionIndices; FixedPoolArray namedObjectsInComponent; + int namedObjectsInComponentCount() const { return namedObjectsInComponent.count; } + const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); } private: friend struct IRLoader; diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index ff8bc49e6f..09a093d911 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -507,6 +507,240 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj return noError; } +template +class QQmlPropertyCacheAliasCreator +{ +public: + typedef typename ObjectContainer::CompiledObject CompiledObject; + + QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer); + + void appendAliasPropertiesToMetaObjects(); + + void appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex); + +private: + void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex); + void propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, quint32 *propertyFlags); + + void collectObjectsWithAliasesRecursively(int objectIndex, QVector *objectsWithAliases) const; + + int objectForId(const CompiledObject &component, int id) const; + + QQmlPropertyCacheVector *propertyCaches; + const ObjectContainer *objectContainer; +}; + +template +inline QQmlPropertyCacheAliasCreator::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer) + : propertyCaches(propertyCaches) + , objectContainer(objectContainer) +{ + +} + +template +inline void QQmlPropertyCacheAliasCreator::appendAliasPropertiesToMetaObjects() +{ + for (int i = 0; i < objectContainer->objectCount(); ++i) { + const CompiledObject &component = *objectContainer->objectAt(i); + if (!(component.flags & QV4::CompiledData::Object::IsComponent)) + continue; + + const auto rootBinding = component.bindingsBegin(); + appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex); + } + + const int rootObjectIndex = objectContainer->rootObjectIndex(); + appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex); +} + +template +inline void QQmlPropertyCacheAliasCreator::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex) +{ + QVector objectsWithAliases; + collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases); + if (objectsWithAliases.isEmpty()) + return; + + const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) { + for (auto alias = object.aliasesBegin(), end = object.aliasesEnd(); alias != end; ++alias) { + Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); + + const int targetObjectIndex = objectForId(component, alias->targetObjectId); + Q_ASSERT(targetObjectIndex >= 0); + + if (alias->encodedMetaPropertyIndex == -1) + continue; + + const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); + Q_ASSERT(targetCache); + + int coreIndex; + QQmlPropertyData::decodeValueTypePropertyIndex(alias->encodedMetaPropertyIndex, &coreIndex); + QQmlPropertyData *targetProperty = targetCache->property(coreIndex); + if (!targetProperty) + return false; + } + return true; + }; + + do { + QVector pendingObjects; + + for (int objectIndex: qAsConst(objectsWithAliases)) { + const CompiledObject &object = *objectContainer->objectAt(objectIndex); + + if (allAliasTargetsExist(object)) { + appendAliasesToPropertyCache(component, objectIndex); + } else { + pendingObjects.append(objectIndex); + } + + } + qSwap(objectsWithAliases, pendingObjects); + } while (!objectsWithAliases.isEmpty()); +} + +template +inline void QQmlPropertyCacheAliasCreator::collectObjectsWithAliasesRecursively(int objectIndex, QVector *objectsWithAliases) const +{ + const CompiledObject &object = *objectContainer->objectAt(objectIndex); + if (object.aliasCount() > 0) + objectsWithAliases->append(objectIndex); + + // Stop at Component boundary + if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != objectContainer->rootObjectIndex()) + return; + + for (auto binding = object.bindingsBegin(), end = object.bindingsEnd(); binding != end; ++binding) { + if (binding->type != QV4::CompiledData::Binding::Type_Object + && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty + && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) + continue; + + collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases); + } +} + +template +inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, quint32 *propertyFlags) +{ + const int targetObjectIndex = objectForId(component, alias.targetObjectId); + Q_ASSERT(targetObjectIndex >= 0); + + const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex); + + *type = 0; + bool writable = false; + bool resettable = false; + + *propertyFlags = QQmlPropertyData::IsAlias; + + if (alias.aliasToLocalAlias) { + auto targetAlias = targetObject.aliasesBegin(); + for (uint i = 0; i < alias.localAliasIndex; ++i) + ++targetAlias; + propertyDataForAlias(component, *targetAlias, type, propertyFlags); + return; + } else if (alias.encodedMetaPropertyIndex == -1) { + Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); + auto *typeRef = objectContainer->resolvedTypes.value(targetObject.inheritedTypeNameIndex); + Q_ASSERT(typeRef); + + if (typeRef->type) + *type = typeRef->type->typeId(); + else + *type = typeRef->compilationUnit->metaTypeId; + + *propertyFlags |= QQmlPropertyData::IsQObjectDerived; + } else { + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(alias.encodedMetaPropertyIndex, &coreIndex); + + QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); + Q_ASSERT(targetCache); + QQmlPropertyData *targetProperty = targetCache->property(coreIndex); + Q_ASSERT(targetProperty); + + *type = targetProperty->propType; + + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + + if (valueTypeIndex != -1) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); + if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) + *type = QVariant::Int; + else + *type = valueTypeMetaObject->property(valueTypeIndex).userType(); + } else { + if (targetProperty->isEnum()) { + *type = QVariant::Int; + } else { + // Copy type flags + *propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask; + + if (targetProperty->isVarProperty()) + *propertyFlags |= QQmlPropertyData::IsQVariant; + } + } + } + + if (!(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable) + *propertyFlags |= QQmlPropertyData::IsWritable; + else + *propertyFlags &= ~QQmlPropertyData::IsWritable; + + if (resettable) + *propertyFlags |= QQmlPropertyData::IsResettable; + else + *propertyFlags &= ~QQmlPropertyData::IsResettable; +} + +template +inline void QQmlPropertyCacheAliasCreator::appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex) +{ + const CompiledObject &object = *objectContainer->objectAt(objectIndex); + if (!object.aliasCount()) + return; + + QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); + Q_ASSERT(propertyCache); + + int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); + int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); + + int aliasIndex = 0; + for (auto alias = object.aliasesBegin(), end = object.aliasesEnd(); alias != end; ++alias, ++aliasIndex) { + Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); + + int type = 0; + quint32 propertyFlags = 0; + propertyDataForAlias(component, *alias, &type, &propertyFlags); + + const QString propertyName = objectContainer->stringAt(alias->nameIndex); + + if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias) + propertyCache->_defaultPropertyName = propertyName; + + propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + type, effectiveSignalIndex++); + } +} + +template +inline int QQmlPropertyCacheAliasCreator::objectForId(const CompiledObject &component, int id) const +{ + for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) { + const int candidateIndex = component.namedObjectsInComponentTable()[i]; + const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex); + if (candidate.id == id) + return candidateIndex; + } + return -1; +} + QT_END_NAMESPACE #endif // QQMLPROPERTYCACHECREATOR_P_H diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 83aaba791b..bb4d603f68 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -775,7 +775,6 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t , pool(typeCompiler->memoryPool()) , qmlObjects(typeCompiler->qmlObjects()) , indexOfRootObject(typeCompiler->rootObjectIndex()) - , _componentIndex(-1) , resolvedTypes(&typeCompiler->resolvedTypes) , propertyCaches(std::move(typeCompiler->takePropertyCaches())) { @@ -924,7 +923,6 @@ bool QQmlComponentAndAliasResolver::resolve() QmlIR::Object *component = qmlObjects->at(componentRoots.at(i)); const QmlIR::Binding *rootBinding = component->firstBinding(); - _componentIndex = i; _idToObjectIndex.clear(); _objectsWithAliases.clear(); @@ -932,24 +930,24 @@ bool QQmlComponentAndAliasResolver::resolve() if (!collectIdsAndAliases(rootBinding->value.objectIndex)) return false; - if (!resolveAliases()) - return false; - component->namedObjectsInComponent.allocate(pool, _idToObjectIndex); + + if (!resolveAliases(componentRoots.at(i))) + return false; } // Collect ids and aliases for root - _componentIndex = -1; _idToObjectIndex.clear(); _objectsWithAliases.clear(); collectIdsAndAliases(indexOfRootObject); - resolveAliases(); - QmlIR::Object *rootComponent = qmlObjects->at(indexOfRootObject); rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex); + if (!resolveAliases(indexOfRootObject)) + return false; + // Implicit component insertion may have added objects and thus we also need // to extend the symmetric propertyCaches. compiler->setPropertyCaches(std::move(propertyCaches)); @@ -991,10 +989,13 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) return true; } -bool QQmlComponentAndAliasResolver::resolveAliases() +bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) { if (_objectsWithAliases.isEmpty()) return true; + + QQmlPropertyCacheAliasCreator aliasCacheCreator(&propertyCaches, compiler); + bool atLeastOneAliasResolved; do { atLeastOneAliasResolved = false; @@ -1011,7 +1012,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases() } if (result == AllAliasesResolved) { - addAliasesToPropertyCache(objectIndex); + aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex); atLeastOneAliasResolved = true; } else if (result == SomeAliasesResolved) { atLeastOneAliasResolved = true; @@ -1153,113 +1154,6 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv return SomeAliasesResolved; } -void QQmlComponentAndAliasResolver::propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags) -{ - const auto objectForId = [this](int objectId) { - for (auto idIt = _idToObjectIndex.constBegin(), end = _idToObjectIndex.constEnd(); idIt != end; ++idIt) { - const QmlIR::Object *obj = qmlObjects->at(*idIt); - if (obj->id == objectId) - return *idIt; - } - return -1; - }; - - const int targetObjectIndex = objectForId(alias->targetObjectId); - Q_ASSERT(targetObjectIndex >= 0); - - const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); - - *type = 0; - bool writable = false; - bool resettable = false; - - *propertyFlags = QQmlPropertyData::IsAlias; - - if (alias->aliasToLocalAlias) { - auto targetAlias = targetObject->firstAlias(); - for (uint i = 0; i < alias->localAliasIndex; ++i) - targetAlias = targetAlias->next; - propertyDataForAlias(targetAlias, type, propertyFlags); - return; - } else if (alias->encodedMetaPropertyIndex == -1) { - Q_ASSERT(alias->flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); - auto *typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - - if (typeRef->type) - *type = typeRef->type->typeId(); - else - *type = typeRef->compilationUnit->metaTypeId; - - *propertyFlags |= QQmlPropertyData::IsQObjectDerived; - } else { - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(alias->encodedMetaPropertyIndex, &coreIndex); - - QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); - Q_ASSERT(targetCache); - QQmlPropertyData *targetProperty = targetCache->property(coreIndex); - Q_ASSERT(targetProperty); - - *type = targetProperty->propType; - - writable = targetProperty->isWritable(); - resettable = targetProperty->isResettable(); - - if (valueTypeIndex != -1) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); - if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) - *type = QVariant::Int; - else - *type = valueTypeMetaObject->property(valueTypeIndex).userType(); - } else { - if (targetProperty->isEnum()) { - *type = QVariant::Int; - } else { - // Copy type flags - *propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask; - - if (targetProperty->isVarProperty()) - *propertyFlags |= QQmlPropertyData::IsQVariant; - } - } - } - - if (!(alias->flags & QV4::CompiledData::Property::IsReadOnly) && writable) - *propertyFlags |= QQmlPropertyData::IsWritable; - else - *propertyFlags &= ~QQmlPropertyData::IsWritable; - - if (resettable) - *propertyFlags |= QQmlPropertyData::IsResettable; - else - *propertyFlags &= ~QQmlPropertyData::IsResettable; -} - -void QQmlComponentAndAliasResolver::addAliasesToPropertyCache(int objectIndex) -{ - const QmlIR::Object * const obj = qmlObjects->at(objectIndex); - QQmlPropertyCache * const propertyCache = propertyCaches.at(objectIndex); - Q_ASSERT(propertyCache); - int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); - int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); - - int aliasIndex = 0; - for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next, ++aliasIndex) { - int type = 0; - quint32 propertyFlags = 0; - propertyDataForAlias(alias, &type, &propertyFlags); - - QString propertyName = stringAt(alias->nameIndex); - - if (obj->defaultPropertyIsAlias && aliasIndex == obj->indexOfDefaultPropertyOrAlias) - propertyCache->_defaultPropertyName = propertyName; - - propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - type, effectiveSignalIndex++); - } -} - QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) , qmlObjects(typeCompiler->qmlObjects()) diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 0b078fd642..9f1a09b35c 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -265,7 +265,7 @@ public: protected: void findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache); bool collectIdsAndAliases(int objectIndex); - bool resolveAliases(); + bool resolveAliases(int componentIndex); void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags); enum AliasResolutionResult { @@ -275,7 +275,6 @@ protected: }; AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error); - void addAliasesToPropertyCache(int objectIndex); QQmlEnginePrivate *enginePrivate; QQmlJS::MemoryPool *pool; @@ -286,7 +285,6 @@ protected: // indices of the objects that are actually Component {} QVector componentRoots; - int _componentIndex; QHash _idToObjectIndex; QVector _objectsWithAliases; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index f1cf365b25..fc7cf4ebf1 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -565,6 +565,8 @@ struct Object typedef TableIterator SignalIterator; SignalIterator signalsBegin() const { return SignalIterator(this, 0); } SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); } + + int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; } // --- }; diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index aecfdf1a74..baba5347a7 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -73,6 +73,7 @@ class QMetaObjectBuilder; class QQmlPropertyCacheMethodArguments; class QQmlVMEMetaObject; template class QQmlPropertyCacheCreator; +template class QQmlPropertyCacheAliasCreator; // We have this somewhat awful split between RawData and Data so that RawData can be // used in unions. In normal code, you should always use Data which initializes RawData @@ -338,6 +339,7 @@ private: friend class QQmlEnginePrivate; friend class QQmlCompiler; template friend class QQmlPropertyCacheCreator; + template friend class QQmlPropertyCacheAliasCreator; friend class QQmlComponentAndAliasResolver; friend class QQmlMetaObject; -- cgit v1.2.3 From b845b6560df7c70a811d1ff361cbf52f0ec5cd59 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 13 Jul 2016 14:08:13 +0200 Subject: move QQuickCloseEvent from qquickwindow_p.h to qquickevents_p_p.h It seems to be the same sort of persistent event for exposure to QML. Change-Id: I4ebc48422ee517f37e300629b6d100f68b9703b3 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents_p_p.h | 17 +++++++++++++++++ src/quick/items/qquickwindow_p.h | 18 ------------------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 39c5a060b5..065e025152 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -222,10 +222,27 @@ private: bool _accepted; }; +class Q_QUICK_PRIVATE_EXPORT QQuickCloseEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + +public: + QQuickCloseEvent() + : _accepted(true) {} + + bool isAccepted() { return _accepted; } + void setAccepted(bool accepted) { _accepted = accepted; } + +private: + bool _accepted; +}; + QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickKeyEvent) QML_DECLARE_TYPE(QQuickMouseEvent) QML_DECLARE_TYPE(QQuickWheelEvent) +QML_DECLARE_TYPE(QQuickCloseEvent) #endif // QQUICKEVENTS_P_P_H diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 31fa079658..71f2bb5778 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -293,22 +293,6 @@ private: static void cleanupNodesOnShutdown(QQuickItem *); }; -class Q_QUICK_PRIVATE_EXPORT QQuickCloseEvent : public QObject -{ - Q_OBJECT - Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) - -public: - QQuickCloseEvent() - : _accepted(true) {} - - bool isAccepted() { return _accepted; } - void setAccepted(bool accepted) { _accepted = accepted; } - -private: - bool _accepted; -}; - class QQuickWindowQObjectCleanupJob : public QRunnable { public: @@ -326,6 +310,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickWindowPrivate::FocusOptions) QT_END_NAMESPACE -QML_DECLARE_TYPE(QQuickCloseEvent) - #endif // QQUICKWINDOW_P_H -- cgit v1.2.3 From b08a4c2f421494b515dc7ef6f50d33e37e18eb91 Mon Sep 17 00:00:00 2001 From: Milian Wolff Date: Tue, 12 Jul 2016 19:29:39 +0200 Subject: Remove vsync sleep before rendering first frame The QSGRenderThread slept for up to one vsync interval before the first frame was rendered. This happened when the QSGRenderContext was not yet initialized and thus a sync could not result in changes. With LTTNG and custom trace points this latency was easily visible before the first frame swap. With perf it can also be checked, when one does: QSG_RENDER_LOOP=threaded perf trace record -m 10M \ -e syscalls:sys_enter_nanosleep,syscalls:sys_enter_ioctl \ --call-graph dwarf qml main.qml ... [ perf record: Captured and wrote 116.731 MB perf.data (14309 samples) ] Then afterwards have a look at the output of perf script --comms QSGRenderThread And you will notice the sleep directly at the start, followed by the bulk of ioctl required to setup the OpenGL contexts: QSGRenderThread 10875 [001] 13940.801449: syscalls:sys_enter_nanosleep: rqtp: 0x7f1ab954cd60, rmtp: 0x7f1ab954cd60 7f1ada3997fd __nanosleep+0x2d (/usr/lib/libpthread-2.23.so) 7f1ada9683ed qt_nanosleep+0x2d (/home/milian/projects/compiled/qt5-5.7-opt/lib/libQt5Core.so.5.7.0) 7f1ada8c73b8 QThread::msleep+0x38 (/home/milian/projects/compiled/qt5-5.7-opt/lib/libQt5Core.so.5.7.0) 7f1abb594320 QSGRenderThread::syncAndRender+0x320 (/home/milian/projects/compiled/qt5-5.7-opt/lib/libQt5Quick.so.5.7.0) 7f1abb598b0c QSGRenderThread::run+0x19c (/home/milian/projects/compiled/qt5-5.7-opt/lib/libQt5Quick.so.5.7.0) 7f1ada8c7f89 QThreadPrivate::start+0x149 (/home/milian/projects/compiled/qt5-5.7-opt/lib/libQt5Core.so.5.7.0) 7f1ada390484 start_thread+0xc4 (/usr/lib/libpthread-2.23.so) 7f1ad982e6dd __clone+0x6d (/usr/lib/libc-2.23.so) ... ioctl follow This change here checks the validity of the render context before sleeping, thereby removing the vsync latency before rendering the first frame. Note that simply skipping the calls to `syncAndRender` from `run` is not an option, as those also trigger the expose event that is required for the initialization to happen eventually. [ChangeLog][QtQuick] The threaded scene graph renderer does not sleep up to one vsync interval before the first frame anymore. Change-Id: If7474d5420e0d4a1d05ccb664c7c6932fa989127 Reviewed-by: Gunnar Sletta Reviewed-by: Ulf Hermann --- src/quick/scenegraph/qsgthreadedrenderloop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 8d6bea9e67..ceb3caa53f 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -593,7 +593,7 @@ void QSGRenderThread::syncAndRender() #endif Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); - if (!syncResultedInChanges && !repaintRequested) { + if (!syncResultedInChanges && !repaintRequested && sgrc->isValid()) { qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- no changes, render aborted"; int waitTime = vsyncDelta - (int) waitTimer.elapsed(); if (waitTime > 0) -- cgit v1.2.3 From 9d4b55640788bf96ea100ad0616fd4236370d140 Mon Sep 17 00:00:00 2001 From: Milian Wolff Date: Thu, 31 Mar 2016 19:43:27 +0200 Subject: Add benchmark for pathological O(N^2) behavior in QQmlChangeSet. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is just here for documentation of this behavior and does not come with a fix yet. This hotspot was found while profiling the example code attached to QTBUG-34391. It is triggered by the repeated calls to _q_itemsMoved in QQmlDelegateModel::_q_layoutChanged. Change-Id: I758744b3650c3c47dc86b914b823c3e9f96ce81e Reviewed-by: Albert Astals Cid Reviewed-by: Daniel Vrátil Reviewed-by: Christopher Adams --- tests/benchmarks/qml/qml.pro | 1 + .../benchmarks/qml/qqmlchangeset/qqmlchangeset.pro | 10 ++++ .../qml/qqmlchangeset/tst_qqmlchangeset.cpp | 60 ++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 tests/benchmarks/qml/qqmlchangeset/qqmlchangeset.pro create mode 100644 tests/benchmarks/qml/qqmlchangeset/tst_qqmlchangeset.cpp diff --git a/tests/benchmarks/qml/qml.pro b/tests/benchmarks/qml/qml.pro index d3ce69c713..5d48ec0067 100644 --- a/tests/benchmarks/qml/qml.pro +++ b/tests/benchmarks/qml/qml.pro @@ -5,6 +5,7 @@ SUBDIRS += \ compilation \ javascript \ holistic \ + qqmlchangeset \ qqmlcomponent \ qqmlimage \ qqmlmetaproperty \ diff --git a/tests/benchmarks/qml/qqmlchangeset/qqmlchangeset.pro b/tests/benchmarks/qml/qqmlchangeset/qqmlchangeset.pro new file mode 100644 index 0000000000..fc0ccdf8ed --- /dev/null +++ b/tests/benchmarks/qml/qqmlchangeset/qqmlchangeset.pro @@ -0,0 +1,10 @@ +CONFIG += benchmark +TEMPLATE = app +TARGET = tst_qqmlchangeset +QT += qml quick-private testlib +osx:CONFIG -= app_bundle + +SOURCES += tst_qqmlchangeset.cpp + +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 + diff --git a/tests/benchmarks/qml/qqmlchangeset/tst_qqmlchangeset.cpp b/tests/benchmarks/qml/qqmlchangeset/tst_qqmlchangeset.cpp new file mode 100644 index 0000000000..bbfb52343c --- /dev/null +++ b/tests/benchmarks/qml/qqmlchangeset/tst_qqmlchangeset.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite 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 + +#include + +#include + +class tst_qqmlchangeset : public QObject +{ + Q_OBJECT + +private slots: + void move(); +}; + +void tst_qqmlchangeset::move() +{ + QBENCHMARK { + QQmlChangeSet set; + const int MAX_ROWS = 30000; + for (int i = 0; i < MAX_ROWS; ++i) { + set.move(i, MAX_ROWS - 1 - i, 1, i); + } + } +} + +QTEST_MAIN(tst_qqmlchangeset) +#include "tst_qqmlchangeset.moc" -- cgit v1.2.3 From fb42ee0a8b0f6ae2b08a17c37fa538ee51cc63bf Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 15 Jul 2016 15:07:13 +0200 Subject: Use public API to access mouseGrabberItem Change-Id: Ifad813196b74b3276d7f5054e8917858460948d3 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 8 ++--- src/quick/items/qquickwindow.cpp | 64 ++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 27acf38faa..d49283a3e1 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -7277,12 +7277,10 @@ void QQuickItem::grabTouchPoints(const QVector &ids) if (oldGrabber) ungrab.insert(oldGrabber); - QQuickItem *mouseGrabber = windowPriv->mouseGrabberItem; + QQuickItem *mouseGrabber = d->window->mouseGrabberItem(); if (windowPriv->touchMouseId == ids.at(i) && mouseGrabber && mouseGrabber != this) { - qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: grabber" << windowPriv->mouseGrabberItem << "-> null"; - windowPriv->mouseGrabberItem = 0; - QEvent ev(QEvent::UngrabMouse); - d->window->sendEvent(mouseGrabber, &ev); + qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: grabber" << mouseGrabber << "-> null"; + mouseGrabber->ungrabMouse(); } } foreach (QQuickItem *oldGrabber, ungrab) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 22086c577c..91bd19e311 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -633,6 +633,8 @@ bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp) bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event) { + Q_Q(QQuickWindow); + // For each point, check if it is accepted, if not, try the next point. // Any of the fingers can become the mouse one. // This can happen because a mouse area might not accept an event at some point but another. @@ -655,7 +657,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e QScopedPointer mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); // Send a single press and see if that's accepted - if (!mouseGrabberItem) + if (!q->mouseGrabberItem()) item->grabMouse(); item->grabTouchPoints(QVector() << touchMouseId); @@ -668,7 +670,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e itemForTouchPointId.remove(p.id()); } - if (mouseGrabberItem == item) + if (q->mouseGrabberItem() == item) item->ungrabMouse(); } @@ -696,13 +698,13 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e // Touch point was there before and moved } else if (p.id() == touchMouseId) { if (p.state() & Qt::TouchPointMoved) { - if (mouseGrabberItem) { + if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { QScopedPointer me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); event->setAccepted(me->isAccepted()); if (me->isAccepted()) { qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem; - itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent() + itemForTouchPointId[p.id()] = q->mouseGrabberItem(); // N.B. the mouseGrabberItem may be different after returning from sendEvent() return true; } } else { @@ -727,7 +729,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e } else if (p.state() & Qt::TouchPointReleased) { // currently handled point was released touchMouseId = -1; - if (mouseGrabberItem) { + if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { QScopedPointer me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); @@ -739,8 +741,8 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e Qt::NoButton, Qt::NoButton, event->modifiers()); QCoreApplication::sendEvent(item, &mm); } - if (mouseGrabberItem) // might have ungrabbed due to event - mouseGrabberItem->ungrabMouse(); + if (q->mouseGrabberItem()) // might have ungrabbed due to event + q->mouseGrabberItem()->ungrabMouse(); return me->isAccepted(); } } @@ -753,18 +755,18 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) { Q_Q(QQuickWindow); - if (mouseGrabberItem == grabber) + if (q->mouseGrabberItem() == grabber) return; - qCDebug(DBG_MOUSE_TARGET) << "grabber" << mouseGrabberItem << "->" << grabber; - QQuickItem *oldGrabber = mouseGrabberItem; + qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber; + QQuickItem *oldGrabber = q->mouseGrabberItem(); mouseGrabberItem = grabber; if (touchMouseId != -1) { // update the touch item for mouse touch id to the new grabber itemForTouchPointId.remove(touchMouseId); if (grabber) { - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << mouseGrabberItem; + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); itemForTouchPointId[touchMouseId] = grabber; } } @@ -787,8 +789,8 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to } } } - if (Q_LIKELY(mouse) && mouseGrabberItem == grabber) { - qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << mouseGrabberItem << "-> null"; + if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) { + qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << q->mouseGrabberItem() << "-> null"; mouseGrabberItem = 0; QEvent ev(QEvent::UngrabMouse); q->sendEvent(grabber, &ev); @@ -1539,8 +1541,8 @@ bool QQuickWindow::event(QEvent *e) if (d->activeFocusItem) qGuiApp->inputMethod()->commit(); #endif - if (d->mouseGrabberItem) - d->mouseGrabberItem->ungrabMouse(); + if (mouseGrabberItem()) + mouseGrabberItem()->ungrabMouse(); break; case QEvent::UpdateRequest: { if (d->windowManager) @@ -1623,8 +1625,8 @@ bool QQuickWindowPrivate::deliverInitialMousePressEvent(QMouseEvent *event) event->setAccepted(me->isAccepted()); if (me->isAccepted()) return true; - if (mouseGrabberItem) - mouseGrabberItem->ungrabMouse(); + if (q->mouseGrabberItem()) + q->mouseGrabberItem()->ungrabMouse(); } } } @@ -1637,7 +1639,7 @@ bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) lastMousePosition = event->windowPos(); - if (!mouseGrabberItem && + if (!q->mouseGrabberItem() && (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) && (event->buttons() & event->button()) == event->buttons()) { @@ -1648,7 +1650,7 @@ bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) return event->isAccepted(); } - if (mouseGrabberItem) { + if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos()); QScopedPointer me(cloneMouseEvent(event, &localPos)); me->accept(); @@ -1879,8 +1881,8 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) q->sendEvent(item, event); } touchMouseId = -1; - if (mouseGrabberItem) - mouseGrabberItem->ungrabMouse(); + if (q->mouseGrabberItem()) + q->mouseGrabberItem()->ungrabMouse(); // The next touch event can only be a TouchBegin so clean up. itemForTouchPointId.clear(); return true; @@ -2009,6 +2011,8 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) { + Q_Q(QQuickWindow); + if (event->source() == Qt::MouseEventSynthesizedBySystem) { event->accept(); return; @@ -2024,14 +2028,14 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonRelease: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(), event->buttons()); - if (!mouseGrabberItem) { + if (!q->mouseGrabberItem()) { event->ignore(); return; } deliverPointerEvent(pointerEventInstance(event)); - if (mouseGrabberItem && !event->buttons()) - mouseGrabberItem->ungrabMouse(); + if (q->mouseGrabberItem() && !event->buttons()) + q->mouseGrabberItem()->ungrabMouse(); break; case QEvent::MouseButtonDblClick: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, @@ -2048,7 +2052,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) updateCursor(event->windowPos()); #endif - if (!mouseGrabberItem) { + if (!q->mouseGrabberItem()) { if (lastMousePosition.isNull()) lastMousePosition = event->windowPos(); QPointF last = lastMousePosition; @@ -2073,6 +2077,8 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) void QQuickWindowPrivate::flushFrameSynchronousEvents() { + Q_Q(QQuickWindow); + if (delayedTouch) { deliverDelayedTouchEvent(); @@ -2087,7 +2093,7 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() // For instance, during animation (including the case of a ListView // whose delegates contain MouseAreas), a MouseArea needs to know // whether it has moved into a position where it is now under the cursor. - if (!mouseGrabberItem && !lastMousePosition.isNull()) { + if (!q->mouseGrabberItem() && !lastMousePosition.isNull()) { bool accepted = false; bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), accepted); if (!delivered) @@ -2615,6 +2621,8 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF // Child event filtering is legacy stuff, so here we use QTouchEvent bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *hasFiltered) { + Q_Q(QQuickWindow); + if (!target) return false; @@ -2633,8 +2641,8 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem for (int i = 0; i < touchPointCount; ++i) touchIds.append(targetEvent->touchPoints().at(i).id()); target->grabTouchPoints(touchIds); - if (mouseGrabberItem) { - mouseGrabberItem->ungrabMouse(); + if (q->mouseGrabberItem()) { + q->mouseGrabberItem()->ungrabMouse(); touchMouseId = -1; } filtered = true; -- cgit v1.2.3 From bcd1e1bafa3ff4c8ba9288ccc8a70bdf2e11c06e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 18 Jul 2016 15:21:30 +0200 Subject: QQuickWindowPrivate::deliverPoints: take QQuickPointerEvent non-const The event should be accepted or rejected. Change-Id: I5d9b222913763d45cfb47ff8fe6ea4426563bc21 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 2 +- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 91bd19e311..364637a793 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2242,7 +2242,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerEvent *event) } // This function recurses and sends the events to the individual items -bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, const QQuickPointerEvent *event, const QList &newPoints, +bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerEvent *event, const QList &newPoints, QSet *acceptedNewPoints, QHash > *updatedPoints, QSet *hasFiltered) { diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index ab704b9adf..c22b6dc7d0 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -167,7 +167,7 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPoints(QQuickItem *, const QQuickPointerEvent *, const QList &, + bool deliverPoints(QQuickItem *, QQuickPointerEvent *, const QList &, QSet *, QHash > *, QSet *); bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, const QList &matchingPoints, QSet *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); -- cgit v1.2.3 From a0dd7400904166c67279e9d08076221e51fc0454 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 18 Jul 2016 11:17:37 +0200 Subject: D3D12: Show a warning when the requested adapter is not usable Change-Id: I3cbc4bf45b22f8bf772163be24ca4c9ccb63fcad Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index d19dfe2db8..fbb294f1e9 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -197,13 +197,17 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX")) { const int adapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX"); - if (SUCCEEDED(factory->EnumAdapters1(adapterIndex, &adapter)) - && SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) { + if (SUCCEEDED(factory->EnumAdapters1(adapterIndex, &adapter))) { adapter->GetDesc1(&desc); const QString name = QString::fromUtf16((char16_t *) desc.Description); - qCDebug(QSG_LOG_INFO, "Using requested adapter '%s'", qPrintable(name)); - *outAdapter = adapter.Detach(); - return; + HRESULT hr = D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr); + if (SUCCEEDED(hr)) { + qCDebug(QSG_LOG_INFO, "Using requested adapter '%s'", qPrintable(name)); + *outAdapter = adapter.Detach(); + return; + } else { + qWarning("Failed to create device for requested adapter '%s': 0x%x", qPrintable(name), hr); + } } } -- cgit v1.2.3 From 12461f7df22e546df9eef4bd725f6009738071fd Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 18 Jul 2016 11:25:33 +0200 Subject: D3D12: Move swapchain/color buffer format to a constant So it can be changed to BGRA, if desired, although tests show that BGRA reduces perf a little bit in fact. Change-Id: Ieb87bba2298426f1503658f8dffb0c659c265ca5 Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index fbb294f1e9..43f765cd38 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -89,6 +89,8 @@ static const int MAX_CACHED_PSO = 64; static const int GPU_CBVSRVUAV_DESCRIPTORS = 512; +static const DXGI_FORMAT RT_COLOR_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; + static const int BUCKETS_PER_HEAP = 8; // must match freeMap static const int DESCRIPTORS_PER_BUCKET = 32; // the bit map (freeMap) is quint32 static const int MAX_DESCRIPTORS_PER_HEAP = BUCKETS_PER_HEAP * DESCRIPTORS_PER_BUCKET; @@ -803,7 +805,7 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.Width = windowSize.width() * windowDpr; swapChainDesc.Height = windowSize.height() * windowDpr; - swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.Format = RT_COLOR_FORMAT; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = swapChainBufferCount; @@ -843,7 +845,7 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int swapChainDesc.BufferCount = swapChainBufferCount; swapChainDesc.BufferDesc.Width = windowSize.width() * windowDpr; swapChainDesc.BufferDesc.Height = windowSize.height() * windowDpr; - swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferDesc.Format = RT_COLOR_FORMAT; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // D3D12 requires the flip model swapChainDesc.OutputWindow = hwnd; @@ -869,7 +871,7 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.Width = windowSize.width() * windowDpr; swapChainDesc.Height = windowSize.height() * windowDpr; - swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.Format = RT_COLOR_FORMAT; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = swapChainBufferCount; @@ -1020,7 +1022,7 @@ ID3D12Resource *QSGD3D12EnginePrivate::createColorBuffer(D3D12_CPU_DESCRIPTOR_HA const QVector4D &clearColor, uint samples) { D3D12_CLEAR_VALUE clearValue = {}; - clearValue.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + clearValue.Format = RT_COLOR_FORMAT; clearValue.Color[0] = clearColor.x(); clearValue.Color[1] = clearColor.y(); clearValue.Color[2] = clearColor.z(); @@ -1035,7 +1037,7 @@ ID3D12Resource *QSGD3D12EnginePrivate::createColorBuffer(D3D12_CPU_DESCRIPTOR_HA rtDesc.Height = size.height(); rtDesc.DepthOrArraySize = 1; rtDesc.MipLevels = 1; - rtDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + rtDesc.Format = RT_COLOR_FORMAT; rtDesc.SampleDesc = makeSampleDesc(rtDesc.Format, samples); rtDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; @@ -1144,7 +1146,7 @@ void QSGD3D12EnginePrivate::setWindowSize(const QSize &size, float dpr) const int w = windowSize.width() * windowDpr; const int h = windowSize.height() * windowDpr; - HRESULT hr = swapChain->ResizeBuffers(swapChainBufferCount, w, h, DXGI_FORMAT_R8G8B8A8_UNORM, + HRESULT hr = swapChain->ResizeBuffers(swapChainBufferCount, w, h, RT_COLOR_FORMAT, waitableSwapChainMaxLatency ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0); if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { deviceManager()->deviceLossDetected(); @@ -1228,7 +1230,7 @@ void QSGD3D12EnginePrivate::resolveMultisampledTarget(ID3D12Resource *msaa, barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_DEST; commandList->ResourceBarrier(2, barriers); - commandList->ResolveSubresource(resolve, 0, msaa, 0, DXGI_FORMAT_R8G8B8A8_UNORM); + commandList->ResolveSubresource(resolve, 0, msaa, 0, RT_COLOR_FORMAT); barriers[0].Transition.pResource = msaa; barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_SOURCE; @@ -1853,7 +1855,7 @@ void QSGD3D12EnginePrivate::finalizePipeline(const QSGD3D12PipelineState &pipeli psoDesc.SampleMask = UINT_MAX; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE(pipelineState.topologyType); psoDesc.NumRenderTargets = 1; - psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; + psoDesc.RTVFormats[0] = RT_COLOR_FORMAT; psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; psoDesc.SampleDesc = defaultRT[0]->GetDesc().SampleDesc; @@ -2921,7 +2923,7 @@ void QSGD3D12EnginePrivate::createRenderTarget(uint id, const QSize &size, const textureDesc.Height = size.height(); textureDesc.DepthOrArraySize = 1; textureDesc.MipLevels = 1; - textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + textureDesc.Format = RT_COLOR_FORMAT; textureDesc.SampleDesc.Count = 1; textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; -- cgit v1.2.3 From 0839d89cc0ada7b304939bc527433c2498d995b0 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Mon, 18 Jul 2016 12:20:16 +0200 Subject: Doc: minor spelling issue Change-Id: I18316de7c0eaee98a7eb0aa274c06864af471116 Reviewed-by: Martin Smith --- examples/quick/scenegraph/simplematerial/doc/src/simplematerial.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/quick/scenegraph/simplematerial/doc/src/simplematerial.qdoc b/examples/quick/scenegraph/simplematerial/doc/src/simplematerial.qdoc index 278f154781..d6eb711929 100644 --- a/examples/quick/scenegraph/simplematerial/doc/src/simplematerial.qdoc +++ b/examples/quick/scenegraph/simplematerial/doc/src/simplematerial.qdoc @@ -102,8 +102,8 @@ state and we use it to update the shader program with the current color. The previous state is passed in as a second parameter so that the user can update only that which has changed. In our - usecase, where all the colors are different, the updateState will - be called once for every node. + use case, where all the colors are different, the updateState() + function will be called once for every node. \snippet scenegraph/simplematerial/simplematerial.cpp 7 -- cgit v1.2.3 From 7605b92573bb12e130d2df4c6b15e9cdfa36d5ec Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 15 Jul 2016 14:18:37 +0200 Subject: Fix compilation Commit be491913c036b148cd4f90fa0132e269a507dbad regressed the build with some compilers that complained that the RegExp fields were uninitialized. Fix is the same as for the Lookup class. Change-Id: Ie6b2adf14496d75845f6a9d9b8fc680e61aa1155 Reviewed-by: BogDan Vatra --- src/qml/compiler/qv4compileddata_p.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index fc7cf4ebf1..eef36f98e6 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -146,6 +146,8 @@ struct RegExp QJsonPrivate::qle_bitfield<0, 4> flags; QJsonPrivate::qle_bitfield<4, 28> stringIndex; }; + + RegExp() { flags = 0; stringIndex = 0; } }; struct Lookup -- cgit v1.2.3 From 9dc4085e593c90c49eb9964ea99295874c62dc4c Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 19 Jul 2016 10:02:48 +0200 Subject: Remove redundant compare in test The same condition is tested in the line above. Change-Id: I99720e3603b1ae5e2f5161696f1de3dd03f6ff50 Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquickview/tst_qquickview.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/auto/quick/qquickview/tst_qquickview.cpp b/tests/auto/quick/qquickview/tst_qquickview.cpp index 200f769a65..0f325fddf5 100644 --- a/tests/auto/quick/qquickview/tst_qquickview.cpp +++ b/tests/auto/quick/qquickview/tst_qquickview.cpp @@ -151,7 +151,6 @@ void tst_QQuickView::resizemodeitem() QCOMPARE(item->width(), 80.0); QCOMPARE(item->height(), 100.0); QTRY_COMPARE(view->size(), QSize(80, 100)); - QCOMPARE(view->size(), QSize(80, 100)); QCOMPARE(view->size(), view->sizeHint()); // size update from root object disabled -- cgit v1.2.3 From b4fc940978e7995e75f17a5386c30ef42333bd38 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 19 Jul 2016 10:03:10 +0200 Subject: Stabilize tst_QQuickView::resizemodeitem FAIL! : tst_QQuickView::resizemodeitem() Compared values are not the same Actual (sizeListener.at(i)): QSize(80x100) Expected (view->size()) : QSize(200x300) Loc: [tst_qquickview.cpp(172)] The "SizeChangesListener" can be instantiated too early, catching signals from the previous resize events. Make sure to flush all events before. Change-Id: Ib0933429f5c589f120c263619f00893fa813e361 Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquickview/tst_qquickview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/quick/qquickview/tst_qquickview.cpp b/tests/auto/quick/qquickview/tst_qquickview.cpp index 0f325fddf5..fa9192edb4 100644 --- a/tests/auto/quick/qquickview/tst_qquickview.cpp +++ b/tests/auto/quick/qquickview/tst_qquickview.cpp @@ -162,6 +162,7 @@ void tst_QQuickView::resizemodeitem() QCOMPARE(QSize(item->width(), item->height()), view->sizeHint()); // size update from view + QCoreApplication::processEvents(); // make sure the last resize events are gone SizeChangesListener sizeListener(item); view->resize(QSize(200,300)); QTRY_COMPARE(item->width(), 200.0); -- cgit v1.2.3 From 4cb2552313280384b59ee1e7d7fc557c7bd64a68 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 15 Jul 2016 13:43:08 +0200 Subject: software: Add support for QSGRenderNode Have to change getResource() a bit since it turns out it is not suitable currently for backends that do not have a per-window rendercontext and do not implement the interface on the rendercontext. Pass in the window to make sure it can always figure out which window we want the resources for. (we do not want rendererInterface() to return window-specific instances created on the fly, with ownership issues, so stick with the simple model where backends implement the interface on one of their existing classes) To support clipping, QSGRenderNode::RenderState is extended accordingly. Also updated the docs since some claims in the rendernode docs are not true since Qt 5.3. Change-Id: I34779c83926f5231b888fcab7131e873ae97964f Reviewed-by: Andy Nichols --- .../scenegraph/rendernode/customrenderitem.cpp | 5 ++ .../quick/scenegraph/rendernode/d3d12renderer.cpp | 4 +- examples/quick/scenegraph/rendernode/main.qml | 5 +- .../quick/scenegraph/rendernode/openglrenderer.cpp | 4 +- .../quick/scenegraph/rendernode/rendernode.pro | 4 +- .../scenegraph/rendernode/softwarerenderer.cpp | 91 ++++++++++++++++++++++ .../quick/scenegraph/rendernode/softwarerenderer.h | 62 +++++++++++++++ src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 2 +- src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h | 2 +- .../scenegraph/d3d12/qsgd3d12rendercontext.cpp | 6 +- .../scenegraph/d3d12/qsgd3d12rendercontext_p.h | 2 +- src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp | 1 + .../scenegraph/d3d12/qsgd3d12renderloop.cpp | 2 + src/quick/items/qquickwindow.cpp | 6 ++ .../adaptations/software/qsgsoftwarecontext.cpp | 11 +++ .../adaptations/software/qsgsoftwarecontext_p.h | 2 + .../software/qsgsoftwarepixmaprenderer.cpp | 5 ++ .../software/qsgsoftwarerenderablenode.cpp | 53 +++++++++++-- .../software/qsgsoftwarerenderablenode_p.h | 5 +- .../software/qsgsoftwarerenderablenodeupdater.cpp | 19 ++++- .../software/qsgsoftwarerenderablenodeupdater_p.h | 2 + .../adaptations/software/qsgsoftwarerenderer.cpp | 4 + .../software/qsgsoftwarerenderlistbuilder.cpp | 12 ++- .../software/qsgsoftwarerenderlistbuilder_p.h | 2 + src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 1 + .../scenegraph/coreapi/qsgrendererinterface.cpp | 17 ++-- .../scenegraph/coreapi/qsgrendererinterface.h | 9 ++- src/quick/scenegraph/coreapi/qsgrendernode.cpp | 34 +++++--- src/quick/scenegraph/coreapi/qsgrendernode.h | 1 + src/quick/scenegraph/qsgadaptationlayer.cpp | 8 ++ src/quick/scenegraph/qsgadaptationlayer_p.h | 3 + 31 files changed, 344 insertions(+), 40 deletions(-) create mode 100644 examples/quick/scenegraph/rendernode/softwarerenderer.cpp create mode 100644 examples/quick/scenegraph/rendernode/softwarerenderer.h diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.cpp b/examples/quick/scenegraph/rendernode/customrenderitem.cpp index 433f3c5a1e..2465f4cbc7 100644 --- a/examples/quick/scenegraph/rendernode/customrenderitem.cpp +++ b/examples/quick/scenegraph/rendernode/customrenderitem.cpp @@ -44,6 +44,7 @@ #include "openglrenderer.h" #include "d3d12renderer.h" +#include "softwarerenderer.h" CustomRenderItem::CustomRenderItem(QQuickItem *parent) : QQuickItem(parent) @@ -70,6 +71,10 @@ QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) n = new D3D12RenderNode(this); break; #endif + case QSGRendererInterface::Software: + n = new SoftwareRenderNode(this); + break; + default: return nullptr; } diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp index b7dc62fe86..f475ec838b 100644 --- a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp +++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp @@ -78,7 +78,7 @@ void D3D12RenderNode::releaseResources() void D3D12RenderNode::init() { QSGRendererInterface *rif = m_item->window()->rendererInterface(); - m_device = static_cast(rif->getResource(QSGRendererInterface::Device)); + m_device = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::Device)); Q_ASSERT(m_device); D3D12_ROOT_PARAMETER rootParameter; @@ -235,7 +235,7 @@ void D3D12RenderNode::render(const RenderState *state) init(); QSGRendererInterface *rif = m_item->window()->rendererInterface(); - ID3D12GraphicsCommandList *commandList = static_cast(rif->getResource(QSGRendererInterface::CommandList)); + ID3D12GraphicsCommandList *commandList = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::CommandList)); Q_ASSERT(commandList); const int msize = 16 * sizeof(float); diff --git a/examples/quick/scenegraph/rendernode/main.qml b/examples/quick/scenegraph/rendernode/main.qml index a91656dfaa..7c8d82181f 100644 --- a/examples/quick/scenegraph/rendernode/main.qml +++ b/examples/quick/scenegraph/rendernode/main.qml @@ -90,7 +90,10 @@ Item { anchors.margins: 20 wrapMode: Text.WordWrap property int api: GraphicsInfo.api - text: "Custom rendering via the graphics API " + (api === GraphicsInfo.OpenGL ? "OpenGL" : (api === GraphicsInfo.Direct3D12 ? "Direct3D 12" : "")) + text: "Custom rendering via the graphics API " + + (api === GraphicsInfo.OpenGL ? "OpenGL" + : api === GraphicsInfo.Direct3D12 ? "Direct3D 12" + : api === GraphicsInfo.Software ? "Software" : "") color: "yellow" } } diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp index 3eff70cb42..33c59198b8 100644 --- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp +++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp @@ -97,8 +97,6 @@ void OpenGLRenderNode::init() const int VERTEX_SIZE = 6 * sizeof(GLfloat); - // A fully featured renderer should also take inheritedOpacity() into account - // and blend, but ignore that for now. static GLfloat colors[] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, @@ -140,6 +138,8 @@ void OpenGLRenderNode::render(const RenderState *state) m_program->enableAttributeArray(0); m_program->enableAttributeArray(1); + // Note that clipping (scissor or stencil) is ignored in this example. + f->glEnable(GL_BLEND); f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); diff --git a/examples/quick/scenegraph/rendernode/rendernode.pro b/examples/quick/scenegraph/rendernode/rendernode.pro index d7ae715a7d..851d5927bd 100644 --- a/examples/quick/scenegraph/rendernode/rendernode.pro +++ b/examples/quick/scenegraph/rendernode/rendernode.pro @@ -1,10 +1,12 @@ QT += qml quick HEADERS += customrenderitem.h \ - openglrenderer.h + openglrenderer.h \ + softwarerenderer.h SOURCES += customrenderitem.cpp \ openglrenderer.cpp \ + softwarerenderer.cpp \ main.cpp RESOURCES += rendernode.qrc diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp new file mode 100644 index 0000000000..2f11c56f29 --- /dev/null +++ b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "softwarerenderer.h" +#include +#include +#include +#include + +SoftwareRenderNode::SoftwareRenderNode(QQuickItem *item) + : m_item(item) +{ +} + +SoftwareRenderNode::~SoftwareRenderNode() +{ + releaseResources(); +} + +void SoftwareRenderNode::releaseResources() +{ +} + +void SoftwareRenderNode::render(const RenderState *renderState) +{ + QSGRendererInterface *rif = m_item->window()->rendererInterface(); + QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::Painter)); + Q_ASSERT(p); + + p->setTransform(matrix()->toTransform()); + p->setOpacity(inheritedOpacity()); + const QRegion *clipRegion = renderState->clipRegion(); + if (clipRegion && !clipRegion->isEmpty()) + p->setClipRegion(*clipRegion); + + const QPointF p0(m_item->width() - 1, m_item->height() - 1); + const QPointF p1(0, 0); + const QPointF p2(0, m_item->height() - 1); + QPainterPath path(p0); + path.lineTo(p1); + path.lineTo(p2); + path.closeSubpath(); + + QLinearGradient gradient(QPointF(0, 0), QPointF(m_item->width(), m_item->height())); + gradient.setColorAt(0, Qt::green); + gradient.setColorAt(1, Qt::red); + + p->fillPath(path, gradient); +} + +QSGRenderNode::StateFlags SoftwareRenderNode::changedStates() const +{ + return 0; +} diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.h b/examples/quick/scenegraph/rendernode/softwarerenderer.h new file mode 100644 index 0000000000..60036f96a1 --- /dev/null +++ b/examples/quick/scenegraph/rendernode/softwarerenderer.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SOFTWARERENDERER_H +#define SOFTWARERENDERER_H + +#include + +class QQuickItem; + +class SoftwareRenderNode : public QSGRenderNode +{ +public: + SoftwareRenderNode(QQuickItem *item); + ~SoftwareRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + +private: + QQuickItem *m_item; +}; + +#endif diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 43f765cd38..eab762f7c1 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -549,7 +549,7 @@ void QSGD3D12Engine::simulateDeviceLoss() d->simulateDeviceLoss(); } -void *QSGD3D12Engine::getResource(QSGRendererInterface::Resource resource) const +void *QSGD3D12Engine::getResource(QQuickWindow *, QSGRendererInterface::Resource resource) const { return d->getResource(resource); } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h index 72007b96db..46cd73e63a 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h @@ -372,7 +372,7 @@ public: void simulateDeviceLoss(); - void *getResource(QSGRendererInterface::Resource resource) const; + void *getResource(QQuickWindow *window, QSGRendererInterface::Resource resource) const; private: QSGD3D12EnginePrivate *d; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp index 09129c954d..4ee4656e63 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp @@ -141,14 +141,14 @@ QSGRendererInterface::GraphicsApi QSGD3D12RenderContext::graphicsApi() const return Direct3D12; } -void *QSGD3D12RenderContext::getResource(Resource resource) const +void *QSGD3D12RenderContext::getResource(QQuickWindow *window, Resource resource) const { if (!m_engine) { qWarning("getResource: No D3D12 engine available yet (window not exposed?)"); return nullptr; } - - return m_engine->getResource(resource); + // window can be ignored since the rendercontext and engine are both per window + return m_engine->getResource(window, resource); } QSGRendererInterface::ShaderType QSGD3D12RenderContext::shaderType() const diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h index ca85aaa46f..35aca100f4 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h @@ -75,7 +75,7 @@ public: // QSGRendererInterface GraphicsApi graphicsApi() const override; - void *getResource(Resource resource) const override; + void *getResource(QQuickWindow *window, Resource resource) const override; ShaderType shaderType() const override; ShaderCompilationTypes shaderCompilationType() const override; ShaderSourceTypes shaderSourceType() const override; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp index 5e5d7a13f8..ce633ae996 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp @@ -451,6 +451,7 @@ struct RenderNodeState : public QSGRenderNode::RenderState bool scissorEnabled() const { return m_scissorEnabled; } int stencilValue() const { return m_stencilValue; } bool stencilEnabled() const { return m_stencilEnabled; } + const QRegion *clipRegion() const override { return nullptr; } const QMatrix4x4 *m_projectionMatrix; QRect m_scissorRect; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index ec3427a9cc..c53a1fa6c1 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -279,6 +279,8 @@ QSGContext *QSGD3D12RenderLoop::sceneGraphContext() const QSGRenderContext *QSGD3D12RenderLoop::createRenderContext(QSGContext *) const { + // The rendercontext and engine are per-window, like with the threaded + // loop, but unlike the non-threaded OpenGL variants. return sg->createRenderContext(); } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index af91722432..56466fdbd1 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -4506,6 +4506,12 @@ qreal QQuickWindow::effectiveDevicePixelRatio() const QSGRendererInterface::graphicsApi() or QSGRendererInterface::shaderType(), will always be functional on the other hand. + \note The ownership of the returned pointer stays with Qt. The returned + instance may or may not be shared between different QQuickWindow instances, + depending on the scenegraph backend in use. Therefore applications are + expected to query the interface object for each QQuickWindow instead of + reusing the already queried pointer. + \sa QSGRenderNode, QSGRendererInterface \since 5.8 diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp index 94e9c81f70..80112c1121 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp @@ -53,6 +53,7 @@ #include #include +#include // Used for very high-level info about the renderering and gl context // Includes GL_VERSION, type of render loop, atlas size, etc. @@ -82,8 +83,10 @@ QT_BEGIN_NAMESPACE QSGSoftwareRenderContext::QSGSoftwareRenderContext(QSGContext *ctx) : QSGRenderContext(ctx) , m_initialized(false) + , m_activePainter(nullptr) { } + QSGSoftwareContext::QSGSoftwareContext(QObject *parent) : QSGContext(parent) { @@ -206,4 +209,12 @@ QSGRendererInterface::ShaderSourceTypes QSGSoftwareContext::shaderSourceType() c return 0; } +void *QSGSoftwareContext::getResource(QQuickWindow *window, Resource resource) const +{ + if (resource == Painter && window && window->isSceneGraphInitialized()) + return static_cast(QQuickWindowPrivate::get(window)->context)->m_activePainter; + + return nullptr; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h index 3c3686c268..dcc137b4b4 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h @@ -78,6 +78,7 @@ public: int maxTextureSize() const override; bool m_initialized; + QPainter *m_activePainter; }; class QSGSoftwareContext : public QSGContext, public QSGRendererInterface @@ -103,6 +104,7 @@ public: ShaderType shaderType() const override; ShaderCompilationTypes shaderCompilationType() const override; ShaderSourceTypes shaderSourceType() const override; + void *getResource(QQuickWindow *window, Resource resource) const override; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp index 304106a84d..f8c1a3d90b 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qsgsoftwarepixmaprenderer_p.h" +#include "qsgsoftwarecontext_p.h" #include @@ -84,6 +85,9 @@ void QSGSoftwarePixmapRenderer::render(QPaintDevice *target) QPainter painter(target); painter.setRenderHint(QPainter::Antialiasing); painter.setWindow(m_projectionRect); + auto rc = static_cast(context()); + QPainter *prevPainter = rc->m_activePainter; + rc->m_activePainter = &painter; renderTimer.start(); buildRenderList(); @@ -100,6 +104,7 @@ void QSGSoftwarePixmapRenderer::render(QPaintDevice *target) QRegion paintedRegion = renderNodes(&painter); qint64 renderTime = renderTimer.elapsed(); + rc->m_activePainter = prevPainter; qCDebug(lcPixmapRenderer) << "pixmapRender" << paintedRegion << buildRenderListTime << optimizeRenderListTime << renderTime; } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 1b2a836dfa..032a06f946 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -47,8 +47,9 @@ #include "qsgsoftwarepixmaptexture_p.h" #include "qsgsoftwarespritenode_p.h" -#include -#include +#include +#include +#include #include Q_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable") @@ -93,6 +94,9 @@ QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *nod case QSGSoftwareRenderableNode::SpriteNode: m_handle.spriteNode = static_cast(node); break; + case QSGSoftwareRenderableNode::RenderNode: + m_handle.renderNode = static_cast(node); + break; case QSGSoftwareRenderableNode::Invalid: m_handle.simpleRectNode = nullptr; break; @@ -182,6 +186,8 @@ void QSGSoftwareRenderableNode::update() m_isOpaque = m_handle.spriteNode->isOpaque(); boundingRect = m_handle.spriteNode->rect().toRect(); break; + case QSGSoftwareRenderableNode::RenderNode: + break; default: break; } @@ -203,15 +209,50 @@ void QSGSoftwareRenderableNode::update() m_dirtyRegion = QRegion(m_boundingRect); } +struct RenderNodeState : public QSGRenderNode::RenderState +{ + const QMatrix4x4 *projectionMatrix() const override { return &ident; } + QRect scissorRect() const { return QRect(); } + bool scissorEnabled() const { return false; } + int stencilValue() const { return 0; } + bool stencilEnabled() const { return false; } + const QRegion *clipRegion() const { return &cr; } + QMatrix4x4 ident; + QRegion cr; +}; + QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaquePainting) { Q_ASSERT(painter); // Check for don't paint conditions - if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) { - m_isDirty = false; - m_dirtyRegion = QRegion(); - return QRegion(); + if (m_nodeType != RenderNode) { + if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) { + m_isDirty = false; + m_dirtyRegion = QRegion(); + return QRegion(); + } + } else { + if (!m_isDirty || qFuzzyIsNull(m_opacity)) { + m_isDirty = false; + m_dirtyRegion = QRegion(); + return QRegion(); + } else { + QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(m_handle.renderNode); + QMatrix4x4 m = m_transform; + rd->m_matrix = &m; + rd->m_opacity = m_opacity; + RenderNodeState rs; + rs.cr = m_clipRegion; + painter->save(); + m_handle.renderNode->render(&rs); + painter->restore(); + const QRect fullRect = QRect(0, 0, painter->device()->width(), painter->device()->height()); + m_previousDirtyRegion = fullRect; + m_isDirty = false; + m_dirtyRegion = QRegion(); + return fullRect; + } } painter->save(); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h index dc224be2c0..0626b1e657 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h @@ -68,6 +68,7 @@ class QSGSoftwareInternalRectangleNode; class QSGSoftwareGlyphNode; class QSGSoftwareNinePatchNode; class QSGSoftwareSpriteNode; +class QSGRenderNode; class QSGSoftwareRenderableNode { @@ -83,7 +84,8 @@ public: NinePatch, SimpleRectangle, SimpleImage, - SpriteNode + SpriteNode, + RenderNode }; QSGSoftwareRenderableNode(NodeType type, QSGNode *node); @@ -126,6 +128,7 @@ private: QSGRectangleNode *simpleRectangleNode; QSGImageNode *simpleImageNode; QSGSoftwareSpriteNode *spriteNode; + QSGRenderNode *renderNode; }; const NodeType m_nodeType; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp index cb866bec12..82f8623b74 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp @@ -47,8 +47,9 @@ #include "qsgsoftwarepainternode_p.h" #include "qsgsoftwarepixmaptexture_p.h" -#include +#include #include +#include QT_BEGIN_NAMESPACE @@ -190,6 +191,15 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGSpriteNode *) } +bool QSGSoftwareRenderableNodeUpdater::visit(QSGRenderNode *node) +{ + return updateRenderableNode(QSGSoftwareRenderableNode::RenderNode, node); +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRenderNode *) +{ +} + void QSGSoftwareRenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRemoved) { m_opacityState.clear(); @@ -269,6 +279,13 @@ void QSGSoftwareRenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRem visitChildren(node); break; } + case QSGNode::RenderNodeType: { + QSGRenderNode *r = static_cast(node); + if (visit(r)) + visitChildren(r); + endVisit(r); + break; + } default: Q_UNREACHABLE(); break; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h index 5bc241cce1..f204867236 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h @@ -88,6 +88,8 @@ public: void endVisit(QSGRootNode *) override; bool visit(QSGSpriteNode *) override; void endVisit(QSGSpriteNode *) override; + bool visit(QSGRenderNode *) override; + void endVisit(QSGRenderNode *) override; void updateNodes(QSGNode *node, bool isNodeRemoved = false); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp index 257009472e..ae89ed7d8a 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp @@ -138,6 +138,9 @@ void QSGSoftwareRenderer::render() QPainter painter(m_paintDevice); painter.setRenderHint(QPainter::Antialiasing); + auto rc = static_cast(context()); + QPainter *prevPainter = rc->m_activePainter; + rc->m_activePainter = &painter; // Render the contents Renderlist m_flushRegion = renderNodes(&painter); @@ -146,6 +149,7 @@ void QSGSoftwareRenderer::render() if (m_backingStore != nullptr) m_backingStore->endPaint(); + rc->m_activePainter = prevPainter; qCDebug(lcRenderer) << "render" << m_flushRegion << buildRenderListTime << optimizeRenderListTime << renderTime; } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp index ac00127b35..4e34517dad 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp @@ -48,8 +48,9 @@ #include "qsgsoftwarepainternode_p.h" #include "qsgsoftwarepixmaptexture_p.h" -#include +#include #include +#include QT_BEGIN_NAMESPACE @@ -150,6 +151,15 @@ void QSGSoftwareRenderListBuilder::endVisit(QSGSpriteNode *) } +bool QSGSoftwareRenderListBuilder::visit(QSGRenderNode *node) +{ + return addRenderableNode(node); +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGRenderNode *) +{ +} + bool QSGSoftwareRenderListBuilder::addRenderableNode(QSGNode *node) { auto renderableNode = m_renderer->renderableNode(node); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h index e34cc81e23..807cb7fdbe 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h @@ -82,6 +82,8 @@ public: void endVisit(QSGRootNode *) override; bool visit(QSGSpriteNode *) override; void endVisit(QSGSpriteNode *) override; + bool visit(QSGRenderNode *) override; + void endVisit(QSGRenderNode *) override; private: bool addRenderableNode(QSGNode *node); diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index d45a0ea75d..bee2015007 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -2768,6 +2768,7 @@ struct RenderNodeState : public QSGRenderNode::RenderState bool scissorEnabled() const override { return m_scissorEnabled; } int stencilValue() const override { return m_stencilValue; } bool stencilEnabled() const override { return m_stencilEnabled; } + const QRegion *clipRegion() const override { return nullptr; } const QMatrix4x4 *m_projectionMatrix; QRect m_scissorRect; diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp index 04e71441f6..fa543aecad 100644 --- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp @@ -81,9 +81,10 @@ QT_BEGIN_NAMESPACE /*! \enum QSGRendererInterface::Resource - \value Device The graphics device - \value CommandQueue The graphics command queue used by the scenergaph - \value CommandList The command list or buffer used by the scenegraph + \value Device The graphics device, when applicable. + \value CommandQueue The graphics command queue used by the scenegraph, when applicable. + \value CommandList The command list or buffer used by the scenegraph, when applicable. + \value Painter The active QPainter used by the scenegraph, when running with the software backend. */ /*! @@ -134,10 +135,13 @@ QSGRendererInterface::~QSGRendererInterface() example, \c{VkDevice dev = *static_cast(result)}). The latter is necessary since such handles may have sizes different from a pointer. + \note The ownership of the returned pointer is never transferred to the caller. + \note This function must only be called on the render thread. */ -void *QSGRendererInterface::getResource(Resource resource) const +void *QSGRendererInterface::getResource(QQuickWindow *window, Resource resource) const { + Q_UNUSED(window); Q_UNUSED(resource); return nullptr; } @@ -147,10 +151,13 @@ void *QSGRendererInterface::getResource(Resource resource) const allows supporting any future resources that are not listed in the Resource enum. + \note The ownership of the returned pointer is never transferred to the caller. + \note This function must only be called on the render thread. */ -void *QSGRendererInterface::getResource(const char *resource) const +void *QSGRendererInterface::getResource(QQuickWindow *window, const char *resource) const { + Q_UNUSED(window); Q_UNUSED(resource); return nullptr; } diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.h b/src/quick/scenegraph/coreapi/qsgrendererinterface.h index 234a061d0e..a50b362aeb 100644 --- a/src/quick/scenegraph/coreapi/qsgrendererinterface.h +++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.h @@ -44,6 +44,8 @@ QT_BEGIN_NAMESPACE +class QQuickWindow; + class Q_QUICK_EXPORT QSGRendererInterface { public: @@ -57,7 +59,8 @@ public: enum Resource { Device, CommandQueue, - CommandList + CommandList, + Painter }; enum ShaderType { @@ -83,8 +86,8 @@ public: virtual GraphicsApi graphicsApi() const = 0; - virtual void *getResource(Resource resource) const; - virtual void *getResource(const char *resource) const; + virtual void *getResource(QQuickWindow *window, Resource resource) const; + virtual void *getResource(QQuickWindow *window, const char *resource) const; virtual ShaderType shaderType() const = 0; virtual ShaderCompilationTypes shaderCompilationType() const = 0; diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 29e8251cb2..365abd09e2 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -111,6 +111,10 @@ QSGRenderNodePrivate::QSGRenderNodePrivate() call related settings (root signature, descriptor heaps, etc.) are always set again by the scenegraph so render() can freely change them. + The software backend exposes its QPainter and saves and restores before and + after invoking render(). Therefore reporting any changed states from here + is not necessary. + \note This function may be called before render(). */ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const @@ -125,17 +129,6 @@ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const directly invoking commands in the graphics API (OpenGL, Direct3D, etc.) currently in use. - The states necessary for clipping has already been set before the function - is called. The clip is a combination of a stencil clip and scissor clip. - Information about the clip is found in \a state. - - \note This means that setting viewport, scissor rectangle, stencil - reference value, and similar is not necessary in render() since the - corresponding commands are on the command list (or, in case of OpenGL, the - context) already. However, for APIs other than OpenGL stencil-based - clipping will need enabling stencil testing in the pipeline state that is - used by render(). - The effective opacity can be retrieved with \l inheritedOpacity(). The projection matrix is available through \a state, while the model-view @@ -156,6 +149,12 @@ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const QQuickFramebufferObject, QQuickWindow::beforeRendering(), or the equivalents of those for APIs other than OpenGL. + Clip information is calculated before the function is called, it is however + not enabled. Implementations wishing to take clipping into account can set + up scissoring or stencil based on the information in \a state. Some + scenegraph backends, software in particular, use no scissor or stencil. + There the clip region is provided as an ordinary QRegion. + For OpenGL the following states are set on the render thread's context before this function is called: \list @@ -286,6 +285,19 @@ QSGRenderNode::RenderState::~RenderState() \c KEEP, comparison function \c EQUAL, and a read and write mask of \c 0xFF. */ +/*! + \fn const QRegion *QSGRenderNode::clipRegion() const + + \return the current clip region or null for backends where clipping is + implemented via stencil or scissoring. + + The software backend uses no projection, scissor or stencil, meaning most + of the render state is not in use. However, the clip region that can be set + on the QPainter still has to be communicated since reconstructing this + manually in render() is not reasonable. It can therefore be queried via + this function. + */ + /*! \return pointer to a \a state value. diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.h b/src/quick/scenegraph/coreapi/qsgrendernode.h index 17569f8c59..6eb425c03b 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.h +++ b/src/quick/scenegraph/coreapi/qsgrendernode.h @@ -68,6 +68,7 @@ public: virtual bool scissorEnabled() const = 0; virtual int stencilValue() const = 0; virtual bool stencilEnabled() const = 0; + virtual const QRegion *clipRegion() const = 0; virtual void *get(const char *state) const; }; diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 9574f2a48e..e219ddd82e 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -512,6 +513,13 @@ void QSGNodeVisitorEx::visitChildren(QSGNode *node) visitChildren(child); break; } + case QSGNode::RenderNodeType: { + QSGRenderNode *r = static_cast(child); + if (visit(r)) + visitChildren(r); + endVisit(r); + break; + } default: Q_UNREACHABLE(); break; diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 00441e1544..a74b38dba8 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -82,6 +82,7 @@ class QSGInternalRectangleNode; class QSGGlyphNode; class QSGRootNode; class QSGSpriteNode; +class QSGRenderNode; class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx { @@ -109,6 +110,8 @@ public: virtual void endVisit(QSGRootNode *) = 0; virtual bool visit(QSGSpriteNode *) = 0; virtual void endVisit(QSGSpriteNode *) = 0; + virtual bool visit(QSGRenderNode *) = 0; + virtual void endVisit(QSGRenderNode *) = 0; void visitChildren(QSGNode *node); }; -- cgit v1.2.3 From 764ca4053dbcb44555e9286a25f21f3a5be504e4 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Fri, 15 Jul 2016 15:46:33 +0200 Subject: SoftwareImageNodes: Support textureFiltering and mirroring Missing features in the QSGImageNode based software node. Change-Id: If6a759873d201307e013a3ab019bd906d98ba7d5 Reviewed-by: Laszlo Agocs --- .../software/qsgsoftwarepublicnodes.cpp | 54 +++++++++++++++++++++- .../software/qsgsoftwarepublicnodes_p.h | 17 ++++--- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp index c8291edd10..1fa5234377 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp @@ -57,7 +57,10 @@ void QSGSoftwareRectangleNode::paint(QPainter *painter) QSGSoftwareImageNode::QSGSoftwareImageNode() : m_texture(nullptr), - m_owns(false) + m_owns(false), + m_filtering(QSGTexture::None), + m_transformMode(NoTransform), + m_cachedMirroredPixmapIsDirty(false) { setMaterial((QSGMaterial*)1); setGeometry((QSGGeometry*)1); @@ -69,9 +72,33 @@ QSGSoftwareImageNode::~QSGSoftwareImageNode() delete m_texture; } +void QSGSoftwareImageNode::setTexture(QSGTexture *texture) +{ + m_texture = texture; markDirty(DirtyMaterial); + m_cachedMirroredPixmapIsDirty = true; +} + +void QSGSoftwareImageNode::setTextureCoordinatesTransform(QSGImageNode::TextureCoordinatesTransformMode transformNode) +{ + if (m_transformMode == transformNode) + return; + + m_transformMode = transformNode; + m_cachedMirroredPixmapIsDirty = true; + + markDirty(DirtyGeometry); +} + void QSGSoftwareImageNode::paint(QPainter *painter) { - if (QSGSoftwarePixmapTexture *pt = dynamic_cast(m_texture)) { + if (m_cachedMirroredPixmapIsDirty) + updateCachedMirroredPixmap(); + + painter->setRenderHint(QPainter::SmoothPixmapTransform, (m_filtering == QSGTexture::Linear)); + + if (!m_cachedPixmap.isNull()) { + painter->drawPixmap(m_rect, m_cachedPixmap, m_sourceRect); + } else if (QSGSoftwarePixmapTexture *pt = dynamic_cast(m_texture)) { const QPixmap &pm = pt->pixmap(); painter->drawPixmap(m_rect, pm, m_sourceRect); } else if (QSGPlainTexture *pt = dynamic_cast(m_texture)) { @@ -80,6 +107,29 @@ void QSGSoftwareImageNode::paint(QPainter *painter) } } +void QSGSoftwareImageNode::updateCachedMirroredPixmap() +{ + if (m_transformMode == NoTransform) { + m_cachedPixmap = QPixmap(); + } else { + + if (QSGSoftwarePixmapTexture *pt = dynamic_cast(m_texture)) { + QTransform mirrorTransform; + if (m_transformMode.testFlag(MirrorVertically)) + mirrorTransform = mirrorTransform.scale(1, -1); + if (m_transformMode.testFlag(MirrorHorizontally)) + mirrorTransform = mirrorTransform.scale(-1, 1); + m_cachedPixmap = pt->pixmap().transformed(mirrorTransform); + } else if (QSGPlainTexture *pt = dynamic_cast(m_texture)) { + m_cachedPixmap = QPixmap::fromImage(pt->image().mirrored(m_transformMode.testFlag(MirrorHorizontally), m_transformMode.testFlag(MirrorVertically))); + } else { + m_cachedPixmap = QPixmap(); + } + } + + m_cachedMirroredPixmapIsDirty = false; +} + QSGSoftwareNinePatchNode::QSGSoftwareNinePatchNode() { setMaterial((QSGMaterial*)1); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h index 8a11f25c82..9f1913205b 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h @@ -88,17 +88,17 @@ public: void setSourceRect(const QRectF &r) override { m_sourceRect = r; } QRectF sourceRect() const override { return m_sourceRect; } - void setTexture(QSGTexture *texture) override { m_texture = texture; markDirty(DirtyMaterial); } + void setTexture(QSGTexture *texture) override; QSGTexture *texture() const override { return m_texture; } - void setFiltering(QSGTexture::Filtering) override { } - QSGTexture::Filtering filtering() const override { return QSGTexture::None; } + void setFiltering(QSGTexture::Filtering filtering) override { m_filtering = filtering; markDirty(DirtyMaterial); } + QSGTexture::Filtering filtering() const override { return m_filtering; } void setMipmapFiltering(QSGTexture::Filtering) override { } QSGTexture::Filtering mipmapFiltering() const override { return QSGTexture::None; } - void setTextureCoordinatesTransform(TextureCoordinatesTransformMode) override { } - TextureCoordinatesTransformMode textureCoordinatesTransform() const override { return NoTransform; } + void setTextureCoordinatesTransform(TextureCoordinatesTransformMode transformNode) override; + TextureCoordinatesTransformMode textureCoordinatesTransform() const override { return m_transformMode; } void setOwnsTexture(bool owns) override { m_owns = owns; } bool ownsTexture() const override { return m_owns; } @@ -106,11 +106,16 @@ public: void paint(QPainter *painter); private: - QPixmap m_pixmap; + void updateCachedMirroredPixmap(); + + QPixmap m_cachedPixmap; QSGTexture *m_texture; QRectF m_rect; QRectF m_sourceRect; bool m_owns; + QSGTexture::Filtering m_filtering; + TextureCoordinatesTransformMode m_transformMode; + bool m_cachedMirroredPixmapIsDirty; }; class QSGSoftwareNinePatchNode : public QSGNinePatchNode -- cgit v1.2.3 From f743fc8feeef498dccf61264f6ad23d0db10f33b Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 19 Jul 2016 13:30:10 +0200 Subject: D3D12: clean up logging Make QSG_RENDERER_DEBUG more fine-grained, the maximum set is now loop;build;change;render;descheap;buffer;texture;shader meaning "render" is broken up into 5 categories. Fix also a comment on the root signature to avoid future headache with trying to optimize the descriptor table for textures away (in vain). Change-Id: I06a2a624ed39aaf5de42b8e984a192c7966360ab Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 59 ++++++++++++---------- .../scenegraph/d3d12/qsgd3d12shadereffectnode.cpp | 14 ++--- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index eab762f7c1..34ab4d11b0 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -70,6 +70,9 @@ QT_BEGIN_NAMESPACE { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; } DECLARE_DEBUG_VAR(render) +DECLARE_DEBUG_VAR(descheap) +DECLARE_DEBUG_VAR(buffer) +DECLARE_DEBUG_VAR(texture) // Except for system info on startup. Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.general") @@ -104,8 +107,8 @@ D3D12_CPU_DESCRIPTOR_HANDLE QSGD3D12CPUDescriptorHeapManager::allocate(D3D12_DES if (heap.freeMap[bucket]) { uint freePos = qCountTrailingZeroBits(heap.freeMap[bucket]); heap.freeMap[bucket] &= ~(1UL << freePos); - if (Q_UNLIKELY(debug_render())) - qDebug("descriptor handle type %x reserve in bucket %d index %d", type, bucket, freePos); + if (Q_UNLIKELY(debug_descheap())) + qDebug("descriptor handle heap %p type %x reserve in bucket %d index %d", &heap, type, bucket, freePos); freePos += bucket * DESCRIPTORS_PER_BUCKET; h = heap.start; h.ptr += freePos * heap.handleSize; @@ -131,7 +134,7 @@ D3D12_CPU_DESCRIPTOR_HANDLE QSGD3D12CPUDescriptorHeapManager::allocate(D3D12_DES heap.start = heap.heap->GetCPUDescriptorHandleForHeapStart(); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_descheap())) qDebug("new descriptor heap, type %x, start %llu", type, heap.start.ptr); heap.freeMap[0] = 0xFFFFFFFE; @@ -155,8 +158,8 @@ void QSGD3D12CPUDescriptorHeapManager::release(D3D12_CPU_DESCRIPTOR_HANDLE handl const int bucket = pos / DESCRIPTORS_PER_BUCKET; const int indexInBucket = pos - bucket * DESCRIPTORS_PER_BUCKET; heap.freeMap[bucket] |= 1UL << indexInBucket; - if (Q_UNLIKELY(debug_render())) - qDebug("free descriptor handle type %x bucket %d index %d", type, bucket, indexInBucket); + if (Q_UNLIKELY(debug_descheap())) + qDebug("free descriptor handle heap %p type %x bucket %d index %d", &heap, type, bucket, indexInBucket); return; } } @@ -1285,7 +1288,7 @@ void QSGD3D12EnginePrivate::ensureBuffer(Buffer *buf) // buffer contents rebuild with a slightly larger total size does // not lead to creating a new buffer. const quint32 sz = alignedSize(buf->cpuDataRef.size, 4096); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug("new buffer[pf=%d] of size %d (actual data size %d)", currentPFrameIndex, sz, buf->cpuDataRef.size); bfd.buffer.Attach(createBuffer(sz)); bfd.resourceSize = sz; @@ -1307,7 +1310,7 @@ void QSGD3D12EnginePrivate::updateBuffer(Buffer *buf) return; } for (const auto &r : qAsConst(buf->cpuDataRef.dirty)) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug("%p o %d s %d", buf, r.first, r.second); memcpy(p + r.first, buf->cpuDataRef.p + r.first, r.second); } @@ -1388,13 +1391,13 @@ void QSGD3D12EnginePrivate::beginFrame() for (uint id : qAsConst(prevFrameData.buffersUsedInFrame)) { Buffer &b(buffers[id - 1]); if (b.d[currentPFrameIndex].buffer && b.d[currentPFrameIndex].dataSize == b.cpuDataRef.size) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug() << "frame" << frameIndex << "takes dirty" << b.d[prevPFrameIndex].dirty << "from frame" << frameIndex - delta << "for buffer" << id; for (const auto &range : qAsConst(b.d[prevPFrameIndex].dirty)) addDirtyRange(&b.cpuDataRef.dirty, range.first, range.second, b.cpuDataRef.size); } else { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug() << "frame" << frameIndex << "makes all dirty from frame" << frameIndex - delta << "for buffer" << id; addDirtyRange(&b.cpuDataRef.dirty, 0, b.cpuDataRef.size, b.cpuDataRef.size); @@ -1408,7 +1411,7 @@ void QSGD3D12EnginePrivate::beginFrame() const quint64 finishedFrameIndex = frameIndex - frameInFlightCount; // we know since we just blocked for this // pfd conveniently refers to the same slot that was used by that frame if (!pfd.pendingTextureUploads.isEmpty()) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("Removing texture upload data for frame %d", finishedFrameIndex); for (uint id : qAsConst(pfd.pendingTextureUploads)) { const int idx = id - 1; @@ -1422,13 +1425,13 @@ void QSGD3D12EnginePrivate::beginFrame() t.lastWaitFenceValue = 0; t.stagingBuffers.clear(); t.stagingHeaps.clear(); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("Cleaned staging data for texture %u", id); } } pfd.pendingTextureUploads.clear(); if (!pfd.pendingTextureMipMap.isEmpty()) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug() << "cleaning mipmap generation data for " << pfd.pendingTextureMipMap; // no special cleanup is needed as mipmap generation uses the frame's resources pfd.pendingTextureMipMap.clear(); @@ -1442,7 +1445,7 @@ void QSGD3D12EnginePrivate::beginFrame() } } if (!hasPending) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("no more pending textures"); copyCommandAllocator->Reset(); } @@ -1563,7 +1566,7 @@ void QSGD3D12EnginePrivate::endDrawCalls(bool lastInFrame) PersistentFrameData &pfd(pframeData[currentPFrameIndex]); // Now is the time to sync all the changed areas in the buffers. - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug() << "buffers used in drawcall set" << pfd.buffersUsedInDrawCallSet; for (uint id : qAsConst(pfd.buffersUsedInDrawCallSet)) updateBuffer(&buffers[id - 1]); @@ -1588,12 +1591,12 @@ void QSGD3D12EnginePrivate::endDrawCalls(bool lastInFrame) pfd.pendingTextureMipMap.insert(id); } if (topFenceValue) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("added wait for texture fence %llu", topFenceValue); commandQueue->Wait(textureUploadFence.Get(), topFenceValue); // Generate mipmaps after the wait, when necessary. if (!pfd.pendingTextureMipMap.isEmpty()) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug() << "starting mipmap generation for" << pfd.pendingTextureMipMap; for (uint id : qAsConst(pfd.pendingTextureMipMap)) mipmapper.queueGenerate(textures[id - 1]); @@ -1672,8 +1675,8 @@ void QSGD3D12EnginePrivate::endLayer() // Root signature: // [0] CBV - always present -// [1] table with 1 SRV per texture (optional) -// one static sampler per texture (optional) +// [1] table with one SRV per texture (must be a table since root descriptor SRVs cannot be textures) - optional +// one static sampler per texture - optional // // SRVs can be created freely via QSGD3D12CPUDescriptorHeapManager and stored // in QSGD3D12TextureView. The engine will copy them onto a dedicated, @@ -2139,7 +2142,7 @@ void QSGD3D12EnginePrivate::ensureGPUDescriptorHeap(int cbvSrvUavDescriptorCount while (pfd.cbvSrvUavNextFreeDescriptorIndex + cbvSrvUavDescriptorCount > newSize) newSize *= 2; if (newSize != pfd.gpuCbvSrvUavHeapSize) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_descheap())) qDebug("Out of space for SRVs, creating new CBV-SRV-UAV descriptor heap with descriptor count %d", newSize); deferredDelete(pfd.gpuCbvSrvUavHeap); createCbvSrvUavHeap(currentPFrameIndex, newSize); @@ -2230,7 +2233,7 @@ void QSGD3D12EnginePrivate::releaseBuffer(uint id) const int idx = id - 1; Q_ASSERT(idx < buffers.count()); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug("releasing buffer %u", id); Buffer &b(buffers[idx]); @@ -2264,7 +2267,7 @@ void QSGD3D12EnginePrivate::resetBuffer(uint id, const quint8 *data, int size) Q_ASSERT(idx < buffers.count() && buffers[idx].entryInUse()); Buffer &b(buffers[idx]); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug("reset buffer %u, size %d", id, size); b.cpuDataRef.p = data; @@ -2442,7 +2445,7 @@ void QSGD3D12EnginePrivate::createTexture(uint id, const QSize &size, QImage::Fo } } - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("created texture %u, size %dx%d, miplevels %d", id, adjustedSize.width(), adjustedSize.height(), textureDesc.MipLevels); } @@ -2463,7 +2466,7 @@ void QSGD3D12EnginePrivate::queueTextureResize(uint id, const QSize &size) return; } - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("resizing texture %u, size %dx%d", id, size.width(), size.height()); D3D12_RESOURCE_DESC textureDesc = t.texture->GetDesc(); @@ -2515,7 +2518,7 @@ void QSGD3D12EnginePrivate::queueTextureResize(uint id, const QSize &size) t.fenceValue = nextTextureUploadFenceValue.fetchAndAddAcquire(1) + 1; copyCommandQueue->Signal(textureUploadFence.Get(), t.fenceValue); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("submitted old content copy for texture %u on the copy queue, fence %llu", id, t.fenceValue); } @@ -2547,7 +2550,7 @@ void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector &i t.fenceValue = nextTextureUploadFenceValue.fetchAndAddAcquire(1) + 1; - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("adding upload for texture %u on the copy queue, fence %llu", id, t.fenceValue); D3D12_RESOURCE_DESC textureDesc = t.texture->GetDesc(); @@ -2563,7 +2566,7 @@ void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector &i totalSize += alignedSize(h * stride, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT); } - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("%d sub-uploads, heap size %d bytes", images.count(), totalSize); // Instead of individual committed resources for each upload buffer, @@ -2589,7 +2592,7 @@ void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector &i QImage::Format convFormat; int bytesPerPixel; textureFormat(images[i].format(), t.alpha(), t.mipmap(), &convFormat, &bytesPerPixel); - if (Q_UNLIKELY(debug_render() && i == 0)) + if (Q_UNLIKELY(debug_texture() && i == 0)) qDebug("source image format %d, target format %d, bpp %d", images[i].format(), convFormat, bytesPerPixel); QImage convImage = images[i].format() == convFormat ? images[i] : images[i].convertToFormat(convFormat); @@ -2664,7 +2667,7 @@ void QSGD3D12EnginePrivate::releaseTexture(uint id) const int idx = id - 1; Q_ASSERT(idx < textures.count()); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("releasing texture %d", id); Texture &t(textures[idx]); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp index c423206377..62771eb8f9 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE static bool debug_ ## variable() \ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; } -DECLARE_DEBUG_VAR(render) +DECLARE_DEBUG_VAR(shader) void QSGD3D12ShaderLinker::reset(const QByteArray &vertBlob, const QByteArray &fragBlob) { @@ -598,7 +598,7 @@ QRectF QSGD3D12ShaderEffectNode::updateNormalizedTextureSubRect(bool supportsAtl void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_shader())) qDebug() << "shadereffect node sync" << syncData->dirty; if (bool(m_material.flags() & QSGMaterial::Blending) != syncData->blending) { @@ -715,7 +715,7 @@ void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData) markDirty(QSGNode::DirtyMaterial); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_shader())) m_material.linker.dump(); } else { if (syncData->dirty & QSGShaderEffectNode::DirtyShaderConstant) { @@ -724,7 +724,7 @@ void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData) if (!syncData->fragment.dirtyConstants->isEmpty()) m_material.linker.feedConstants(*syncData->fragment.shader, syncData->fragment.dirtyConstants); markDirty(QSGNode::DirtyMaterial); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_shader())) m_material.linker.dump(); } @@ -736,7 +736,7 @@ void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData) m_material.linker.linkTextureSubRects(); m_material.updateTextureProviders(false); markDirty(QSGNode::DirtyMaterial); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_shader())) m_material.linker.dump(); } } @@ -939,7 +939,7 @@ bool QSGD3D12GuiThreadShaderEffectManager::reflect(ShaderInfo *result) return false; } - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_shader())) qDebug("Shader reflection size %d type %d v%u.%u input elems %d cbuffers %d boundres %d", result->blob.size(), result->type, major, minor, ieCount, cbufferCount, boundResCount); @@ -1036,7 +1036,7 @@ bool QSGD3D12GuiThreadShaderEffectManager::reflect(ShaderInfo *result) } } - if (Q_UNLIKELY(debug_render())) { + if (Q_UNLIKELY(debug_shader())) { qDebug() << "Input:" << result->inputParameters; qDebug() << "Variables:" << result->variables << "cbuffer size" << result->constantDataSize; } -- cgit v1.2.3 From ac326046e1dffac5052cca1138078a26f34cf159 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 19 Jul 2016 13:37:01 +0200 Subject: QML: Fix property notifier registration for constant properties Constant properties typically do not have change notification signals, so do not attempt to listen to those. Change-Id: I4b3622d014aa2f8b3794a63f78ada9806c53966b Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4qobjectwrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index a2da7d6d89..20179643fd 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -362,7 +362,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje Scope scope(engine); QV4::ScopedValue rv(scope, LoadProperty(engine, object, *property, nptr)); - if (captureRequired) { + if (captureRequired && !property->isConstant()) { if (property->accessors->notifier) { if (n && ep->propertyCapture) ep->propertyCapture->captureProperty(n); -- cgit v1.2.3 From fa06494d888727d229cd47efbe7265d61c7f7c25 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 15 Jul 2016 16:15:52 +0200 Subject: QML: Fix check when to use accessors The bit indicating if accessors could be used only returned true when an interceptor was added to the object, but the property in question was not intercepted. So it missed the case where no interceptor was installed on any property of the object. Change-Id: I355eb7bbe232f512d02c1b9783a0e6f990552689 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlbinding.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 78d0a4386f..9737dfdd6b 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -463,11 +463,11 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) setEnabledFlag(e); setNotifyOnValueChanged(e); - m_nextBinding.clearFlag2(); + m_nextBinding.setFlag2(); // Always use accessors, only not when: if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(targetObject())) { int coreIndex = getPropertyCoreIndex(); - if (coreIndex != -1 && !interceptorMetaObject->intercepts(coreIndex)) - m_nextBinding.setFlag2(); + if (coreIndex == -1 || interceptorMetaObject->intercepts(coreIndex)) + m_nextBinding.clearFlag2(); } if (e) -- cgit v1.2.3 From e0e50532d29d02c8bcbab01dbfb72377102eaf8f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 6 Jun 2016 16:19:47 +0200 Subject: Added basic loading of compilation units from disk This remains hidden behind the QML_DISK_CACHE flag until all backends support it and the verification code is in place. Change-Id: Ic77c64e20a2dc4c43473c47640e09f8070237e85 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 47 +++++++++++++ src/qml/compiler/qv4compileddata_p.h | 3 + src/qml/compiler/qv4isel_moth.cpp | 7 ++ src/qml/compiler/qv4isel_moth_p.h | 2 + src/qml/compiler/qv4isel_p.h | 1 + src/qml/jit/qv4assembler.cpp | 19 ++++++ src/qml/jit/qv4assembler_p.h | 1 + src/qml/jit/qv4isel_masm.cpp | 6 ++ src/qml/jit/qv4isel_masm_p.h | 1 + src/qml/qml/qqmltypeloader.cpp | 126 +++++++++++++++++++++++++++++++++-- src/qml/qml/qqmltypeloader_p.h | 2 + 11 files changed, 209 insertions(+), 6 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 17eee03a0a..6a30faba19 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -51,6 +51,9 @@ #include #include #include +#include +#include +#include #endif #include #include @@ -327,6 +330,44 @@ bool CompilationUnit::saveToDisk(QString *errorString) return true; } +bool CompilationUnit::loadFromDisk(const QUrl &url, QString *errorString) +{ + if (!url.isLocalFile()) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + QScopedPointer cacheFile(new QFile(url.toLocalFile() + QLatin1Char('c'))); + + { + QFileInfo sourceCode(url.toLocalFile()); + if (sourceCode.exists() && sourceCode.lastModified() >= QFileInfo(*cacheFile).lastModified()) { + *errorString = QStringLiteral("QML source file is equal or newer than cached file."); + return false; + } + } + + if (!cacheFile->open(QIODevice::ReadOnly)) { + *errorString = cacheFile->errorString(); + return false; + } + + uchar *cacheData = cacheFile->map(/*offset*/0, cacheFile->size()); + if (!cacheData) { + *errorString = cacheFile->errorString(); + return false; + } + + QScopedValueRollback dataPtrChange(data, reinterpret_cast(cacheData)); + + if (!memoryMapCode(errorString)) + return false; + + dataPtrChange.commit(); + backingFile.reset(cacheFile.take()); + return true; +} + void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) { Q_UNUSED(unit); @@ -339,6 +380,12 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QStrin *errorString = QStringLiteral("Saving code to disk is not supported in this configuration"); return false; } + +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + *errorString = QStringLiteral("Missing code mapping backend"); + return false; +} #endif // V4_BOOTSTRAP Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index eef36f98e6..0f85460a08 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -845,6 +845,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount int listMetaTypeId; bool isRegisteredWithEngine; + QScopedPointer backingFile; // --- interface for QQmlPropertyCacheCreator typedef Object CompiledObject; @@ -877,11 +878,13 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount void destroy() Q_DECL_OVERRIDE; bool saveToDisk(QString *errorString); + bool loadFromDisk(const QUrl &url, QString *errorString); protected: virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit); virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); + virtual bool memoryMapCode(QString *errorString); #endif // V4_BOOTSTRAP }; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 8814ad8a78..5fa3a808ab 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -1594,3 +1594,10 @@ void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) runtimeFunctions[i] = runtimeFunction; } } + +QQmlRefPointer ISelFactory::createUnitForLoading() +{ + QQmlRefPointer result; + result.adopt(new Moth::CompilationUnit); + return result; +} diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index 29d117af38..f30b540a2b 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -207,6 +207,8 @@ public: { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } virtual bool jitCompileRegexps() const { return false; } + QQmlRefPointer createUnitForLoading() Q_DECL_OVERRIDE; + }; template diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index ecafafcea1..d93c0893ae 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -107,6 +107,7 @@ public: virtual ~EvalISelFactory() = 0; virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0; virtual bool jitCompileRegexps() const = 0; + virtual QQmlRefPointer createUnitForLoading() = 0; }; namespace IR { diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index d700449e9e..08e4f0a8c0 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -126,6 +126,25 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit return true; } +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + Q_UNUSED(errorString); + Q_ASSERT(codeRefs.isEmpty()); + codeRefs.reserve(data->functionTableSize); + + const char *basePtr = reinterpret_cast(data); + + for (uint i = 0; i < data->functionTableSize; ++i) { + const CompiledData::Function *compiledFunction = data->functionAt(i); + void *codePtr = const_cast(reinterpret_cast(basePtr + compiledFunction->codeOffset)); + JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr)); + JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize); + codeRefs.append(codeRef); + } + + return true; +} + const Assembler::VoidType Assembler::Void; Assembler::Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 9373821082..748afbfba4 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -84,6 +84,7 @@ struct CompilationUnit : public QV4::CompiledData::CompilationUnit void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; + bool memoryMapCode(QString *errorString); // Coderef + execution engine diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index a45d74bb4c..bde2c59526 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -1961,5 +1961,11 @@ void InstructionSelection::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *tru _block, trueBlock, falseBlock); } +QQmlRefPointer ISelFactory::createUnitForLoading() +{ + QQmlRefPointer result; + result.adopt(new JIT::CompilationUnit); + return result; +} #endif // ENABLE(ASSEMBLER) diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index a92196f5f7..7616ba147f 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -290,6 +290,7 @@ public: { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } virtual bool jitCompileRegexps() const { return true; } + QQmlRefPointer createUnitForLoading() Q_DECL_OVERRIDE; }; } // end of namespace JIT diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 190ac29e33..8deb41c505 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -99,6 +100,7 @@ DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); DEFINE_BOOL_CONFIG_OPTION(diskCache, QML_DISK_CACHE); +DEFINE_BOOL_CONFIG_OPTION(forceDiskCacheRefresh, QML_FORCE_DISK_CACHE_REFRESH); QT_BEGIN_NAMESPACE @@ -1953,7 +1955,9 @@ void QQmlTypeLoader::trimCache() QList unneededTypes; for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter) { QQmlTypeData *typeData = iter.value(); - if (typeData->m_compiledData && typeData->count() == 1 + // typeData->m_compiledData may be set early on in the proccess of loading a file, so it's important + // to check the general loading status of the typeData before making any other decisions. + if (typeData->isComplete() && typeData->m_compiledData && typeData->count() == 1 && typeData->m_compiledData->count() == 1) { // There are no live objects of this type unneededTypes.append(iter); @@ -2038,6 +2042,106 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) Q_ASSERT(!m_callbacks.contains(callback)); } +bool QQmlTypeData::tryLoadFromDiskCache() +{ + if (!diskCache()) + return false; + + if (forceDiskCacheRefresh()) + return false; + + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine()); + if (!v4) + return false; + + QQmlRefPointer unit = v4->iselFactory->createUnitForLoading(); + { + QString error; + if (!unit->loadFromDisk(url(), &error)) { + qDebug() << "Error loading" << url().toString() << "from disk cache:" << error; + return false; + } + } + + m_compiledData = unit; + + for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) + m_typeReferences.collectFromObject(m_compiledData->objectAt(i)); + + m_importCache.setBaseUrl(finalUrl(), finalUrlString()); + + // For remote URLs, we don't delay the loading of the implicit import + // because the loading probably requires an asynchronous fetch of the + // qmldir (so we can't load it just in time). + if (!finalUrl().scheme().isEmpty()) { + QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir"))); + if (!QQmlImports::isLocal(qmldirUrl)) { + if (!loadImplicitImport()) + return false; + + // find the implicit import + for (quint32 i = 0; i < m_compiledData->data->nImports; ++i) { + const QV4::CompiledData::Import *import = m_compiledData->data->importAt(i); + if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".") + && import->qualifierIndex == 0 + && import->majorVersion == -1 + && import->minorVersion == -1) { + QList errors; + if (!fetchQmldir(qmldirUrl, import, 1, &errors)) { + setError(errors); + return false; + } + break; + } + } + } + } + + for (int i = 0, count = m_compiledData->data->nImports; i < count; ++i) { + const QV4::CompiledData::Import *import = m_compiledData->data->importAt(i); + QList errors; + if (!addImport(import, &errors)) { + Q_ASSERT(errors.size()); + QQmlError error(errors.takeFirst()); + error.setUrl(m_importCache.baseUrl()); + error.setLine(import->location.line); + error.setColumn(import->location.column); + errors.prepend(error); // put it back on the list after filling out information. + setError(errors); + return false; + } + } + + return true; +} + +void QQmlTypeData::rebuildTypeAndPropertyCaches() +{ + Q_ASSERT(m_compiledData); + + QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + + { + QQmlCompileError error = buildTypeResolutionCaches(&m_compiledData->importCache, &m_compiledData->resolvedTypes); + if (error.isSet()) { + setError(error); + return; + } + } + + { + QQmlPropertyCacheCreator propertyCacheCreator(&m_compiledData->propertyCaches, engine, m_compiledData, &m_importCache); + QQmlCompileError error = propertyCacheCreator.buildMetaObjects(); + if (error.isSet()) { + setError(error); + return; + } + } + + QQmlPropertyCacheAliasCreator aliasCreator(&m_compiledData->propertyCaches, m_compiledData); + aliasCreator.appendAliasPropertiesToMetaObjects(); +} + void QQmlTypeData::done() { // Check all script dependencies for errors @@ -2062,7 +2166,7 @@ void QQmlTypeData::done() const TypeReference &type = *it; Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); if (type.typeData && type.typeData->isError()) { - QString typeName = m_document->stringAt(it.key()); + const QString typeName = stringAt(it.key()); QList errors = type.typeData->errors(); QQmlError error; @@ -2093,9 +2197,14 @@ void QQmlTypeData::done() } } - // Compile component - if (!isError()) - compile(); + if (!isError()) { + if (!m_document.isNull()) { + // Compile component + compile(); + } else { + rebuildTypeAndPropertyCaches(); + } + } if (!isError()) { QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); @@ -2193,6 +2302,9 @@ bool QQmlTypeData::loadImplicitImport() void QQmlTypeData::dataReceived(const Data &data) { + if (tryLoadFromDiskCache()) + return; + QString error; QString code = QString::fromUtf8(data.readAll(&error)); if (!error.isEmpty()) { @@ -2315,6 +2427,8 @@ void QQmlTypeData::downloadProgressChanged(qreal p) QString QQmlTypeData::stringAt(int index) const { + if (m_compiledData) + return m_compiledData->stringAt(index); return m_document->jsGenerator.stringTable.stringForIndex(index); } @@ -2336,7 +2450,7 @@ void QQmlTypeData::compile() setError(compiler.compilationErrors()); return; } - if (diskCache()) { + if (diskCache() || forceDiskCacheRefresh()) { QString errorString; if (!m_compiledData->saveToDisk(&errorString)) { qDebug() << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString; diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 2030dbf427..3e815878f8 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -441,10 +441,12 @@ protected: virtual QString stringAt(int index) const; private: + bool tryLoadFromDiskCache(); void continueLoadFromIR(); void resolveTypes(); QQmlCompileError buildTypeResolutionCaches(QQmlRefPointer *importCache, QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap *resolvedTypeCache) const; void compile(); + void rebuildTypeAndPropertyCaches(); bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref); virtual void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace); -- cgit v1.2.3 From 3fa5a9c0113f381cedb1b9d82d82bdd2289c57c0 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 18 Jul 2016 15:50:21 +0200 Subject: QQuickEventPoint: be a QObject-child of QQuickPointerEvent The points are conceptually children, so this will help take care of memory management. It's also useful to be able to emit or pass a QQuickEventPoint without losing the context of the event it came from. To this end, a pointerEvent() accessor is added, which simply does the static_cast for you. Change-Id: I71e57655cf1a0e7d741c4099c7eb9fc3a9a76446 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 16 ++++++++++++++-- src/quick/items/qquickevents_p_p.h | 11 +++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 5f6cadaf00..c04fbeea6e 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -492,7 +492,7 @@ void QQuickPointerEvent::initFromMouse(QMouseEvent *ev) { } if (!m_mousePoint) - m_mousePoint = new QQuickEventPoint; + m_mousePoint = new QQuickEventPoint(this); m_pointCount = 1; m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0 } @@ -507,7 +507,7 @@ void QQuickPointerEvent::initFromTouch(QTouchEvent *ev) { m_pointCount = tps.count(); m_touchPoints.reserve(m_pointCount); for (int i = m_touchPoints.size(); i < m_pointCount; ++i) - m_touchPoints.insert(i, new QQuickEventTouchPoint); + m_touchPoints.insert(i, new QQuickEventTouchPoint(this)); for (int i = 0; i < m_pointCount; ++i) m_touchPoints.at(i)->reset(tps.at(i), ev->timestamp()); @@ -563,6 +563,18 @@ bool QQuickPointerEvent::isTabletEvent() const } } +QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent) + : QObject(parent), m_pointId(0), m_timestamp(0), m_pressTimestamp(0), + m_state(Qt::TouchPointReleased), m_valid(false), m_accept(false) +{ + Q_UNUSED(m_reserved); +} + +QQuickPointerEvent *QQuickEventPoint::pointerEvent() const +{ + return static_cast(parent()); +} + QQuickEventPoint *QQuickPointerEvent::point(int i) const { if (Q_UNLIKELY(i < 0 || i >= m_pointCount)) return nullptr; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index f9668eb434..ed75556d6a 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -238,6 +238,8 @@ private: bool _accepted; }; +class QQuickPointerEvent; + class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject { Q_OBJECT @@ -248,11 +250,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) public: - QQuickEventPoint() : QObject(), m_pointId(0), m_timestamp(0), m_pressTimestamp(0), - m_state(Qt::TouchPointReleased), m_valid(false), m_accept(false) - { - Q_UNUSED(m_reserved); - } + QQuickEventPoint(QQuickPointerEvent *parent); void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp) { @@ -269,6 +267,7 @@ public: void invalidate() { m_valid = false; } + QQuickPointerEvent *pointerEvent() const; QPointF scenePos() const { return m_scenePos; } Qt::TouchPointState state() const { return m_state; } quint64 pointId() const { return m_pointId; } @@ -296,7 +295,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint Q_PROPERTY(QPointerUniqueId uniqueId READ uniqueId) public: - QQuickEventTouchPoint() : QQuickEventPoint(), m_rotation(0), m_pressure(0) { } + QQuickEventTouchPoint(QQuickPointerEvent *parent) : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) { } void reset(const QTouchEvent::TouchPoint &tp, ulong timestamp) { -- cgit v1.2.3 From ccf4878e1f3b54d547f58db13b1fb52ac24e017b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 7 Jul 2016 14:09:14 +0200 Subject: add QDebug operators for QQuickPointerEvent, Point and Device Change-Id: I25c521856cd6d73daeddacb4ae998e4de9109448 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 51 ++++++++++++++++++++++++++++++++++++++ src/quick/items/qquickevents_p_p.h | 5 ++++ 2 files changed, 56 insertions(+) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index c04fbeea6e..d7f5d347ce 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -41,6 +41,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -738,4 +739,54 @@ QTouchEvent *QQuickPointerEvent::touchEventForItem(const QListname() << ' '; + QtDebugUtils::formatQEnum(dbg, dev->type()); + dbg << ' '; + QtDebugUtils::formatQEnum(dbg, dev->pointerType()); + dbg << " caps:"; + QtDebugUtils::formatQFlags(dbg, dev->capabilities()); + if (dev->type() == QQuickPointerDevice::TouchScreen || + dev->type() == QQuickPointerDevice::TouchPad) + dbg << " maxTouchPoints:" << dev->maximumTouchPoints(); + else + dbg << " buttonCount:" << dev->buttonCount(); + dbg << ')'; + return dbg; +} + +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) { + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QQuickPointerEvent(dev:"; + QtDebugUtils::formatQEnum(dbg, event->device()->type()); + if (event->buttons() != Qt::NoButton) { + dbg << " buttons:"; + QtDebugUtils::formatQEnum(dbg, event->buttons()); + } + dbg << " ["; + int c = event->pointCount(); + for (int i = 0; i < c; ++i) + dbg << event->point(i) << ' '; + dbg << "])"; + return dbg; +} + +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) { + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QQuickEventPoint(valid:" << event->isValid() << " accepted:" << event->isAccepted() + << " state:"; + QtDebugUtils::formatQEnum(dbg, event->state()); + dbg << " scenePos:" << event->scenePos() << " id:" << event->pointId() + << " timeHeld:" << event->timeHeld() << ')'; + return dbg; +} + +#endif + QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index ed75556d6a..12022a0f80 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -471,6 +471,11 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes) Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::PointerTypes) Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::Capabilities) +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickPointerDevice *); +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickPointerEvent *); +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickEventPoint *); +//Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickEventTouchPoint *); TODO maybe + QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickKeyEvent) -- cgit v1.2.3 From 6a1667bc4e6d1074f547434d714cd47ed8ebfc41 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Jul 2016 10:13:26 +0200 Subject: Improve robustness of qml cache expiry checking Instead of relying on two time stamps in the file system (source file and cache file), make the determination on whether the source file is newer than the cache solely depend on the time stamp of only the source file. This means that when cache files are stored in archives for example their modification date does not need to be preserved upon extraction. Change-Id: I0b4362663868c6fb9bd7e106028161b2d67274d4 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 17 +++++++++-------- src/qml/compiler/qv4compileddata_p.h | 2 ++ src/qml/compiler/qv4compiler.cpp | 1 + src/qml/compiler/qv4jsir_p.h | 2 ++ src/qml/qml/qqmltypeloader.cpp | 17 ++++++++++++----- src/qml/qml/qqmltypeloader_p.h | 2 +- tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 5 +++++ 7 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6a30faba19..3f7c8df973 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -339,14 +339,6 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, QString *errorString) QScopedPointer cacheFile(new QFile(url.toLocalFile() + QLatin1Char('c'))); - { - QFileInfo sourceCode(url.toLocalFile()); - if (sourceCode.exists() && sourceCode.lastModified() >= QFileInfo(*cacheFile).lastModified()) { - *errorString = QStringLiteral("QML source file is equal or newer than cached file."); - return false; - } - } - if (!cacheFile->open(QIODevice::ReadOnly)) { *errorString = cacheFile->errorString(); return false; @@ -360,6 +352,15 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, QString *errorString) QScopedValueRollback dataPtrChange(data, reinterpret_cast(cacheData)); + { + QFileInfo sourceCode(url.toLocalFile()); + if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != data->sourceTimeStamp) { + *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + return false; + } + } + + if (!memoryMapCode(errorString)) return false; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 0f85460a08..20b68026e9 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -96,6 +96,7 @@ typedef QJsonPrivate::q_littleendian LEUInt16; typedef QJsonPrivate::q_littleendian LEUInt32; typedef QJsonPrivate::q_littleendian LEInt32; typedef QJsonPrivate::q_littleendian LEUInt64; +typedef QJsonPrivate::q_littleendian LEInt64; struct String; struct Function; @@ -599,6 +600,7 @@ struct Unit char magic[8]; LEInt16 architecture; LEInt16 version; + LEInt64 sourceTimeStamp; LEUInt32 unitSize; // Size of the Unit and any depending data. enum : unsigned int { diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index fc462bb1c6..5d13734247 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -404,6 +404,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp } unit.indexOfRootFunction = -1; unit.sourceFileIndex = getStringId(irModule->fileName); + unit.sourceTimeStamp = irModule->sourceTimeStamp; unit.nImports = 0; unit.offsetToImports = 0; unit.nObjects = 0; diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 2d6d7d728f..de84accbb1 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -932,6 +932,7 @@ struct Q_QML_PRIVATE_EXPORT Module { QVector functions; Function *rootFunction; QString fileName; + qint64 sourceTimeStamp; bool isQmlModule; // implies rootFunction is always 0 bool debugMode; @@ -939,6 +940,7 @@ struct Q_QML_PRIVATE_EXPORT Module { Module(bool debugMode) : rootFunction(0) + , sourceTimeStamp(0) , isQmlModule(false) , debugMode(debugMode) {} diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 8deb41c505..adeec52b74 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2305,14 +2305,16 @@ void QQmlTypeData::dataReceived(const Data &data) if (tryLoadFromDiskCache()) return; + qint64 sourceTimeStamp; QString error; - QString code = QString::fromUtf8(data.readAll(&error)); + QString code = QString::fromUtf8(data.readAll(&error, &sourceTimeStamp)); if (!error.isEmpty()) { setError(error); return; } QQmlEngine *qmlEngine = typeLoader()->engine(); m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0)); + m_document->jsModule.sourceTimeStamp = sourceTimeStamp; QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) { QList errors; @@ -2833,15 +2835,16 @@ struct EmptyCompilationUnit : public QV4::CompiledData::CompilationUnit void QQmlScriptBlob::dataReceived(const Data &data) { + QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); + QmlIR::Document irUnit(v4->debugger != 0); + QString error; - QString source = QString::fromUtf8(data.readAll(&error)); + QString source = QString::fromUtf8(data.readAll(&error, &irUnit.jsModule.sourceTimeStamp)); if (!error.isEmpty()) { setError(error); return; } - QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); - QmlIR::Document irUnit(v4->debugger != 0); QmlIR::ScriptDirectivesCollector collector(&irUnit.jsParserEngine, &irUnit.jsGenerator); QList errors; @@ -3006,11 +3009,13 @@ void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit * Q_UNIMPLEMENTED(); } -QByteArray QQmlDataBlob::Data::readAll(QString *error) const +QByteArray QQmlDataBlob::Data::readAll(QString *error, qint64 *sourceTimeStamp) const { Q_ASSERT(!d.isNull()); error->clear(); if (d.isT1()) { + if (sourceTimeStamp) + *sourceTimeStamp = 0; return *d.asT1(); } QFile f(*d.asT2()); @@ -3018,6 +3023,8 @@ QByteArray QQmlDataBlob::Data::readAll(QString *error) const *error = f.errorString(); return QByteArray(); } + if (sourceTimeStamp) + *sourceTimeStamp = QFileInfo(f).lastModified().toMSecsSinceEpoch(); QByteArray data(f.size(), Qt::Uninitialized); if (f.read(data.data(), data.length()) != data.length()) { *error = f.errorString(); diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 3e815878f8..4c57306ed7 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -131,7 +131,7 @@ public: class Data { public: - QByteArray readAll(QString *error) const; + QByteArray readAll(QString *error, qint64 *sourceTimeStamp = 0) const; private: friend class QQmlDataBlob; friend class QQmlTypeLoader; diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index b4697a4a3b..f85f66378a 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -31,6 +31,7 @@ #include #include #include +#include class tst_qmldiskcache: public QObject { @@ -63,6 +64,10 @@ struct TestCompiler } mappedFile.close(); + // Qt API limits the precision of QFileInfo::modificationTime() to seconds, so to ensure that + // the newly written file has a modification date newer than an existing cache file, we must + // wait. + QThread::sleep(1); { QFile f(testFilePath); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { -- cgit v1.2.3 From ffebff97a9eee8aacce059a2a671ee634d11064c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Jul 2016 11:19:49 +0200 Subject: Simplify reference management in QV4::Script There's no apparent need to store the refptr to the compilation unit in a persistent value, when the persistent's life time is bound to the life time of the V4::Script. Change-Id: Ib4f3008f45c17a680dbe12ca1f80522fd7f6fdfc Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4script.cpp | 30 ++---------------------------- src/qml/jsruntime/qv4script_p.h | 2 +- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index e8d562cf4c..97f5cbb786 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -64,30 +64,12 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace Heap { -struct CompilationUnitHolder : Object { - inline CompilationUnitHolder(CompiledData::CompilationUnit *unit); - - QQmlRefPointer unit; -}; - struct QmlBindingWrapper : FunctionObject { QmlBindingWrapper(QV4::QmlContext *scope, Function *f); }; } -struct CompilationUnitHolder : public Object -{ - V4_OBJECT2(CompilationUnitHolder, Object) - V4_NEEDS_DESTROY -}; - -inline -Heap::CompilationUnitHolder::CompilationUnitHolder(CompiledData::CompilationUnit *unit) - : unit(unit) -{ -} - struct QmlBindingWrapper : FunctionObject { V4_OBJECT2(QmlBindingWrapper, FunctionObject) @@ -101,7 +83,6 @@ QT_END_NAMESPACE using namespace QV4; DEFINE_OBJECT_VTABLE(QmlBindingWrapper); -DEFINE_OBJECT_VTABLE(CompilationUnitHolder); Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::QmlContext *scope, Function *f) : Heap::FunctionObject(scope, scope->d()->engine->id_eval(), /*createProto = */ false) @@ -139,7 +120,7 @@ void QmlBindingWrapper::call(const Managed *that, Scope &scope, CallData *callDa Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit) : line(0), column(0), scope(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) - , vmFunction(0), parseAsBinding(true) + , compilationUnit(compilationUnit), vmFunction(0), parseAsBinding(true) { if (qml) qmlContext.set(v4, *qml); @@ -147,11 +128,6 @@ Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUn parsed = true; vmFunction = compilationUnit ? compilationUnit->linkToEngine(v4) : 0; - if (vmFunction) { - Scope valueScope(v4); - ScopedObject holder(valueScope, v4->memoryManager->allocObject(compilationUnit)); - compilationUnitHolder.set(v4, holder); - } } Script::~Script() @@ -218,10 +194,8 @@ void Script::parse() QScopedPointer isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator)); if (inheritContext) isel->setUseFastLookups(false); - QQmlRefPointer compilationUnit = isel->compile(); + compilationUnit = isel->compile(); vmFunction = compilationUnit->linkToEngine(v4); - ScopedObject holder(valueScope, v4->memoryManager->allocObject(compilationUnit)); - compilationUnitHolder.set(v4, holder); } if (!vmFunction) { diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index e13388393b..e81bc3049c 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -126,7 +126,7 @@ struct Q_QML_EXPORT Script { bool inheritContext; bool parsed; QV4::PersistentValue qmlContext; - QV4::PersistentValue compilationUnitHolder; + QQmlRefPointer compilationUnit; Function *vmFunction; bool parseAsBinding; -- cgit v1.2.3 From 877ce4d446b34375096f5e7fae30101405d7ea95 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Jul 2016 14:11:20 +0200 Subject: Add support for caching of .js files imported from QML sources This requires only minimal changes. Change-Id: I89e16bad97bf26669f8e4f4336ac6d32e5027c35 Reviewed-by: Lars Knoll --- src/qml/qml/qqmltypeloader.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index adeec52b74..52fed14ecb 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2836,6 +2836,19 @@ struct EmptyCompilationUnit : public QV4::CompiledData::CompilationUnit void QQmlScriptBlob::dataReceived(const Data &data) { QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); + + if (diskCache() && !forceDiskCacheRefresh()) { + QQmlRefPointer unit = v4->iselFactory->createUnitForLoading(); + QString error; + if (unit->loadFromDisk(url(), &error)) { + initializeFromCompilationUnit(unit); + return; + } else { + qDebug() << "Error loading" << url().toString() << "from disk cache:" << error; + } + } + + QmlIR::Document irUnit(v4->debugger != 0); QString error; @@ -2869,6 +2882,13 @@ void QQmlScriptBlob::dataReceived(const Data &data) // The js unit owns the data and will free the qml unit. unit->data = unitData; + if (diskCache() || forceDiskCacheRefresh()) { + QString errorString; + if (!unit->saveToDisk(&errorString)) { + qDebug() << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString; + } + } + initializeFromCompilationUnit(unit); } -- cgit v1.2.3 From 28a5042370c3d1a5b983518753549bb585e78e6e Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 19 Jul 2016 17:30:52 +0200 Subject: Move forward declarations to the top of the header Change-Id: I58af7a1603bca3a17713e4cb674ae004e2d6eda9 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 12022a0f80..ceebe6207d 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -61,6 +61,9 @@ QT_BEGIN_NAMESPACE +class QQuickItem; +class QQuickPointerDevice; + class QQuickKeyEvent : public QObject { Q_OBJECT @@ -315,9 +318,6 @@ private: QPointerUniqueId m_uniqueId; }; -class QQuickItem; -class QQuickPointerDevice; - class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject { Q_OBJECT -- cgit v1.2.3 From aa5f09a0d22e9fa6e125afc798f6b3289088b791 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 28 Jun 2016 10:13:17 +0200 Subject: Add tryVerify() This is useful for conditions which can't be tested using tryCompare. One such situation is that of the currentItem property of ListView. In Qt Quick Controls 2, the currentItem property of the ListView that is internally a child of Tumbler can be null for a certain period of time, so using tryCompare() would result in errors due to trying to access a property of a null object: tryCompare(tumblerView.currentItem, "text", "2") The current workaround is to use wait(50) in a for loop, which is ugly and could lead to flaky tests: for (var delay = 1000; delay >= 0; delay -= 50) { if (tumblerView.currentItem) break; wait(50); } verify(tumblerView.currentItem); compare(tumblerView.currentItem.text, data.currentIndex.toString()); Using tryVerify(), we can first ensure that currentItem isn't null, and then use a regular synchronous compare afterwards: tryVerify(function(){ return tumblerView.currentItem; }); compare(tumbler.currentItem.text, data.currentIndex.toString()); [ChangeLog][QtTest][TestCase] Added tryVerify() function to allow verification of asynchronous conditions that can't be tested using tryCompare(). Change-Id: Ie93052b650f7fe0bf26853054a8f0f35a483e387 Task-number: QTBUG-19708 Reviewed-by: Frederik Gladhorn --- src/imports/testlib/TestCase.qml | 67 ++++++++++++++++++++++++ tests/auto/qmltest/selftests/tst_tryVerify.qml | 72 ++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 tests/auto/qmltest/selftests/tst_tryVerify.qml diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index d5e256a123..bc6786f9f8 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -394,6 +394,73 @@ Item { throw new Error("QtQuickTest::fail") } + /*! + \since 5.8 + \qmlmethod TestCase::tryVerify(function, timeout = 5000, message = "") + + Fails the current test case if \a function does not evaluate to + \c true before the specified \a timeout (in milliseconds) has elapsed. + The function is evaluated multiple times until the timeout is + reached. An optional \a message is displayed upon failure. + + This function is intended for testing applications where a condition + changes based on asynchronous events. Use verify() for testing + synchronous condition changes, and tryCompare() for testing + asynchronous property changes. + + For example, in the code below, it's not possible to use tryCompare(), + because the \c currentItem property might be \c null for a short period + of time: + + \code + tryCompare(listView.currentItem, "text", "Hello"); + \endcode + + Instead, we can use tryVerify() to first check that \c currentItem + isn't \c null, and then use a regular compare afterwards: + + \code + tryVerify(function(){ return listView.currentItem }) + compare(listView.currentItem.text, "Hello") + \endcode + + \sa verify(), compare(), tryCompare(), SignalSpy::wait() + */ + function tryVerify(expressionFunction, timeout, msg) { + if (!expressionFunction || !(expressionFunction instanceof Function)) { + qtest_results.fail("First argument must be a function", util.callerFile(), util.callerLine()) + throw new Error("QtQuickTest::fail") + } + + if (timeout && typeof(timeout) !== "number") { + qtest_results.fail("timeout argument must be a number", util.callerFile(), util.callerLine()) + throw new Error("QtQuickTest::fail") + } + + if (msg && typeof(msg) !== "string") { + qtest_results.fail("message argument must be a string", util.callerFile(), util.callerLine()) + throw new Error("QtQuickTest::fail") + } + + if (!timeout) + timeout = 5000 + + if (msg === undefined) + msg = "function returned false" + + if (!expressionFunction()) + wait(0) + + var i = 0 + while (i < timeout && !expressionFunction()) { + wait(50) + i += 50 + } + + if (!qtest_results.verify(expressionFunction(), msg, util.callerFile(), util.callerLine())) + throw new Error("QtQuickTest::fail") + } + /*! \internal */ // Determine what is o. // Discussions and reference: http://philrathe.com/articles/equiv diff --git a/tests/auto/qmltest/selftests/tst_tryVerify.qml b/tests/auto/qmltest/selftests/tst_tryVerify.qml new file mode 100644 index 0000000000..6f29d8643d --- /dev/null +++ b/tests/auto/qmltest/selftests/tst_tryVerify.qml @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +import QtQuick 2.8 +import QtTest 1.1 + +TestCase { + name: "tst_tryVerify" + + Item { + id: item + } + + QtObject { + id: itemContainer + property Item i + } + + Timer { + id: timer + interval: 100 + onTriggered: itemContainer.i = item + } + + function resetTimer() { + itemContainer.i = null; + timer.restart(); + } + + function test_tryVerify() { + timer.start(); + tryVerify(function(){ return itemContainer.i; }, 200, "string"); + compare(itemContainer.i, item); + + resetTimer(); + tryVerify(function(){ return itemContainer.i; }, 200); + compare(itemContainer.i, item); + + resetTimer(); + tryVerify(function(){ return itemContainer.i; }); + compare(itemContainer.i, item); + + resetTimer(); + tryVerify(function(){ return !itemContainer.i; }, 0, "string"); + verify(!itemContainer.i); + } +} -- cgit v1.2.3 From 06ed1ad17abe1ef597d4ecf86962da5ac21365e5 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 19 Jul 2016 11:28:14 +0200 Subject: JS: Check for errors before using sub-expression results Specifically: don't de-reference a result and assume that it's not-null. Task-number: QTBUG-54687 Change-Id: If07d3250a95a7815ab7a3262b88e0227965ef8e7 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4codegen.cpp | 116 +++++++++++++++++---- .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 7 ++ 2 files changed, 102 insertions(+), 21 deletions(-) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index c14163a2f7..6bf931c882 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1052,6 +1052,8 @@ bool Codegen::visit(ArrayLiteral *ast) current->expr = _block->CONST(IR::MissingType, 0); } Result expr = expression(it->expression); + if (hasError) + return false; IR::ExprList *arg = _function->New(); if (!current) { @@ -1094,6 +1096,8 @@ bool Codegen::visit(ArrayMemberExpression *ast) Result base = expression(ast->base); Result index = expression(ast->expression); + if (hasError) + return false; _expr.code = subscript(*base, *index); return false; } @@ -1133,10 +1137,16 @@ bool Codegen::visit(BinaryExpression *ast) const unsigned r = _block->newTemp(); - move(_block->TEMP(r), *expression(ast->left)); + Result lhs = expression(ast->left); + if (hasError) + return false; + move(_block->TEMP(r), *lhs); setLocation(cjump(_block->TEMP(r), iftrue, endif), ast->operatorToken); _block = iftrue; - move(_block->TEMP(r), *expression(ast->right)); + Result rhs = expression(ast->right); + if (hasError) + return false; + move(_block->TEMP(r), *rhs); _block->JUMP(endif); _expr.code = _block->TEMP(r); @@ -1154,10 +1164,16 @@ bool Codegen::visit(BinaryExpression *ast) IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); const unsigned r = _block->newTemp(); - move(_block->TEMP(r), *expression(ast->left)); + Result lhs = expression(ast->left); + if (hasError) + return false; + move(_block->TEMP(r), *lhs); setLocation(cjump(_block->TEMP(r), endif, iffalse), ast->operatorToken); _block = iffalse; - move(_block->TEMP(r), *expression(ast->right)); + Result rhs = expression(ast->right); + if (hasError) + return false; + move(_block->TEMP(r), *rhs); _block->JUMP(endif); _block = endif; @@ -1167,6 +1183,8 @@ bool Codegen::visit(BinaryExpression *ast) } IR::Expr* left = *expression(ast->left); + if (hasError) + return false; switch (ast->op) { case QSOperator::Or: @@ -1176,17 +1194,19 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::Assign: { if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation())) return false; - IR::Expr* right = *expression(ast->right); + Result right = expression(ast->right); + if (hasError) + return false; if (!left->isLValue()) { throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue")); return false; } if (_expr.accept(nx)) { - move(left, right); + move(left, *right); } else { const unsigned t = _block->newTemp(); - move(_block->TEMP(t), right); + move(_block->TEMP(t), *right); move(left, _block->TEMP(t)); _expr.code = _block->TEMP(t); } @@ -1206,17 +1226,19 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::InplaceXor: { if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation())) return false; - IR::Expr* right = *expression(ast->right); + Result right = expression(ast->right); + if (hasError) + return false; if (!left->isLValue()) { throwSyntaxError(ast->operatorToken, QStringLiteral("left-hand side of inplace operator is not an lvalue")); return false; } if (_expr.accept(nx)) { - move(left, right, baseOp(ast->op)); + move(left, *right, baseOp(ast->op)); } else { const unsigned t = _block->newTemp(); - move(_block->TEMP(t), right); + move(_block->TEMP(t), *right); move(left, _block->TEMP(t), baseOp(ast->op)); _expr.code = left; } @@ -1239,12 +1261,14 @@ bool Codegen::visit(BinaryExpression *ast) left = _block->TEMP(t); } - IR::Expr* right = *expression(ast->right); + Result right = expression(ast->right); + if (hasError) + return false; if (_expr.accept(cx)) { - setLocation(cjump(binop(IR::binaryOperator(ast->op), left, right, ast->operatorToken), _expr.iftrue, _expr.iffalse), ast->operatorToken); + setLocation(cjump(binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken), _expr.iftrue, _expr.iffalse), ast->operatorToken); } else { - IR::Expr *e = binop(IR::binaryOperator(ast->op), left, right, ast->operatorToken); + IR::Expr *e = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); if (e->asConst() || e->asString()) _expr.code = e; else { @@ -1273,9 +1297,11 @@ bool Codegen::visit(BinaryExpression *ast) left = _block->TEMP(t); } - IR::Expr* right = *expression(ast->right); + Result right = expression(ast->right); + if (hasError) + return false; - IR::Expr *e = binop(IR::binaryOperator(ast->op), left, right, ast->operatorToken); + IR::Expr *e = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); if (e->asConst() || e->asString()) _expr.code = e; else { @@ -1300,11 +1326,15 @@ bool Codegen::visit(CallExpression *ast) IR::ExprList *args = 0, **args_it = &args; for (ArgumentList *it = ast->arguments; it; it = it->next) { Result arg = expression(it->expression); + if (hasError) + return false; IR::Expr *actual = argument(*arg); *args_it = _function->New(); (*args_it)->init(actual); args_it = &(*args_it)->next; } + if (hasError) + return false; _expr.code = call(*base, args); return false; } @@ -1323,11 +1353,17 @@ bool Codegen::visit(ConditionalExpression *ast) condition(ast->expression, iftrue, iffalse); _block = iftrue; - move(_block->TEMP(t), *expression(ast->ok)); + Result ok = expression(ast->ok); + if (hasError) + return false; + move(_block->TEMP(t), *ok); _block->JUMP(endif); _block = iffalse; - move(_block->TEMP(t), *expression(ast->ko)); + Result ko = expression(ast->ko); + if (hasError) + return false; + move(_block->TEMP(t), *ko); _block->JUMP(endif); _block = endif; @@ -1343,6 +1379,8 @@ bool Codegen::visit(DeleteExpression *ast) return false; IR::Expr* expr = *expression(ast->expression); + if (hasError) + return false; // Temporaries cannot be deleted IR::ArgLocal *al = expr->asArgLocal(); if (al && al->index < static_cast(_env->members.size())) { @@ -1404,7 +1442,8 @@ bool Codegen::visit(FieldMemberExpression *ast) return false; Result base = expression(ast->base); - _expr.code = member(*base, _function->newString(ast->name.toString())); + if (!hasError) + _expr.code = member(*base, _function->newString(ast->name.toString())); return false; } @@ -1495,6 +1534,8 @@ bool Codegen::visit(NewExpression *ast) return false; Result base = expression(ast->expression); + if (hasError) + return false; IR::Expr *expr = *base; if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) { const unsigned t = _block->newTemp(); @@ -1511,6 +1552,8 @@ bool Codegen::visit(NewMemberExpression *ast) return false; Result base = expression(ast->base); + if (hasError) + return false; IR::Expr *expr = *base; if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) { const unsigned t = _block->newTemp(); @@ -1521,6 +1564,8 @@ bool Codegen::visit(NewMemberExpression *ast) IR::ExprList *args = 0, **args_it = &args; for (ArgumentList *it = ast->arguments; it; it = it->next) { Result arg = expression(it->expression); + if (hasError) + return false; IR::Expr *actual = argument(*arg); *args_it = _function->New(); (*args_it)->init(actual); @@ -1538,6 +1583,8 @@ bool Codegen::visit(NotExpression *ast) return false; Result expr = expression(ast->expression); + if (hasError) + return false; const unsigned r = _block->newTemp(); setLocation(move(_block->TEMP(r), unop(IR::OpNot, *expr, ast->notToken)), ast->notToken); _expr.code = _block->TEMP(r); @@ -1595,6 +1642,8 @@ bool Codegen::visit(ObjectLiteral *ast) QString name = it->assignment->name->asString(); if (PropertyNameAndValue *nv = AST::cast(it->assignment)) { Result value = expression(nv->value); + if (hasError) + return false; ObjectPropertyValue &v = valueMap[name]; if (v.hasGetter() || v.hasSetter() || (_function->isStrict && v.value)) { throwSyntaxError(nv->lastSourceLocation(), @@ -1725,6 +1774,8 @@ bool Codegen::visit(PostDecrementExpression *ast) return false; Result expr = expression(ast->base); + if (hasError) + return false; if (!expr->isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); return false; @@ -1751,6 +1802,8 @@ bool Codegen::visit(PostIncrementExpression *ast) return false; Result expr = expression(ast->base); + if (hasError) + return false; if (!expr->isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); return false; @@ -1777,6 +1830,8 @@ bool Codegen::visit(PreDecrementExpression *ast) return false; Result expr = expression(ast->expression); + if (hasError) + return false; if (!expr->isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); return false; @@ -1802,6 +1857,8 @@ bool Codegen::visit(PreIncrementExpression *ast) return false; Result expr = expression(ast->expression); + if (hasError) + return false; if (!expr->isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); return false; @@ -1854,6 +1911,8 @@ bool Codegen::visit(TildeExpression *ast) return false; Result expr = expression(ast->expression); + if (hasError) + return false; const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), unop(IR::OpCompl, *expr, ast->tildeToken)), ast->tildeToken); _expr.code = _block->TEMP(t); @@ -1879,6 +1938,8 @@ bool Codegen::visit(TypeOfExpression *ast) return false; Result expr = expression(ast->expression); + if (hasError) + return false; IR::ExprList *args = _function->New(); args->init(reference(*expr)); _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); @@ -1891,6 +1952,8 @@ bool Codegen::visit(UnaryMinusExpression *ast) return false; Result expr = expression(ast->expression); + if (hasError) + return false; const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), unop(IR::OpUMinus, *expr, ast->minusToken)), ast->minusToken); _expr.code = _block->TEMP(t); @@ -1903,6 +1966,8 @@ bool Codegen::visit(UnaryPlusExpression *ast) return false; Result expr = expression(ast->expression); + if (hasError) + return false; const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), unop(IR::OpUPlus, *expr, ast->plusToken)), ast->plusToken); _expr.code = _block->TEMP(t); @@ -2217,7 +2282,10 @@ bool Codegen::visit(ForEachStatement *ast) IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler()); int objectToIterateOn = _block->newTemp(); - move(_block->TEMP(objectToIterateOn), *expression(ast->expression)); + Result expr = expression(ast->expression); + if (hasError) + return false; + move(_block->TEMP(objectToIterateOn), *expr); IR::ExprList *args = _function->New(); args->init(_block->TEMP(objectToIterateOn)); @@ -2229,7 +2297,10 @@ bool Codegen::visit(ForEachStatement *ast) _block = foreachbody; int temp = _block->newTemp(); - move(*expression(ast->initialiser), _block->TEMP(temp)); + Result init = expression(ast->initialiser); + if (hasError) + return false; + move(*init, _block->TEMP(temp)); statement(ast->statement); _block->JUMP(foreachin); @@ -2719,7 +2790,10 @@ bool Codegen::visit(WithStatement *ast) _function->hasWith = true; const int withObject = _block->newTemp(); - _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); + Result src = expression(ast->expression); + if (hasError) + return false; + _block->MOVE(_block->TEMP(withObject), *src); // need an exception handler for with to cleanup the with scope IR::BasicBlock *withExceptionHandler = _function->newBasicBlock(exceptionHandler()); diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 2ec296ae66..aaa72d48cd 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -329,6 +329,7 @@ private slots: void qtbug_46022(); void qtbug_52340(); void qtbug_54589(); + void qtbug_54687(); private: // static void propertyVarWeakRefCallback(v8::Persistent object, void* parameter); @@ -7932,6 +7933,12 @@ void tst_qqmlecmascript::qtbug_54589() QCOMPARE(obj->property("result").toBool(), true); } +void tst_qqmlecmascript::qtbug_54687() +{ + QJSEngine e; + // it's simple: this shouldn't crash. + engine.evaluate("12\n----12"); +} QTEST_MAIN(tst_qqmlecmascript) -- cgit v1.2.3 From d48b397cc79265e80c8437888f9ded0b0364e418 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Wed, 20 Jul 2016 11:30:39 +0200 Subject: Doc: syntax error qml example onRelased --> onReleased Change-Id: I0078229b66bc8db4c70789d0536de70166ee0852 Reviewed-by: Martin Smith Reviewed-by: Mitch Curtis --- src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index 04d0d0ed2e..de0515e5d0 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -729,7 +729,7 @@ Rectangle { MouseArea { anchors.fill: parent onPressed: root.activated(mouse.x, mouse.y) - onRelased: root.deactivated() + onReleased: root.deactivated() } } \endqml -- cgit v1.2.3 From d6ac86f59250e05eb5e7db5bb8a45a85db1f0f25 Mon Sep 17 00:00:00 2001 From: Maximiliano Curia Date: Tue, 19 Jul 2016 15:49:48 +0200 Subject: Fix QQmlEngine crash on big endian 64 bit architectures (s390x) This change disables the BIG_ENDIAN 32 bits words mangling in 64 bits machines (where the words are 64 bits long); this would otherwise result in a segfault. Task-number: QTBUG-54717 Change-Id: I6b5ab6f213880b030795185c05e609d290168901 Reviewed-by: Simon Hausmann Reviewed-by: Timo Jyrinki Reviewed-by: Erik Verbruggen --- src/qml/jsruntime/qv4value_p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 7c2bb31a00..5abf5ad9e8 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -96,13 +96,13 @@ public: Q_ALWAYS_INLINE quint64 rawValue() const { return _val; } Q_ALWAYS_INLINE void setRawValue(quint64 raw) { _val = raw; } -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN || defined(QV4_USE_64_BIT_VALUE_ENCODING) static inline int valueOffset() { return 0; } static inline int tagOffset() { return 4; } Q_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } Q_ALWAYS_INLINE quint32 value() const { return _val & quint64(~quint32(0)); } Q_ALWAYS_INLINE quint32 tag() const { return _val >> 32; } -#else // !Q_LITTLE_ENDIAN +#else // !Q_LITTLE_ENDIAN && !defined(QV4_USE_64_BIT_VALUE_ENCODING) static inline int valueOffset() { return 4; } static inline int tagOffset() { return 0; } Q_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(value) << 32 | tag; } -- cgit v1.2.3 From e4aaa3845ce153273be8322cf95808ee04bb30c8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 20 Jul 2016 12:38:31 +0200 Subject: Prepare moth bytecode for serialization When using the threaded interpreter, don't produce the instruction handler addresses right away but perform that as a separate step when linking against the engine. Change-Id: I216100f8b99f45eb8d954c733ac128e83aa6da38 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4instr_moth_p.h | 2 +- src/qml/compiler/qv4isel_moth.cpp | 28 +++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 93a7170e68..e95a7f7046 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -162,7 +162,7 @@ QT_BEGIN_NAMESPACE #define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1) #ifdef MOTH_THREADED_INTERPRETER -# define MOTH_INSTR_HEADER void *code; +# define MOTH_INSTR_HEADER union { quint32 instructionType; void *code; }; #else # define MOTH_INSTR_HEADER quint32 instructionType; #endif diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 5fa3a808ab..c7217183ac 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -1493,12 +1493,7 @@ void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject() ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) { - -#ifdef MOTH_THREADED_INTERPRETER - instr.common.code = VME::instructionJumpTable()[static_cast(type)]; -#else instr.common.instructionType = type; -#endif int instructionSize = Instr::size(type); if (_codeEnd - _codeNext < instructionSize) { @@ -1584,6 +1579,29 @@ CompilationUnit::~CompilationUnit() void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) { +#ifdef MOTH_THREADED_INTERPRETER + // link byte code against addresses of instructions + for (int i = 0; i < codeRefs.count(); ++i) { + QByteArray &codeRef = codeRefs[i]; + char *code = codeRef.data(); + int index = 0; + while (index < codeRef.size()) { + Instr *genericInstr = reinterpret_cast(code + index); + + switch (genericInstr->common.instructionType) { +#define LINK_INSTRUCTION(InstructionType, Member) \ + case Instr::InstructionType: \ + genericInstr->common.code = VME::instructionJumpTable()[static_cast(genericInstr->common.instructionType)]; \ + index += InstrMeta<(int)Instr::InstructionType>::Size; \ + break; + + FOR_EACH_MOTH_INSTR(LINK_INSTRUCTION) + + } + } + } +#endif + runtimeFunctions.resize(data->functionTableSize); runtimeFunctions.fill(0); for (int i = 0 ;i < runtimeFunctions.size(); ++i) { -- cgit v1.2.3 From a6b2c5100afd3236042f9a44811686bf67508a52 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 20 Jul 2016 12:50:56 +0200 Subject: Add support for saving/loading the moth interpreter byte code With the JIT and moth supporting persistence, we can enable the disk cache tests unconditionally. Change-Id: I6d6652411237001433a32a2de21d1f78f51b43ef Reviewed-by: Lars Knoll --- src/qml/compiler/qv4isel_moth.cpp | 72 ++++++++++++++++++++++++ src/qml/compiler/qv4isel_moth_p.h | 5 +- tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 10 ---- 3 files changed, 76 insertions(+), 11 deletions(-) diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index c7217183ac..283948727b 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #undef USE_TYPE_INFO @@ -1613,6 +1614,77 @@ void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) } } +void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) +{ + const int codeAlignment = 16; + quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize); + Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + for (int i = 0; i < codeRefs.size(); ++i) { + CompiledData::Function *compiledFunction = const_cast(unit->functionAt(i)); + compiledFunction->codeOffset = offset; + compiledFunction->codeSize = codeRefs.at(i).size(); + offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize); + } +} + +bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) +{ + Q_ASSERT(device->pos() == unit->unitSize); + Q_ASSERT(device->atEnd()); + Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + + QByteArray padding; + + for (int i = 0; i < codeRefs.size(); ++i) { + const CompiledData::Function *compiledFunction = unit->functionAt(i); + + if (device->pos() > qint64(compiledFunction->codeOffset)) { + *errorString = QStringLiteral("Invalid state of cache file to write."); + return false; + } + + const quint64 paddingSize = compiledFunction->codeOffset - device->pos(); + padding.fill(0, paddingSize); + qint64 written = device->write(padding); + if (written != padding.size()) { + *errorString = device->errorString(); + return false; + } + + const void *codePtr = codeRefs.at(i).constData(); + written = device->write(reinterpret_cast(codePtr), compiledFunction->codeSize); + if (written != qint64(compiledFunction->codeSize)) { + *errorString = device->errorString(); + return false; + } + } + return true; +} + +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + Q_UNUSED(errorString); + Q_ASSERT(codeRefs.isEmpty()); + codeRefs.reserve(data->functionTableSize); + + const char *basePtr = reinterpret_cast(data); + + for (uint i = 0; i < data->functionTableSize; ++i) { + const CompiledData::Function *compiledFunction = data->functionAt(i); + const char *codePtr = const_cast(reinterpret_cast(basePtr + compiledFunction->codeOffset)); +#ifdef MOTH_THREADED_INTERPRETER + // for the threaded interpreter we need to make a copy of the data because it needs to be + // modified for the instruction handler addresses. + QByteArray code(codePtr, compiledFunction->codeSize); +#else + QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); +#endif + codeRefs.append(code); + } + + return true; +} + QQmlRefPointer ISelFactory::createUnitForLoading() { QQmlRefPointer result; diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index f30b540a2b..eaca299c94 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -66,7 +66,10 @@ namespace Moth { struct CompilationUnit : public QV4::CompiledData::CompilationUnit { virtual ~CompilationUnit(); - virtual void linkBackendToEngine(QV4::ExecutionEngine *engine); + void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; + void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; + bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; + bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; QVector codeRefs; diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index f85f66378a..bb78f2856f 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -134,7 +134,6 @@ void tst_qmldiskcache::regenerateAfterChange() QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); -#ifdef V4_ENABLE_JIT { const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); @@ -151,10 +150,6 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1); QVERIFY(bindingFunction->codeOffset > testUnit->unitSize); } -#else - QVERIFY(!testCompiler.mapUnit()); - return; -#endif engine.clearComponentCache(); @@ -196,7 +191,6 @@ void tst_qmldiskcache::registerImportForImplicitComponent() "}"); QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); -#ifdef V4_ENABLE_JIT { const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); @@ -218,10 +212,6 @@ void tst_qmldiskcache::registerImportForImplicitComponent() const QV4::CompiledData::Object *implicitComponent = testUnit->objectAt(obj->bindingTable()->value.objectIndex); QCOMPARE(testUnit->stringAt(implicitComponent->inheritedTypeNameIndex), QStringLiteral("QmlInternals.") + componentType->elementName()); } -#else - QVERIFY(!testCompiler.mapUnit()); - return; -#endif } QTEST_MAIN(tst_qmldiskcache) -- cgit v1.2.3 From c016634478a4c9f480f7d9d5c85dded307a80d13 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 20 Jul 2016 09:25:46 +0200 Subject: Fix string property assignments to 2d vectors and quaternions Just like it's possible to assign "1,2,3" to a QVector3D, the same should be possible for a QVector2D and a QQuaternion. Task-number: QTBUG-54858 Change-Id: I8f394279dcdf5c057876efaa316b4bad51a4c126 Reviewed-by: Robin Burchell --- src/qml/compiler/qqmltypecompiler.cpp | 24 ++++++++++++++++++++ src/qml/qml/qqmlobjectcreator.cpp | 26 ++++++++++++++++++++++ .../qml/qqmllanguage/data/assignBasicTypes.qml | 2 ++ tests/auto/qml/qqmllanguage/testtypes.h | 24 ++++++++++++++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 2 ++ 5 files changed, 78 insertions(+) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index fcc0ca8d14..7e4ee344ff 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -2244,6 +2244,17 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa } } break; + case QVariant::Vector2D: { + struct { + float xp; + float yp; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { + recordError(binding->valueLocation, tr("Invalid property assignment: 2D vector expected")); + return false; + } + } + break; case QVariant::Vector3D: { struct { float xp; @@ -2269,6 +2280,19 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa } } break; + case QVariant::Quaternion: { + struct { + float wp; + float xp; + float yp; + float zp; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { + recordError(binding->valueLocation, tr("Invalid property assignment: quaternion expected")); + return false; + } + } + break; case QVariant::RegExp: recordError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); return false; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 15c38c1d5b..0c13a7a017 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -507,6 +507,18 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; + case QVariant::Vector2D: { + struct { + float xp; + float yp; + } vec; + bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec)); + Q_ASSERT(ok); + Q_UNUSED(ok); + argv[0] = reinterpret_cast(&vec); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } + break; case QVariant::Vector3D: { struct { float xp; @@ -534,6 +546,20 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; + case QVariant::Quaternion: { + struct { + float wp; + float xp; + float yp; + float zp; + } vec; + bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec)); + Q_ASSERT(ok); + Q_UNUSED(ok); + argv[0] = reinterpret_cast(&vec); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } + break; case QVariant::RegExp: Q_ASSERT(!"not possible"); break; diff --git a/tests/auto/qml/qqmllanguage/data/assignBasicTypes.qml b/tests/auto/qml/qqmllanguage/data/assignBasicTypes.qml index c91cf581b3..52027232db 100644 --- a/tests/auto/qml/qqmllanguage/data/assignBasicTypes.qml +++ b/tests/auto/qml/qqmllanguage/data/assignBasicTypes.qml @@ -26,7 +26,9 @@ MyTypeObject { boolProperty: true variantProperty: "Hello World!" vectorProperty: "10,1,2.2" + vector2Property: "2, 3" vector4Property: "10,1,2.2,2.3" + quaternionProperty: "4,5,6,7" urlProperty: "main.qml?with%3cencoded%3edata" objectProperty: MyTypeObject { intProperty: 8 } diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index c6c956cf36..be1129f4ab 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -38,8 +38,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -247,8 +249,10 @@ class MyTypeObject : public QObject Q_PROPERTY(QRectF rectFProperty READ rectFProperty WRITE setRectFProperty NOTIFY rectFPropertyChanged) Q_PROPERTY(bool boolProperty READ boolProperty WRITE setBoolProperty NOTIFY boolPropertyChanged) Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty NOTIFY variantPropertyChanged) + Q_PROPERTY(QVector2D vector2Property READ vector2Property WRITE setVector2Property NOTIFY vector2PropertyChanged) Q_PROPERTY(QVector3D vectorProperty READ vectorProperty WRITE setVectorProperty NOTIFY vectorPropertyChanged) Q_PROPERTY(QVector4D vector4Property READ vector4Property WRITE setVector4Property NOTIFY vector4PropertyChanged) + Q_PROPERTY(QQuaternion quaternionProperty READ quaternionProperty WRITE setQuaternionProperty NOTIFY quaternionPropertyChanged) Q_PROPERTY(QUrl urlProperty READ urlProperty WRITE setUrlProperty NOTIFY urlPropertyChanged) Q_PROPERTY(QQmlScriptString scriptProperty READ scriptProperty WRITE setScriptProperty) @@ -529,6 +533,15 @@ public: emit vectorPropertyChanged(); } + QVector2D vector2PropertyValue; + QVector2D vector2Property() const { + return vector2PropertyValue; + } + void setVector2Property(const QVector2D &v) { + vector2PropertyValue = v; + emit vector2PropertyChanged(); + } + QVector4D vector4PropertyValue; QVector4D vector4Property() const { return vector4PropertyValue; @@ -538,6 +551,15 @@ public: emit vector4PropertyChanged(); } + QQuaternion quaternionPropertyValue; + QQuaternion quaternionProperty() const { + return quaternionPropertyValue; + } + void setQuaternionProperty(const QQuaternion &v) { + quaternionPropertyValue = v; + emit quaternionPropertyChanged(); + } + QUrl urlPropertyValue; QUrl urlProperty() const { return urlPropertyValue; @@ -591,7 +613,9 @@ signals: void boolPropertyChanged(); void variantPropertyChanged(); void vectorPropertyChanged(); + void vector2PropertyChanged(); void vector4PropertyChanged(); + void quaternionPropertyChanged(); void urlPropertyChanged(); }; diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index f66caa31f1..2a519d55f0 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -682,7 +682,9 @@ void tst_qqmllanguage::assignBasicTypes() QCOMPARE(object->boolProperty(), true); QCOMPARE(object->variantProperty(), QVariant("Hello World!")); QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2f)); + QCOMPARE(object->vector2Property(), QVector2D(2, 3)); QCOMPARE(object->vector4Property(), QVector4D(10, 1, 2.2f, 2.3f)); + QCOMPARE(object->quaternionProperty(), QQuaternion(4, 5, 6, 7)); QUrl encoded; encoded.setEncodedUrl("main.qml?with%3cencoded%3edata", QUrl::TolerantMode); QCOMPARE(object->urlProperty(), component.url().resolved(encoded)); -- cgit v1.2.3 From 8780d416875000f3935c286f53dfaa3b6c6450fe Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 20 Jul 2016 13:59:42 +0200 Subject: Remove unused variable Change-Id: Ibfac6509696ad62da8741d3ccb938ecdcc94ecd3 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow_p.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 940ac7c77b..7e66aef9f4 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -247,10 +247,6 @@ public: QQuickAnimatorController *animationController; QScopedPointer delayedTouch; - // An event instance for each device that we've seen so far. - // One of these gets re-used (reset) for every incoming mouse/touch/tablet event. - // One reason to keep them separate is so that m_touchPoints will be only those from a single device. - QVector pointerEventsByDevice; int pointerEventRecursionGuard; QQuickCustomRenderStage *customRenderStage; -- cgit v1.2.3 From f727b9baad6a061fbd3625afe0295077dccff610 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 20 Jul 2016 13:01:28 +0200 Subject: Make touch device work in case of 0 device in QTouchEvent QTestLib events often don't have a device associated, we might also run into this with other synthetic user generated events. Change-Id: I301cf30fe8695446fe30aaacd08286bd39b18216 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index fbc8eb86ca..9a0cff1abc 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1880,14 +1880,24 @@ QQuickPointerDevice *QQuickWindowPrivate::touchDevice(QTouchDevice *d) return touchDevices.value(d); QQuickPointerDevice::DeviceType type = QQuickPointerDevice::TouchScreen; - QQuickPointerDevice::Capabilities caps = - static_cast(static_cast(d->capabilities()) & 0x0F); - if (d->type() == QTouchDevice::TouchPad) { - type = QQuickPointerDevice::TouchPad; - caps |= QQuickPointerDevice::Scroll; + QString name; + int maximumTouchPoints = 10; + QQuickPointerDevice::Capabilities caps = QQuickPointerDevice::Capabilities(QTouchDevice::Position); + if (d) { + QQuickPointerDevice::Capabilities caps = + static_cast(static_cast(d->capabilities()) & 0x0F); + if (d->type() == QTouchDevice::TouchPad) { + type = QQuickPointerDevice::TouchPad; + caps |= QQuickPointerDevice::Scroll; + } + name = d->name(); + maximumTouchPoints = d->maximumTouchPoints(); + } else { + qWarning() << "QQuickWindowPrivate::touchDevice: creating touch device from nullptr device in QTouchEvent"; } + QQuickPointerDevice *dev = new QQuickPointerDevice(type, QQuickPointerDevice::Finger, - caps, d->maximumTouchPoints(), 0, d->name(), 0); + caps, maximumTouchPoints, 0, name, 0); touchDevices.insert(d, dev); return dev; } -- cgit v1.2.3 From c856d877e77048701182cc0e5ae275c398e55166 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 15 Jul 2016 16:24:34 +0200 Subject: Hierarchy for touch and mouse pointer events Change-Id: Ie84e39918d9657b29df697be7b0e5198298c48ba Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 129 ++++++++------------- src/quick/items/qquickevents_p_p.h | 100 +++++++++++----- src/quick/items/qquickwindow.cpp | 3 +- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 67 ++++++----- 4 files changed, 151 insertions(+), 148 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index d7f5d347ce..cb48255c4e 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -441,6 +441,11 @@ Item { \l inverted always returns false. */ + +QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) + : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) +{} + /*! \internal \class QQuickPointerEvent @@ -455,23 +460,11 @@ Item { dynamically create and destroy objects of this type for each event. */ -/*! - \internal - Reset the current event to \a ev, which must be a touch, mouse or tablet event. -*/ -QQuickPointerEvent *QQuickPointerEvent::reset(QEvent *ev) { - m_event = static_cast(ev); - if (isMouseEvent()) { - initFromMouse(static_cast(ev)); - } else if (isTouchEvent()) { - initFromTouch(static_cast(ev)); - } else { - Q_ASSERT_X(false, "", "invalid event type"); - } - return this; -} +QQuickPointerEvent::~QQuickPointerEvent() +{} -void QQuickPointerEvent::initFromMouse(QMouseEvent *ev) { +QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) { + auto ev = static_cast(event); m_device = QQuickWindowPrivate::genericMouseDevice; m_event = ev; m_button = ev->button(); @@ -491,14 +484,12 @@ void QQuickPointerEvent::initFromMouse(QMouseEvent *ev) { default: break; } - - if (!m_mousePoint) - m_mousePoint = new QQuickEventPoint(this); - m_pointCount = 1; m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0 + return this; } -void QQuickPointerEvent::initFromTouch(QTouchEvent *ev) { +QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) { + auto ev = static_cast(event); m_device = QQuickWindowPrivate::touchDevice(ev->device()); m_event = ev; m_button = Qt::NoButton; @@ -512,6 +503,7 @@ void QQuickPointerEvent::initFromTouch(QTouchEvent *ev) { for (int i = 0; i < m_pointCount; ++i) m_touchPoints.at(i)->reset(tps.at(i), ev->timestamp()); + return this; } /*! @@ -534,34 +526,16 @@ QMouseEvent *QQuickPointerEvent::asMouseEvent() const { return nullptr; } -bool QQuickPointerEvent::isMouseEvent() const -{ - return m_event - && m_event->type() >= QEvent::MouseButtonPress - && m_event->type() <= QEvent::MouseMove; -} - -bool QQuickPointerEvent::isTouchEvent() const -{ - return m_event - && ((m_event->type() >= QEvent::TouchBegin && m_event->type() <= QEvent::TouchEnd) - || m_event->type() == QEvent::TouchCancel); +QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const { + if (i == 0) + return m_mousePoint; + return nullptr; } -bool QQuickPointerEvent::isTabletEvent() const -{ - if (!m_event) - return false; - switch (m_event->type()) { - case QEvent::TabletPress: - case QEvent::TabletRelease: - case QEvent::TabletMove: - case QEvent::TabletEnterProximity: - case QEvent::TabletLeaveProximity: - return true; - default: - return false; - } +QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { + if (i >= 0 && i < m_pointCount) + return m_touchPoints.at(i); + return nullptr; } QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent) @@ -576,27 +550,14 @@ QQuickPointerEvent *QQuickEventPoint::pointerEvent() const return static_cast(parent()); } -QQuickEventPoint *QQuickPointerEvent::point(int i) const { - if (Q_UNLIKELY(i < 0 || i >= m_pointCount)) - return nullptr; - if (isTouchEvent()) - return m_touchPoints.at(i); - if (isMouseEvent()) - return m_mousePoint; - return nullptr; +bool QQuickPointerMouseEvent::allPointsAccepted() const { + return m_mousePoint->isAccepted(); } -bool QQuickPointerEvent::allPointsAccepted() const -{ - Q_ASSERT(m_event && !isTabletEvent()); - if (isMouseEvent()) { - return m_mousePoint->isAccepted(); - } - if (isTouchEvent()) { - for (int i = 0; i < m_pointCount; ++i) { - if (!m_touchPoints.at(i)->isAccepted()) - return false; - } +bool QQuickPointerTouchEvent::allPointsAccepted() const { + for (int i = 0; i < m_pointCount; ++i) { + if (!m_touchPoints.at(i)->isAccepted()) + return false; } return true; } @@ -608,7 +569,7 @@ bool QQuickPointerEvent::allPointsAccepted() const If the touchpoint cannot be found, this returns nullptr. Ownership of the event is NOT transferred to the caller. */ -QMouseEvent *QQuickPointerEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const { +QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const { const QTouchEvent::TouchPoint *p = touchPointById(pointID); if (!p) return nullptr; @@ -652,30 +613,34 @@ QMouseEvent *QQuickPointerEvent::syntheticMouseEvent(int pointID, QQuickItem *re Returns a pointer to the QQuickEventPoint which has the \a pointId as \l {QQuickEventPoint::pointId}{pointId}. Returns nullptr if there is no point with that ID. + + \fn QQuickPointerEvent::pointById(quint64 pointId) const */ -QQuickEventPoint *QQuickPointerEvent::pointById(quint64 pointId) const { - if (isMouseEvent()) { - if (m_mousePoint && pointId == m_mousePoint->pointId()) - return m_mousePoint; - } - if (isTouchEvent()) { - auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(), - [pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } ); - if (it != m_touchPoints.constEnd()) { - return *it; - } - } - // TODO it could alternatively be a tablet point + + + +QQuickEventPoint *QQuickPointerMouseEvent::pointById(quint64 pointId) const { + if (m_mousePoint && pointId == m_mousePoint->pointId()) + return m_mousePoint; return nullptr; } +QQuickEventPoint *QQuickPointerTouchEvent::pointById(quint64 pointId) const { + auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(), + [pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } ); + if (it != m_touchPoints.constEnd()) + return *it; + return nullptr; +} + + /*! \internal Returns a pointer to the original TouchPoint which has the same \l {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a QTouchEvent, and if that point is found. Otherwise, returns nullptr. */ -const QTouchEvent::TouchPoint *QQuickPointerEvent::touchPointById(int pointId) const { +const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const { const QTouchEvent *ev = asTouchEvent(); if (!ev) return nullptr; @@ -690,7 +655,7 @@ const QTouchEvent::TouchPoint *QQuickPointerEvent::touchPointById(int pointId) c \internal Make a new QTouchEvent, giving it a subset of the original touch points. */ -QTouchEvent *QQuickPointerEvent::touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const +QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const { QList touchPoints; Qt::TouchPointStates eventStates; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index ceebe6207d..123d5e5280 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -63,6 +63,8 @@ QT_BEGIN_NAMESPACE class QQuickItem; class QQuickPointerDevice; +class QQuickPointerEvent; +class QQuickPointerTouchEvent; class QQuickKeyEvent : public QObject { @@ -241,8 +243,6 @@ private: bool _accepted; }; -class QQuickPointerEvent; - class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject { Q_OBJECT @@ -298,7 +298,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint Q_PROPERTY(QPointerUniqueId uniqueId READ uniqueId) public: - QQuickEventTouchPoint(QQuickPointerEvent *parent) : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) { } + QQuickEventTouchPoint(QQuickPointerTouchEvent *parent); void reset(const QTouchEvent::TouchPoint &tp, ulong timestamp) { @@ -333,10 +333,10 @@ public: , m_event(nullptr) , m_button(Qt::NoButton) , m_pressedButtons(Qt::NoButton) - , m_pointCount(0) - , m_mousePoint(nullptr) , m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier) { } + virtual ~QQuickPointerEvent(); + public: // property accessors const QQuickPointerDevice *device() const { return m_device; } Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; } @@ -344,44 +344,74 @@ public: // property accessors Qt::MouseButtons buttons() const { return m_pressedButtons; } public: // helpers for C++ only (during event delivery) - QQuickPointerEvent *reset(QEvent *ev); + virtual QQuickPointerEvent *reset(QEvent *ev) = 0; QTouchEvent *asTouchEvent() const; QMouseEvent *asMouseEvent() const; - bool isMouseEvent() const; - bool isTouchEvent() const; - bool isTabletEvent() const; + virtual bool isMouseEvent() const { return false; } + virtual bool isTouchEvent() const { return false; } + virtual bool isTabletEvent() const { return false; } bool isValid() const { return m_event != nullptr; } - bool allPointsAccepted() const; - - int pointCount() const { return m_pointCount; } - QQuickEventPoint *point(int i) const; - QQuickEventPoint *pointById(quint64 pointId) const; + virtual bool allPointsAccepted() const = 0; - const QTouchEvent::TouchPoint *touchPointById(int pointId) const; + virtual int pointCount() const = 0; + virtual QQuickEventPoint *point(int i) const = 0; + virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; - QTouchEvent *touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const; - - QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; - -private: - void initFromMouse(QMouseEvent *ev); - void initFromTouch(QTouchEvent *ev); +protected: -private: const QQuickPointerDevice *m_device; QInputEvent *m_event; // original event as received by QQuickWindow Qt::MouseButton m_button; Qt::MouseButtons m_pressedButtons; - int m_pointCount; - QVector m_touchPoints; - QQuickEventPoint *m_mousePoint; mutable QMouseEvent m_synthMouseEvent; Q_DISABLE_COPY(QQuickPointerEvent) }; +class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent +{ +public: + QQuickPointerMouseEvent(QObject *parent = nullptr) + : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) + { + } + + QQuickPointerEvent *reset(QEvent *) override; + bool isMouseEvent() const override { return true; } + int pointCount() const override { return 1; } + QQuickEventPoint *point(int i) const override; + QQuickEventPoint *pointById(quint64 pointId) const override; + bool allPointsAccepted() const override; + +private: + QQuickEventPoint *m_mousePoint; +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent +{ +public: + QQuickPointerTouchEvent(QObject *parent = nullptr) + : QQuickPointerEvent(parent) + {} + + QQuickPointerEvent *reset(QEvent *) override; + bool isTouchEvent() const override { return true; } + int pointCount() const override { return m_pointCount; } + QQuickEventPoint *point(int i) const override; + QQuickEventPoint *pointById(quint64 pointId) const override; + const QTouchEvent::TouchPoint *touchPointById(int pointId) const; + bool allPointsAccepted() const override; + + QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; + QTouchEvent *touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const; + +private: + int m_pointCount; + QVector m_touchPoints; +}; + // ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject { @@ -439,10 +469,18 @@ public: QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) - , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId) - {} + , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId), m_event(nullptr) + { + if (m_deviceType == Mouse) { + m_event = new QQuickPointerMouseEvent; + } else if (m_deviceType == TouchScreen || m_deviceType == TouchPad) { + m_event = new QQuickPointerTouchEvent; + } else { + Q_ASSERT(false); + } + } - ~QQuickPointerDevice() { } + ~QQuickPointerDevice() { delete m_event; } DeviceType type() const { return m_deviceType; } PointerType pointerType() const { return m_pointerType; } Capabilities capabilities() const { return m_capabilities; } @@ -451,7 +489,7 @@ public: int buttonCount() const { return m_buttonCount; } QString name() const { return m_name; } qint64 uniqueId() const { return m_uniqueId; } - QQuickPointerEvent *pointerEvent() const { return &m_event; } + QQuickPointerEvent *pointerEvent() const { return m_event; } private: DeviceType m_deviceType; @@ -462,7 +500,7 @@ private: QString m_name; qint64 m_uniqueId; // the device-specific event instance which is reused during event delivery - mutable QQuickPointerEvent m_event; + QQuickPointerEvent *m_event; Q_DISABLE_COPY(QQuickPointerDevice) }; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 9a0cff1abc..2210194114 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2365,7 +2365,8 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ const QSet &matchingNewPoints, const QList &matchingPoints, QSet *hasFiltered) { - QScopedPointer touchEvent(event->touchEventForItem(matchingPoints, item)); + auto pointerTouchEvent = static_cast(event); + QScopedPointer touchEvent(pointerTouchEvent->touchEventForItem(matchingPoints, item)); if (touchEvent.data()->touchPoints().isEmpty()) return false; bool touchEventAccepted = false; diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index fd1024a8b3..18f1b28b02 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -2322,48 +2322,47 @@ void tst_qquickwindow::testHoverChildMouseEventFilter() void tst_qquickwindow::pointerEventTypeAndPointCount() { - QQuickPointerEvent pe; QMouseEvent me(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QTouchEvent te(QEvent::TouchBegin, touchDevice, Qt::NoModifier, Qt::TouchPointPressed, QList() << QTouchEvent::TouchPoint(1)); - QVERIFY(!pe.isValid()); - - pe.reset(&me); - QVERIFY(pe.isValid()); - QVERIFY(pe.isMouseEvent()); - QVERIFY(!pe.isTouchEvent()); - QVERIFY(!pe.isTabletEvent()); - QVERIFY(pe.asMouseEvent()); - QVERIFY(!pe.asTouchEvent()); -// QVERIFY(!pe.asTabletEvent()); // TODO - QCOMPARE(pe.pointCount(), 1); - - pe.reset(&te); - QVERIFY(pe.isValid()); - QVERIFY(!pe.isMouseEvent()); - QVERIFY(pe.isTouchEvent()); - QVERIFY(!pe.isTabletEvent()); - QVERIFY(!pe.asMouseEvent()); - QVERIFY(pe.asTouchEvent()); -// QVERIFY(!pe.asTabletEvent()); // TODO - QCOMPARE(pe.pointCount(), 1); - QCOMPARE(pe.touchPointById(1)->id(), 1); - QVERIFY(!pe.touchPointById(0)); + QQuickPointerMouseEvent pme; + pme.reset(&me); + QVERIFY(pme.isValid()); + QVERIFY(pme.isMouseEvent()); + QVERIFY(!pme.isTouchEvent()); + QVERIFY(!pme.isTabletEvent()); + QVERIFY(pme.asMouseEvent()); + QVERIFY(!pme.asTouchEvent()); +// QVERIFY(!pe->asTabletEvent()); // TODO + QCOMPARE(pme.pointCount(), 1); + + QQuickPointerTouchEvent pte; + pte.reset(&te); + QVERIFY(pte.isValid()); + QVERIFY(!pte.isMouseEvent()); + QVERIFY(pte.isTouchEvent()); + QVERIFY(!pte.isTabletEvent()); + QVERIFY(!pte.asMouseEvent()); + QVERIFY(pte.asTouchEvent()); +// QVERIFY(!pte.asTabletEvent()); // TODO + QCOMPARE(pte.pointCount(), 1); + QCOMPARE(pte.touchPointById(1)->id(), 1); + QVERIFY(!pte.touchPointById(0)); te.setTouchPoints(QList() << QTouchEvent::TouchPoint(1) << QTouchEvent::TouchPoint(2)); - pe.reset(&te); - QCOMPARE(pe.pointCount(), 2); - QCOMPARE(pe.touchPointById(1)->id(), 1); - QCOMPARE(pe.touchPointById(2)->id(), 2); - QVERIFY(!pe.touchPointById(0)); + pte.reset(&te); + QCOMPARE(pte.pointCount(), 2); + QCOMPARE(pte.touchPointById(1)->id(), 1); + QCOMPARE(pte.touchPointById(2)->id(), 2); + QVERIFY(!pte.touchPointById(0)); te.setTouchPoints(QList() << QTouchEvent::TouchPoint(2)); - pe.reset(&te); - QCOMPARE(pe.pointCount(), 1); - QCOMPARE(pe.touchPointById(2)->id(), 2); - QVERIFY(!pe.touchPointById(1)); - QVERIFY(!pe.touchPointById(0)); + pte.reset(&te); + QCOMPARE(pte.pointCount(), 1); + QCOMPARE(pte.touchPointById(2)->id(), 2); + QVERIFY(!pte.touchPointById(1)); + QVERIFY(!pte.touchPointById(0)); } QTEST_MAIN(tst_qquickwindow) -- cgit v1.2.3 From a5e7897dd7705a9df3a97e5de75b5b95d6a88e25 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 21 Jul 2016 11:35:42 +0200 Subject: Improve encapsuation of touch/mouse event specific things This makes it easy to avoid casts when using the classes. Change-Id: I27bd1244bffb3a7d2cdb4572c229333e4c499d9b Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 30 ++++++++-------------- src/quick/items/qquickevents_p_p.h | 24 +++++++++++------ src/quick/items/qquickwindow.cpp | 13 +++++----- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 17 ++++++------ 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index cb48255c4e..e4a080d2b7 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -506,26 +506,6 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) { return this; } -/*! - \internal - Returns the original touch event, or nullptr if it was not a touch event. -*/ -QTouchEvent *QQuickPointerEvent::asTouchEvent() const { - if (!isTouchEvent()) - return nullptr; - return static_cast(m_event); -} - -/*! - \internal - Returns the original mouse event, or nullptr if it was not a mouse event. -*/ -QMouseEvent *QQuickPointerEvent::asMouseEvent() const { - if (isMouseEvent()) - return static_cast(m_event); - return nullptr; -} - QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const { if (i == 0) return m_mousePoint; @@ -554,6 +534,11 @@ bool QQuickPointerMouseEvent::allPointsAccepted() const { return m_mousePoint->isAccepted(); } +QMouseEvent *QQuickPointerMouseEvent::asMouseEvent() const +{ + return static_cast(m_event); +} + bool QQuickPointerTouchEvent::allPointsAccepted() const { for (int i = 0; i < m_pointCount; ++i) { if (!m_touchPoints.at(i)->isAccepted()) @@ -704,6 +689,11 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(const QList(m_event); +} + #ifndef QT_NO_DEBUG_STREAM Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) { diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 123d5e5280..9e37955914 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -64,6 +64,8 @@ QT_BEGIN_NAMESPACE class QQuickItem; class QQuickPointerDevice; class QQuickPointerEvent; +class QQuickPointerMouseEvent; +class QQuickPointerTabletEvent; class QQuickPointerTouchEvent; class QQuickKeyEvent : public QObject @@ -346,12 +348,12 @@ public: // property accessors public: // helpers for C++ only (during event delivery) virtual QQuickPointerEvent *reset(QEvent *ev) = 0; - QTouchEvent *asTouchEvent() const; - QMouseEvent *asMouseEvent() const; - - virtual bool isMouseEvent() const { return false; } - virtual bool isTouchEvent() const { return false; } - virtual bool isTabletEvent() const { return false; } + virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } + virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; } + virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; } + virtual const QQuickPointerMouseEvent *asPointerMouseEvent() const { return nullptr; } + virtual const QQuickPointerTouchEvent *asPointerTouchEvent() const { return nullptr; } + virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } bool isValid() const { return m_event != nullptr; } virtual bool allPointsAccepted() const = 0; @@ -379,12 +381,15 @@ public: } QQuickPointerEvent *reset(QEvent *) override; - bool isMouseEvent() const override { return true; } + QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; } + const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; } int pointCount() const override { return 1; } QQuickEventPoint *point(int i) const override; QQuickEventPoint *pointById(quint64 pointId) const override; bool allPointsAccepted() const override; + QMouseEvent *asMouseEvent() const; + private: QQuickEventPoint *m_mousePoint; }; @@ -397,7 +402,8 @@ public: {} QQuickPointerEvent *reset(QEvent *) override; - bool isTouchEvent() const override { return true; } + QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; } + const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; } int pointCount() const override { return m_pointCount; } QQuickEventPoint *point(int i) const override; QQuickEventPoint *pointById(quint64 pointId) const override; @@ -407,6 +413,8 @@ public: QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; QTouchEvent *touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const; + QTouchEvent *asTouchEvent() const; + private: int m_pointCount; QVector m_touchPoints; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 2210194114..0c88c511eb 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2174,9 +2174,9 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) // the usecase a bit evil, but we at least don't want to lose events. ++pointerEventRecursionGuard; - if (QMouseEvent *mouse = event->asMouseEvent()) { - deliverMouseEvent(mouse); - } else if (event->isTouchEvent()) { + if (QQuickPointerMouseEvent *mouse = event->asPointerMouseEvent()) { + deliverMouseEvent(mouse->asMouseEvent()); + } else if (event->asPointerTouchEvent()) { deliverTouchEvent(event); } else { Q_ASSERT(false); @@ -2217,7 +2217,7 @@ QVector QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerEvent *event) { - qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); + qCDebug(DBG_TOUCH) << " - delivering" << event->asPointerTouchEvent()->asTouchEvent(); // List of all items that received an event before // When we have TouchBegin this is and will stay empty @@ -2365,7 +2365,8 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ const QSet &matchingNewPoints, const QList &matchingPoints, QSet *hasFiltered) { - auto pointerTouchEvent = static_cast(event); + auto pointerTouchEvent = event->asPointerTouchEvent(); + Q_ASSERT(pointerTouchEvent); QScopedPointer touchEvent(pointerTouchEvent->touchEventForItem(matchingPoints, item)); if (touchEvent.data()->touchPoints().isEmpty()) return false; @@ -2375,7 +2376,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ // First check whether the parent wants to be a filter, // and if the parent accepts the event we are done. - if (sendFilteredTouchEvent(item->parentItem(), item, event->asTouchEvent(), hasFiltered)) { + if (sendFilteredTouchEvent(item->parentItem(), item, pointerTouchEvent->asTouchEvent(), hasFiltered)) { // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 18f1b28b02..1e1197637b 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -2329,21 +2329,20 @@ void tst_qquickwindow::pointerEventTypeAndPointCount() QQuickPointerMouseEvent pme; pme.reset(&me); QVERIFY(pme.isValid()); - QVERIFY(pme.isMouseEvent()); - QVERIFY(!pme.isTouchEvent()); - QVERIFY(!pme.isTabletEvent()); - QVERIFY(pme.asMouseEvent()); - QVERIFY(!pme.asTouchEvent()); + QCOMPARE(pme.asMouseEvent(), &me); + QVERIFY(pme.asPointerMouseEvent()); + QVERIFY(!pme.asPointerTouchEvent()); + QVERIFY(!pme.asPointerTabletEvent()); // QVERIFY(!pe->asTabletEvent()); // TODO QCOMPARE(pme.pointCount(), 1); QQuickPointerTouchEvent pte; pte.reset(&te); QVERIFY(pte.isValid()); - QVERIFY(!pte.isMouseEvent()); - QVERIFY(pte.isTouchEvent()); - QVERIFY(!pte.isTabletEvent()); - QVERIFY(!pte.asMouseEvent()); + QCOMPARE(pte.asTouchEvent(), &te); + QVERIFY(!pte.asPointerMouseEvent()); + QVERIFY(pte.asPointerTouchEvent()); + QVERIFY(!pte.asPointerTabletEvent()); QVERIFY(pte.asTouchEvent()); // QVERIFY(!pte.asTabletEvent()); // TODO QCOMPARE(pte.pointCount(), 1); -- cgit v1.2.3 From 0a87552e8122cdda58160da2dd549da411d9093c Mon Sep 17 00:00:00 2001 From: Dan Cape Date: Wed, 7 Oct 2015 15:46:14 -0400 Subject: Fix QQuickItem's setAcceptedMouseButtons function When using setAcceptedMouseButtons to only allow the LeftButton, the user can click the LeftButton and while still holding it press the RightButton. There would be a press event sent for both. To resolve this, a check needed to be added to ensure the acceptedMouseButtons are checked when a second press comes in. [ChangeLog][QtQuick][QQuickItem] Fixed issue with mouse button events being sent even when they were disabled by setAcceptedMouseButtons. Change-Id: I064f3ff56ede12b1572e172be326eb337e280750 Task-number: QTBUG-31861 Reviewed-by: Frederik Gladhorn Reviewed-by: Robin Burchell Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 6 ++++++ tests/auto/quick/qquickitem/tst_qquickitem.cpp | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index de1b5f236e..8a2471b34f 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1584,6 +1584,12 @@ bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) } if (mouseGrabberItem) { + if (event->button() != Qt::NoButton + && mouseGrabberItem->acceptedMouseButtons() + && !(mouseGrabberItem->acceptedMouseButtons() & event->button())) { + event->ignore(); + return false; + } QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos()); QScopedPointer me(cloneMouseEvent(event, &localPos)); me->accept(); diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index 73e691b08c..6a49c4dd7e 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -172,6 +172,8 @@ private slots: void contains_data(); void contains(); + void ignoreButtonPressNotInAcceptedMouseButtons(); + private: enum PaintOrderOp { @@ -1971,6 +1973,27 @@ void tst_qquickitem::contains() QCOMPARE(result.toBool(), contains); } +void tst_qquickitem::ignoreButtonPressNotInAcceptedMouseButtons() +{ + // Verify the fix for QTBUG-31861 + TestItem item; + QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::NoButton)); + + QQuickWindow window; + item.setSize(QSizeF(200,100)); + item.setParentItem(window.contentItem()); + + item.setAcceptedMouseButtons(Qt::LeftButton); + QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::LeftButton)); + + QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(50, 50)); + QTest::mousePress(&window, Qt::RightButton, 0, QPoint(50, 50)); // ignored because it's not LeftButton + QTest::mouseRelease(&window, Qt::RightButton, 0, QPoint(50, 50)); // ignored because it didn't grab the RightButton press + QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(50, 50)); + + QCOMPARE(item.pressCount, 1); + QCOMPARE(item.releaseCount, 1); +} QTEST_MAIN(tst_qquickitem) -- cgit v1.2.3 From 6c630da50d28262d755ad6cb46aadff0b12e24ef Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Thu, 21 Jul 2016 17:12:52 +0200 Subject: V4 CompilationUnit: missing #include for date-time comparison MinGW's cc got upset at a QDateTime comparison when it only had a forward declaration of the type. Change-Id: I0404248cbdfc35610d8465294534bd5201dcf2a2 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compileddata.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 3f7c8df973..1bcd6892d2 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From c3a8f7b0eb824f974f752992e6d1dbd1651ab7bf Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 22 Jul 2016 22:00:15 -0700 Subject: Fix ICC change-of-sign warning INT_MIN is negative. qv4assembler_p.h(108): warning #68: integer conversion resulted in a change of sign Change-Id: I149e0540c00745fe8119fffd1463d31acd727690 Reviewed-by: Simon Hausmann --- src/qml/jit/qv4assembler_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 748afbfba4..669b0b2ff4 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -105,7 +105,7 @@ struct LookupCall { struct RuntimeCall { JSC::MacroAssembler::Address addr; - inline RuntimeCall(uint offset = INT_MIN); + inline RuntimeCall(uint offset = uint(INT_MIN)); bool isValid() const { return addr.offset >= 0; } }; -- cgit v1.2.3 From 8a8b826cad197cf39e50f88521d0c40dc9c9344d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 22 Jul 2016 22:03:25 -0700 Subject: ICC: suppress the warning about -fno-delete-null-pointer-checks Recent qtbase updates set QT_GCC_MAJOR_VERSION even for ICC. Change-Id: I149e0540c00745fe8119fffd1463d34701f370f3 Reviewed-by: Simon Hausmann --- src/qml/qml.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml.pro b/src/qml/qml.pro index d9452a6257..cc5023f12a 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -22,7 +22,7 @@ exists("qqml_enable_gcov") { LIBS_PRIVATE += -lgcov } -greaterThan(QT_GCC_MAJOR_VERSION, 5) { +gcc:!intel_icc:greaterThan(QT_GCC_MAJOR_VERSION, 5) { # Our code is bad. Temporary workaround. QMAKE_CXXFLAGS += -fno-delete-null-pointer-checks } -- cgit v1.2.3 From 4c1a51006e5936dc69e3373539787120092f6719 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 21 Jul 2016 16:30:57 +0200 Subject: Improve robustness of qml disk caching Perform various basic checks before proceeding to load an existing cache file, including the qt version, architecture, data structure version and others. Change-Id: Ie822b056e944ac120643aad260e97f62616688bf Reviewed-by: Erik Verbruggen --- src/qml/compiler/qv4compileddata.cpp | 55 ++++++++++- src/qml/compiler/qv4compileddata_p.h | 15 ++- src/qml/compiler/qv4compiler.cpp | 10 +- src/qml/compiler/qv4compiler_p.h | 3 +- src/qml/compiler/qv4instr_moth_p.h | 2 + src/qml/compiler/qv4isel_moth.cpp | 4 +- src/qml/compiler/qv4isel_moth_p.h | 9 +- src/qml/compiler/qv4isel_p.cpp | 3 +- src/qml/compiler/qv4isel_p.h | 6 +- src/qml/jit/qv4isel_masm.cpp | 4 +- src/qml/jit/qv4isel_masm_p.h | 11 ++- src/qml/qml/qqmltypeloader.cpp | 4 +- tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 121 +++++++++++++++++++++-- 13 files changed, 210 insertions(+), 37 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 1bcd6892d2..965924262d 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -331,20 +331,56 @@ bool CompilationUnit::saveToDisk(QString *errorString) return true; } -bool CompilationUnit::loadFromDisk(const QUrl &url, QString *errorString) +bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) { if (!url.isLocalFile()) { *errorString = QStringLiteral("File has to be a local file."); return false; } - QScopedPointer cacheFile(new QFile(url.toLocalFile() + QLatin1Char('c'))); + const QString sourcePath = url.toLocalFile(); + QScopedPointer cacheFile(new QFile(sourcePath + QLatin1Char('c'))); if (!cacheFile->open(QIODevice::ReadOnly)) { *errorString = cacheFile->errorString(); return false; } + { + CompiledData::Unit header; + qint64 bytesRead = cacheFile->read(reinterpret_cast(&header), sizeof(header)); + + if (bytesRead != sizeof(header)) { + *errorString = QStringLiteral("File too small for the header fields"); + return false; + } + + if (strncmp(header.magic, CompiledData::magic_str, sizeof(header.magic))) { + *errorString = QStringLiteral("Magic bytes in the header do not match"); + return false; + } + + if (header.version != quint32(QV4_DATA_STRUCTURE_VERSION)) { + *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header.version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); + return false; + } + + if (header.qtVersion != quint32(QT_VERSION)) { + *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header.qtVersion, 0, 16).arg(QT_VERSION, 0, 16); + return false; + } + + { + QFileInfo sourceCode(sourcePath); + if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header.sourceTimeStamp) { + *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + return false; + } + } + + } + // Data structure and qt version matched, so now we can access the rest of the file safely. + uchar *cacheData = cacheFile->map(/*offset*/0, cacheFile->size()); if (!cacheData) { *errorString = cacheFile->errorString(); @@ -354,13 +390,22 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, QString *errorString) QScopedValueRollback dataPtrChange(data, reinterpret_cast(cacheData)); { - QFileInfo sourceCode(url.toLocalFile()); - if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != data->sourceTimeStamp) { - *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + const QString foundArchitecture = stringAt(data->architectureIndex); + const QString expectedArchitecture = QSysInfo::buildAbi(); + if (foundArchitecture != expectedArchitecture) { + *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture); return false; } } + { + const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); + const QString expectedCodeGenerator = iselFactory->codeGeneratorName; + if (foundCodeGenerator != expectedCodeGenerator) { + *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); + return false; + } + } if (!memoryMapCode(errorString)) return false; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 20b68026e9..4153259760 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -70,6 +70,9 @@ QT_BEGIN_NAMESPACE +// Bump this whenever the compiler data structures change in an incompatible way. +#define QV4_DATA_STRUCTURE_VERSION 0x01 + class QIODevice; class QQmlPropertyCache; class QQmlPropertyData; @@ -88,6 +91,7 @@ struct Function; } struct Function; +class EvalISelFactory; namespace CompiledData { @@ -597,11 +601,16 @@ static const char magic_str[] = "qv4cdata"; struct Unit { + // DO NOT CHANGE THESE FIELDS EVER char magic[8]; - LEInt16 architecture; - LEInt16 version; + LEUInt32 version; + LEUInt32 qtVersion; LEInt64 sourceTimeStamp; LEUInt32 unitSize; // Size of the Unit and any depending data. + // END DO NOT CHANGE THESE FIELDS EVER + + LEUInt32 architectureIndex; // string index to QSysInfo::buildAbi() + LEUInt32 codeGeneratorIndex; enum : unsigned int { IsJavascript = 0x1, @@ -880,7 +889,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount void destroy() Q_DECL_OVERRIDE; bool saveToDisk(QString *errorString); - bool loadFromDisk(const QUrl &url, QString *errorString); + bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString); protected: virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 5d13734247..768a4ffcd3 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -352,17 +352,19 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i } } -QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian *functionOffsets, uint *jsClassDataOffset) const +QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian *functionOffsets, uint *jsClassDataOffset) { CompiledData::Unit unit; memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic)); - unit.architecture = 0; // ### unit.flags = QV4::CompiledData::Unit::IsJavascript; - unit.version = 1; - unit.functionTableSize = irModule->functions.size(); + unit.version = QV4_DATA_STRUCTURE_VERSION; + unit.qtVersion = QT_VERSION; + unit.architectureIndex = registerString(QSysInfo::buildAbi()); + unit.codeGeneratorIndex = registerString(codeGeneratorName); quint32 nextOffset = sizeof(CompiledData::Unit); + unit.functionTableSize = irModule->functions.size(); unit.offsetToFunctionTable = nextOffset; nextOffset += unit.functionTableSize * sizeof(uint); diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 4b67fe8600..49b8664513 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -118,8 +118,9 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { void writeFunction(char *f, IR::Function *irFunction) const; StringTableGenerator stringTable; + QString codeGeneratorName; private: - CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian *functionOffsets, uint *jsClassDataOffset) const; + CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian *functionOffsets, uint *jsClassDataOffset); IR::Module *irModule; diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index e95a7f7046..112003ebb8 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -174,6 +174,8 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace Moth { + // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h + struct Param { // Params are looked up as follows: // Constant: 0 diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 283948727b..dc803f1ee2 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -313,8 +313,8 @@ protected: }; } // anonymous namespace -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - : EvalInstructionSelection(execAllocator, module, jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) + : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) , qmlEngine(qmlEngine) , _block(0) , _codeStart(0) diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index eaca299c94..ea323497ef 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -80,7 +80,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); ~InstructionSelection(); virtual void run(int functionIndex); @@ -205,10 +205,11 @@ private: class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: + ISelFactory() : EvalISelFactory(QStringLiteral("moth")) {} virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } - virtual bool jitCompileRegexps() const + EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL + { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } + bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL { return false; } QQmlRefPointer createUnitForLoading() Q_DECL_OVERRIDE; diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 0ae08160ab..d97eec5e1d 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -52,7 +52,7 @@ using namespace QV4; using namespace QV4::IR; -EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) +EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) : useFastLookups(true) , useTypeInference(true) , executableAllocator(execAllocator) @@ -67,6 +67,7 @@ EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *exe Q_ASSERT(execAllocator); #endif Q_ASSERT(module); + jsGenerator->codeGeneratorName = iselFactory->codeGeneratorName; } EvalInstructionSelection::~EvalInstructionSelection() diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index d93c0893ae..a3fa80b6f0 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -65,13 +65,14 @@ class QQmlEnginePrivate; namespace QV4 { +class EvalISelFactory; class ExecutableAllocator; struct Function; class Q_QML_PRIVATE_EXPORT EvalInstructionSelection { public: - EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); virtual ~EvalInstructionSelection() = 0; QQmlRefPointer compile(bool generateUnitData = true); @@ -104,10 +105,13 @@ protected: class Q_QML_PRIVATE_EXPORT EvalISelFactory { public: + EvalISelFactory(const QString &codeGeneratorName) : codeGeneratorName(codeGeneratorName) {} virtual ~EvalISelFactory() = 0; virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0; virtual bool jitCompileRegexps() const = 0; virtual QQmlRefPointer createUnitForLoading() = 0; + + const QString codeGeneratorName; }; namespace IR { diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index bde2c59526..4066ab213c 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -258,8 +258,8 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) return codeRef; } -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator) - : EvalInstructionSelection(execAllocator, module, jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) + : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) , _block(0) , _as(0) , compilationUnit(new CompilationUnit) diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 7616ba147f..4b35a72e01 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -76,7 +76,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); ~InstructionSelection(); virtual void run(int functionIndex); @@ -285,12 +285,13 @@ private: class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: + ISelFactory() : EvalISelFactory(QStringLiteral("jit")) {} virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } - virtual bool jitCompileRegexps() const + EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL + { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } + bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL { return true; } - QQmlRefPointer createUnitForLoading() Q_DECL_OVERRIDE; + QQmlRefPointer createUnitForLoading() Q_DECL_OVERRIDE Q_DECL_FINAL; }; } // end of namespace JIT diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 52fed14ecb..851a18fa5f 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2057,7 +2057,7 @@ bool QQmlTypeData::tryLoadFromDiskCache() QQmlRefPointer unit = v4->iselFactory->createUnitForLoading(); { QString error; - if (!unit->loadFromDisk(url(), &error)) { + if (!unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { qDebug() << "Error loading" << url().toString() << "from disk cache:" << error; return false; } @@ -2840,7 +2840,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) if (diskCache() && !forceDiskCacheRefresh()) { QQmlRefPointer unit = v4->iselFactory->createUnitForLoading(); QString error; - if (unit->loadFromDisk(url(), &error)) { + if (unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { initializeFromCompilationUnit(unit); return; } else { diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index bb78f2856f..1fe63bb99a 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -29,6 +29,11 @@ #include #include +#include +#include +#include +#include +#include #include #include #include @@ -42,6 +47,7 @@ private slots: void regenerateAfterChange(); void registerImportForImplicitComponent(); + void basicVersionChecks(); }; struct TestCompiler @@ -58,11 +64,8 @@ struct TestCompiler bool compile(const QByteArray &contents) { - if (currentMapping) { - mappedFile.unmap(currentMapping); - currentMapping = nullptr; - } - mappedFile.close(); + closeMapping(); + engine->clearComponentCache(); // Qt API limits the precision of QFileInfo::modificationTime() to seconds, so to ensure that // the newly written file has a modification date newer than an existing cache file, we must @@ -106,6 +109,44 @@ struct TestCompiler return unitPtr; } + typedef void (*HeaderTweakFunction)(QV4::CompiledData::Unit *header); + bool tweakHeader(HeaderTweakFunction function) + { + closeMapping(); + + QFile f(cacheFilePath); + if (!f.open(QIODevice::ReadWrite)) + return false; + QV4::CompiledData::Unit header; + if (f.read(reinterpret_cast(&header), sizeof(header)) != sizeof(header)) + return false; + function(&header); + f.seek(0); + return f.write(reinterpret_cast(&header), sizeof(header)) == sizeof(header); + } + + bool verify() + { + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + QQmlRefPointer unit = v4->iselFactory->createUnitForLoading(); + return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), v4->iselFactory.data(), &lastErrorString); + } + + void closeMapping() + { + if (currentMapping) { + mappedFile.unmap(currentMapping); + currentMapping = nullptr; + } + mappedFile.close(); + } + + void clearCache() + { + closeMapping(); + QFile::remove(cacheFilePath); + } + QQmlEngine *engine; const QTemporaryDir tempDir; const QString testFilePath; @@ -151,8 +192,6 @@ void tst_qmldiskcache::regenerateAfterChange() QVERIFY(bindingFunction->codeOffset > testUnit->unitSize); } - engine.clearComponentCache(); - { const QByteArray newContents = QByteArrayLiteral("import QtQml 2.0\n" "QtObject {\n" @@ -214,6 +253,74 @@ void tst_qmldiskcache::registerImportForImplicitComponent() } } +void tst_qmldiskcache::basicVersionChecks() +{ + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n" + "QtObject {\n" + " property string blah: Qt.platform;\n" + "}"); + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString)); + } + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) { + header->qtVersion = 0; + }); + + QVERIFY(!testCompiler.verify()); + QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Qt version mismatch. Found 0 expected %1").arg(QT_VERSION, 0, 16)); + testCompiler.clearCache(); + } + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) { + header->version = 0; + }); + + QVERIFY(!testCompiler.verify()); + QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("V4 data structure version mismatch. Found 0 expected %1").arg(QV4_DATA_STRUCTURE_VERSION, 0, 16)); + } + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) { + header->architectureIndex = 0; + }); + + QVERIFY(!testCompiler.verify()); + QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Architecture mismatch. Found expected %1").arg(QSysInfo::buildAbi())); + } + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) { + header->codeGeneratorIndex = 0; + }); + + QVERIFY(!testCompiler.verify()); + QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Code generator mismatch. Found code generated by but expected %1").arg(QV8Engine::getV4(&engine)->iselFactory->codeGeneratorName)); + } +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" -- cgit v1.2.3 From 1943ef3b06d3f95a4e6c4d24f771cee8db02d342 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 22 Jul 2016 19:38:10 -0700 Subject: Work around ICC bug about C++11 noexcept inheritance qquickitemanimation.cpp(213): error #809: exception specification for virtual function "QQuickParentAnimationData::~QQuickParentAnimationData" is incompatible with that of overridden function "QAbstractAnimationAction::~QAbstractAnimationAction" Change-Id: I149e0540c00745fe8119fffd1463cb59e590b6b8 Reviewed-by: Simon Hausmann --- src/quick/items/qquickitemanimation.cpp | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp index 9d140b3156..6b48ca5bf9 100644 --- a/src/quick/items/qquickitemanimation.cpp +++ b/src/quick/items/qquickitemanimation.cpp @@ -194,6 +194,27 @@ QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::Transfo } } +struct QQuickParentAnimationData : public QAbstractAnimationAction +{ + QQuickParentAnimationData() : reverse(false) {} + ~QQuickParentAnimationData() { qDeleteAll(pc); } + + QQuickStateActions actions; + //### reverse should probably apply on a per-action basis + bool reverse; + QList pc; + void doAction() Q_DECL_OVERRIDE + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QQuickStateAction &action = actions.at(ii); + if (reverse) + action.event->reverse(); + else + action.event->execute(); + } + } +}; + QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &actions, QQmlProperties &modified, TransitionDirection direction, @@ -201,27 +222,6 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act { Q_D(QQuickParentAnimation); - struct QQuickParentAnimationData : public QAbstractAnimationAction - { - QQuickParentAnimationData() : reverse(false) {} - ~QQuickParentAnimationData() { qDeleteAll(pc); } - - QQuickStateActions actions; - //### reverse should probably apply on a per-action basis - bool reverse; - QList pc; - void doAction() Q_DECL_OVERRIDE - { - for (int ii = 0; ii < actions.count(); ++ii) { - const QQuickStateAction &action = actions.at(ii); - if (reverse) - action.event->reverse(); - else - action.event->execute(); - } - } - }; - QQuickParentAnimationData *data = new QQuickParentAnimationData; QQuickParentAnimationData *viaData = new QQuickParentAnimationData; -- cgit v1.2.3 From 7436638b51ef7b121afd5ea8135e4a3ce5913fa4 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Wed, 20 Jul 2016 16:42:40 +0200 Subject: Use Q_GLOBAL_STATIC instead of static members of QWindowPrivate Having the members static in QWindowPrivate only gives the benefit of tidyness, but there's still the problem of initialization order and thread-safety. Q_GLOBAL_STATIC solves those issues for us. Change-Id: I8e1279959d0bb2b16fd720cb7f4e9afb6eda6355 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 59 ++++++++++++++++++++++++++++++++++++-- src/quick/items/qquickevents_p_p.h | 4 +++ src/quick/items/qquickwindow.cpp | 41 ++------------------------ src/quick/items/qquickwindow_p.h | 5 ---- 4 files changed, 63 insertions(+), 46 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index e4a080d2b7..80a4e6bd9a 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -441,6 +441,61 @@ Item { \l inverted always returns false. */ +typedef QHash PointerDeviceForTouchDeviceHash; +Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices) + +Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice, + (QQuickPointerDevice::Mouse, + QQuickPointerDevice::GenericPointer, + QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover, + 1, 3, QLatin1String("core pointer"), 0)) + +typedef QHash PointerDeviceForDeviceIdHash; +Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices) + +QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d) +{ + if (g_touchDevices->contains(d)) + return g_touchDevices->value(d); + + QQuickPointerDevice::DeviceType type = QQuickPointerDevice::TouchScreen; + QString name; + int maximumTouchPoints = 10; + QQuickPointerDevice::Capabilities caps = QQuickPointerDevice::Capabilities(QTouchDevice::Position); + if (d) { + QQuickPointerDevice::Capabilities caps = + static_cast(static_cast(d->capabilities()) & 0x0F); + if (d->type() == QTouchDevice::TouchPad) { + type = QQuickPointerDevice::TouchPad; + caps |= QQuickPointerDevice::Scroll; + } + name = d->name(); + maximumTouchPoints = d->maximumTouchPoints(); + } else { + qWarning() << "QQuickWindowPrivate::touchDevice: creating touch device from nullptr device in QTouchEvent"; + } + + QQuickPointerDevice *dev = new QQuickPointerDevice(type, QQuickPointerDevice::Finger, + caps, maximumTouchPoints, 0, name, 0); + g_touchDevices->insert(d, dev); + return dev; +} + +QQuickPointerDevice *QQuickPointerDevice::genericMouseDevice() +{ + return g_genericMouseDevice; +} + +QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) +{ + auto it = g_tabletDevices->find(id); + if (it != g_tabletDevices->end()) + return it.value(); + + // ### Figure out how to populate the tablet devices + return nullptr; +} + QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) @@ -465,7 +520,7 @@ QQuickPointerEvent::~QQuickPointerEvent() QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) { auto ev = static_cast(event); - m_device = QQuickWindowPrivate::genericMouseDevice; + m_device = QQuickPointerDevice::genericMouseDevice(); m_event = ev; m_button = ev->button(); m_pressedButtons = ev->buttons(); @@ -490,7 +545,7 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) { QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) { auto ev = static_cast(event); - m_device = QQuickWindowPrivate::touchDevice(ev->device()); + m_device = QQuickPointerDevice::touchDevice(ev->device()); m_event = ev; m_button = Qt::NoButton; m_pressedButtons = Qt::NoButton; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 9e37955914..f2e06b69df 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -499,6 +499,10 @@ public: qint64 uniqueId() const { return m_uniqueId; } QQuickPointerEvent *pointerEvent() const { return m_event; } + static QQuickPointerDevice *touchDevice(QTouchDevice *d); + static QQuickPointerDevice *genericMouseDevice(); + static QQuickPointerDevice *tabletDevice(qint64); + private: DeviceType m_deviceType; PointerType m_pointerType; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 0c88c511eb..73b04f825a 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -93,10 +93,6 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_ bool QQuickWindowPrivate::defaultAlphaBuffer = false; -QQuickPointerDevice *QQuickWindowPrivate::genericMouseDevice(nullptr); -QHash QQuickWindowPrivate::touchDevices; -QHash QQuickWindowPrivate::tabletDevices; - void QQuickWindowPrivate::updateFocusItemTransform() { #ifndef QT_NO_IM @@ -507,11 +503,6 @@ QQuickWindowPrivate::QQuickWindowPrivate() #ifndef QT_NO_DRAGANDDROP dragGrabber = new QQuickDragGrabber; #endif - if (!genericMouseDevice) { - genericMouseDevice = new QQuickPointerDevice(QQuickPointerDevice::Mouse, QQuickPointerDevice::GenericPointer, - QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover, - 1, 3, QStringLiteral("core pointer"), 0); - } } QQuickWindowPrivate::~QQuickWindowPrivate() @@ -1874,34 +1865,6 @@ bool QQuickWindowPrivate::deliverNativeGestureEvent(QQuickItem *item, QNativeGes } #endif // QT_NO_GESTURES -QQuickPointerDevice *QQuickWindowPrivate::touchDevice(QTouchDevice *d) -{ - if (touchDevices.contains(d)) - return touchDevices.value(d); - - QQuickPointerDevice::DeviceType type = QQuickPointerDevice::TouchScreen; - QString name; - int maximumTouchPoints = 10; - QQuickPointerDevice::Capabilities caps = QQuickPointerDevice::Capabilities(QTouchDevice::Position); - if (d) { - QQuickPointerDevice::Capabilities caps = - static_cast(static_cast(d->capabilities()) & 0x0F); - if (d->type() == QTouchDevice::TouchPad) { - type = QQuickPointerDevice::TouchPad; - caps |= QQuickPointerDevice::Scroll; - } - name = d->name(); - maximumTouchPoints = d->maximumTouchPoints(); - } else { - qWarning() << "QQuickWindowPrivate::touchDevice: creating touch device from nullptr device in QTouchEvent"; - } - - QQuickPointerDevice *dev = new QQuickPointerDevice(type, QQuickPointerDevice::Finger, - caps, maximumTouchPoints, 0, name, 0); - touchDevices.insert(d, dev); - return dev; -} - bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) { qCDebug(DBG_TOUCH) << event; @@ -2151,13 +2114,13 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: - dev = genericMouseDevice; + dev = QQuickPointerDevice::genericMouseDevice(); break; case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: case QEvent::TouchCancel: - dev = touchDevice(static_cast(event)->device()); + dev = QQuickPointerDevice::touchDevice(static_cast(event)->device()); break; // TODO tablet event types default: diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 7e66aef9f4..15ac701c54 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -154,7 +154,6 @@ public: #ifndef QT_NO_GESTURES bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *); #endif - static QQuickPointerDevice *touchDevice(QTouchDevice *d); // entry point of events to the window void handleTouchEvent(QTouchEvent *); @@ -274,10 +273,6 @@ public: mutable QQuickWindowIncubationController *incubationController; - static QQuickPointerDevice *genericMouseDevice; - static QHash touchDevices; - static QHash tabletDevices; - static bool defaultAlphaBuffer; static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1); -- cgit v1.2.3 From ba3fb9f8ee4ba735182713272c3abdc01d1de763 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 21 Jul 2016 22:19:17 +0200 Subject: Removing mouse grab must not cancel touch grab When calling setMouseGrab(nullptr) we would ungrab the touch point that corresponds to the touchMouseId. This is why the code could not call setMouseGrab(nullptr) before. Remove the wrong removal of the item from itemForTouchPointId. This was not a problem so far because every part of the code that cares about touch would do special handling and not call this function, but access QQuickWindow's internals instead. Change-Id: I032065d5b6db8cd85c78a022a168dbd220440f5f Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 73b04f825a..7a524a1377 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -753,13 +753,10 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) QQuickItem *oldGrabber = q->mouseGrabberItem(); mouseGrabberItem = grabber; - if (touchMouseId != -1) { + if (grabber && touchMouseId != -1) { // update the touch item for mouse touch id to the new grabber - itemForTouchPointId.remove(touchMouseId); - if (grabber) { - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); - itemForTouchPointId[touchMouseId] = grabber; - } + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); + itemForTouchPointId[touchMouseId] = grabber; } if (oldGrabber) { -- cgit v1.2.3 From 16e299fbb106f7d2de954afb1b921a80f4cd3e67 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 15 Jul 2016 15:15:29 +0200 Subject: Move mouseGrabberItem into the QQuickPointerEvent The idea is to manage the "grab" for mouse and touch points inside the pointer event, instead of having awkward extra state in the window. Change-Id: I4011c66c163159b0315bf8e284d8e1c7c460f108 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 2 +- src/quick/items/qquickevents_p_p.h | 4 ++++ src/quick/items/qquickwindow.cpp | 22 ++++++++++------------ src/quick/items/qquickwindow_p.h | 1 - 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 80a4e6bd9a..96eb59f518 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -574,7 +574,7 @@ QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { } QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent) - : QObject(parent), m_pointId(0), m_timestamp(0), m_pressTimestamp(0), + : QObject(parent), m_pointId(0), m_grabber(nullptr), m_timestamp(0), m_pressTimestamp(0), m_state(Qt::TouchPointReleased), m_valid(false), m_accept(false) { Q_UNUSED(m_reserved); diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index f2e06b69df..3cec7c782a 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -253,6 +253,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject Q_PROPERTY(quint64 pointId READ pointId) Q_PROPERTY(qreal timeHeld READ timeHeld) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + Q_PROPERTY(QQuickItem *grabber READ grabber WRITE setGrabber) public: QQuickEventPoint(QQuickPointerEvent *parent); @@ -280,10 +281,13 @@ public: qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } bool isAccepted() const { return m_accept; } void setAccepted(bool accepted = true) { m_accept = accepted; } + QQuickItem *grabber() const { return m_grabber; } + void setGrabber(QQuickItem *grabber) { m_grabber = grabber; } private: QPointF m_scenePos; quint64 m_pointId; + QQuickItem *m_grabber; ulong m_timestamp; ulong m_pressTimestamp; Qt::TouchPointState m_state; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 7a524a1377..a8565be414 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -471,7 +471,6 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size) QQuickWindowPrivate::QQuickWindowPrivate() : contentItem(0) , activeFocusItem(0) - , mouseGrabberItem(0) #ifndef QT_NO_CURSOR , cursorItem(0) #endif @@ -751,7 +750,10 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber; QQuickItem *oldGrabber = q->mouseGrabberItem(); - mouseGrabberItem = grabber; + + QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); + Q_ASSERT(event->pointCount() == 1); + event->point(0)->setGrabber(grabber); if (grabber && touchMouseId != -1) { // update the touch item for mouse touch id to the new grabber @@ -778,12 +780,10 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVectormouseGrabberItem(); if (touchMouseId == ids.at(i) && mouseGrabberItem && mouseGrabberItem != grabber) { qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: grabber" << mouseGrabberItem << "-> null"; - mouseGrabberItem = 0; - QEvent ev(QEvent::UngrabMouse); - q->sendEvent(originalMouseGrabberItem, &ev); + setMouseGrabber(nullptr); } } foreach (QQuickItem *oldGrabber, ungrab) @@ -804,9 +804,7 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to } if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) { qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << q->mouseGrabberItem() << "-> null"; - mouseGrabberItem = 0; - QEvent ev(QEvent::UngrabMouse); - q->sendEvent(grabber, &ev); + setMouseGrabber(nullptr); } } @@ -1481,9 +1479,9 @@ QObject *QQuickWindow::focusObject() const */ QQuickItem *QQuickWindow::mouseGrabberItem() const { - Q_D(const QQuickWindow); - - return d->mouseGrabberItem; + QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); + Q_ASSERT(event->pointCount()); + return event->point(0)->grabber(); } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 15ac701c54..0579295fca 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -125,7 +125,6 @@ public: void deliverKeyEvent(QKeyEvent *e); // Keeps track of the item currently receiving mouse events - QQuickItem *mouseGrabberItem; #ifndef QT_NO_CURSOR QQuickItem *cursorItem; #endif -- cgit v1.2.3 From aa48ecd09b56cee3fb34d48b70c6138a1d675f2a Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 19 Jul 2016 18:44:09 +0200 Subject: Add QQuickPointerEvent::grabbers Returns a list of all items that grabbed a point of the event before. Change-Id: Ifa7e6cc7486c4e1a7446a6bf3d4e62d19983ecf7 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 20 ++++++++++++++++++++ src/quick/items/qquickevents_p_p.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 96eb59f518..ea1a6e263e 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -594,6 +594,14 @@ QMouseEvent *QQuickPointerMouseEvent::asMouseEvent() const return static_cast(m_event); } +QVector QQuickPointerMouseEvent::grabbers() const +{ + QVector result; + if (QQuickItem *grabber = m_mousePoint->grabber()) + result << grabber; + return result; +} + bool QQuickPointerTouchEvent::allPointsAccepted() const { for (int i = 0; i < m_pointCount; ++i) { if (!m_touchPoints.at(i)->isAccepted()) @@ -602,6 +610,18 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const { return true; } +QVector QQuickPointerTouchEvent::grabbers() const +{ + QVector result; + for (auto point : qAsConst(m_touchPoints)) { + if (QQuickItem *grabber = point->grabber()) { + if (!result.contains(grabber)) + result << grabber; + } + } + return result; +} + /*! \internal Populate the reusable synth-mouse event from one touchpoint. diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 3cec7c782a..59b211e43e 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -364,6 +364,7 @@ public: // helpers for C++ only (during event delivery) virtual int pointCount() const = 0; virtual QQuickEventPoint *point(int i) const = 0; virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; + virtual QVector grabbers() const = 0; protected: @@ -391,6 +392,7 @@ public: QQuickEventPoint *point(int i) const override; QQuickEventPoint *pointById(quint64 pointId) const override; bool allPointsAccepted() const override; + QVector grabbers() const override; QMouseEvent *asMouseEvent() const; @@ -413,6 +415,7 @@ public: QQuickEventPoint *pointById(quint64 pointId) const override; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; bool allPointsAccepted() const override; + QVector grabbers() const override; QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; QTouchEvent *touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const; -- cgit v1.2.3 From f32d0f724ff2010bfb69f8b1772a62b5a2141823 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 22 Jul 2016 21:06:32 -0700 Subject: Fix build on macOS with ICC: -sectcreate is a linker option Apparently Clang understands it and passes to the linker. ICC doesn't, so we have to use the -Wl option. Change-Id: Ibad13c8c3c8d7596aca965c4f6e96c1e82b3cef5 Reviewed-by: Jake Petroules --- tools/qmlplugindump/qmlplugindump.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/qmlplugindump/qmlplugindump.pro b/tools/qmlplugindump/qmlplugindump.pro index e45a7fad83..b38eea2554 100644 --- a/tools/qmlplugindump/qmlplugindump.pro +++ b/tools/qmlplugindump/qmlplugindump.pro @@ -17,7 +17,7 @@ macx { # Prevent qmlplugindump from popping up in the dock when launched. # We embed the Info.plist file, so the application doesn't need to # be a bundle. - QMAKE_LFLAGS += -sectcreate __TEXT __info_plist $$shell_quote($$PWD/Info.plist) + QMAKE_LFLAGS += -Wl,-sectcreate,__TEXT,__info_plist,$$shell_quote($$PWD/Info.plist) CONFIG -= app_bundle } -- cgit v1.2.3 From 007ae316a62670eeff8b08526fad9110fff0bbd4 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 14 Jul 2016 12:09:23 +0200 Subject: QML: Unify property reads/writes and use accessors Pass property reads/writes through utility functions in QQmlProperty, which in turn will try to use accessors when available (and no interceptors have to be called). Change-Id: I60ecfc202b6024bfe4a33206a46299787b152546 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4sequenceobject.cpp | 2 +- src/qml/qml/qqmlabstractbinding_p.h | 2 +- src/qml/qml/qqmlbinding.cpp | 26 ++-- src/qml/qml/qqmlbinding_p.h | 8 +- src/qml/qml/qqmlengine.cpp | 4 +- src/qml/qml/qqmlobjectcreator.cpp | 136 ++++++--------------- src/qml/qml/qqmlproperty.cpp | 51 +++----- src/qml/qml/qqmlproperty_p.h | 18 +-- src/qml/qml/qqmlpropertycache_p.h | 37 ++++++ src/qml/qml/qqmlvaluetype.cpp | 2 +- src/qml/qml/qqmlvaluetype_p.h | 2 +- src/qml/qml/qqmlvaluetypeproxybinding.cpp | 2 +- src/qml/qml/qqmlvaluetypeproxybinding_p.h | 2 +- src/qml/qml/qqmlvmemetaobject.cpp | 6 +- .../designer/qquickdesignercustomobjectdata.cpp | 4 +- src/quick/util/qquickanimation.cpp | 6 +- src/quick/util/qquickbehavior.cpp | 6 +- src/quick/util/qquickpropertychanges.cpp | 12 +- src/quick/util/qquicksmoothedanimation.cpp | 12 +- src/quick/util/qquickspringanimation.cpp | 4 +- src/quick/util/qquicktransitionmanager.cpp | 6 +- tests/auto/qml/qqmlvaluetypes/testtypes.h | 6 +- 22 files changed, 155 insertions(+), 199 deletions(-) diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index d3b021ac37..b15ac7eb5e 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -531,7 +531,7 @@ public: Q_ASSERT(d()->object); Q_ASSERT(d()->isReference); int status = -1; - QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding; + QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding; void *a[] = { &d()->container, 0, &status, &flags }; QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a); } diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index d25c0c6288..0ccfae4610 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -82,7 +82,7 @@ public: // binding is not enabled or added to the object. QObject *targetObject() const { return m_target.data(); } - virtual void setEnabled(bool e, QQmlPropertyPrivate::WriteFlags f = QQmlPropertyPrivate::DontRemoveBinding) = 0; + virtual void setEnabled(bool e, QQmlPropertyData::WriteFlags f = QQmlPropertyData::DontRemoveBinding) = 0; void addToObject(); void removeFromObject(); diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 9737dfdd6b..10d16a8a12 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -157,7 +157,7 @@ void QQmlBinding::setNotifyOnValueChanged(bool v) QQmlJavaScriptExpression::setNotifyOnValueChanged(v); } -void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) +void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) { if (!enabledFlag() || !context() || !context()->isValid()) return; @@ -181,6 +181,9 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) QV4::ScopedFunctionObject f(scope, m_function.value()); Q_ASSERT(f); + if (canUseAccessor()) + flags.setFlag(QQmlPropertyData::BypassInterceptor); + QQmlBindingProfiler prof(ep->profiler, this, f); doUpdate(this, watcher, flags, scope, f); @@ -197,7 +200,7 @@ class QQmlBindingBinding: public QQmlBinding { protected: void doUpdate(QQmlBinding *binding, const DeleteWatcher &, - QQmlPropertyPrivate::WriteFlags flags, QV4::Scope &, + QQmlPropertyData::WriteFlags flags, QV4::Scope &, const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL { QQmlPropertyData pd = getPropertyData(); @@ -216,7 +219,7 @@ class GenericBinding: public QQmlBinding { protected: void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, - QQmlPropertyPrivate::WriteFlags flags, QV4::Scope &scope, + QQmlPropertyData::WriteFlags flags, QV4::Scope &scope, const QV4::ScopedFunctionObject &f) Q_DECL_OVERRIDE Q_DECL_FINAL { auto ep = QQmlEnginePrivate::get(scope.engine); @@ -251,7 +254,7 @@ protected: // Returns true if successful, false if an error description was set on expression Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, - QQmlPropertyPrivate::WriteFlags flags) + QQmlPropertyData::WriteFlags flags) { QQmlPropertyData pd = getPropertyData(); int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded. @@ -298,22 +301,15 @@ protected: } template - Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData &pd, QQmlPropertyPrivate::WriteFlags flags) const + Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData &pd, QQmlPropertyData::WriteFlags flags) const { void *o = &value; - if (pd.hasAccessors() && canUseAccessor()) { - pd.accessors->write(m_target.data(), o); - } else { - int status = -1; - void *argv[] = { o, 0, &status, &flags }; - QMetaObject::metacall(targetObject(), QMetaObject::WriteProperty, pd.coreIndex, argv); - } - return true; + return pd.writeProperty(targetObject(), o, flags); } }; Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const QV4::Value &result, - bool isUndefined, QQmlPropertyPrivate::WriteFlags flags) + bool isUndefined, QQmlPropertyData::WriteFlags flags) { QQmlEngine *engine = context()->engine; QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); @@ -458,7 +454,7 @@ void QQmlBinding::refresh() update(); } -void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) +void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) { setEnabledFlag(e); setNotifyOnValueChanged(e); diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index a7c90603f6..0c797d7df2 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -87,9 +87,9 @@ public: void refresh() Q_DECL_OVERRIDE; - void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding) Q_DECL_OVERRIDE; + void setEnabled(bool, QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding) Q_DECL_OVERRIDE; QString expression() const Q_DECL_OVERRIDE; - void update(QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding); + void update(QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding); typedef int Identifier; enum { @@ -103,7 +103,7 @@ public: protected: virtual void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, - QQmlPropertyPrivate::WriteFlags flags, QV4::Scope &scope, + QQmlPropertyData::WriteFlags flags, QV4::Scope &scope, const QV4::ScopedFunctionObject &f) = 0; QQmlPropertyData getPropertyData() const; @@ -111,7 +111,7 @@ protected: int getPropertyType() const; bool slowWrite(const QQmlPropertyData &core, const QV4::Value &result, bool isUndefined, - QQmlPropertyPrivate::WriteFlags flags); + QQmlPropertyData::WriteFlags flags); private: inline bool updatingFlag() const; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index d391f38b61..7dac164248 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -848,8 +848,8 @@ void QQmlData::flushPendingBindingImpl(int coreIndex) b = b->nextBinding(); if (b && b->targetPropertyIndex() == coreIndex) - b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::DontRemoveBinding); + b->setEnabled(true, QQmlPropertyData::BypassInterceptor | + QQmlPropertyData::DontRemoveBinding); } bool QQmlEnginePrivate::baseModulesUninitialized = true; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 7cb7047b04..87212ef713 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -275,11 +275,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance) void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) { - QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::RemoveBindingOnAliasWrite; - int propertyWriteStatus = -1; - void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags }; - + QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | QQmlPropertyData::RemoveBindingOnAliasWrite; QV4::Scope scope(v4); int propertyType = property->propType; @@ -307,16 +303,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } else { int i = int(n); QVariant value(i); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } } else { if (property->isVarProperty()) { _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromDouble(n)); } else { QVariant value(n); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } } } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { @@ -324,8 +318,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromBoolean(binding->valueAsBoolean())); } else { QVariant value(binding->valueAsBoolean()); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } } else { QString stringValue = binding->valueAsString(qmlUnit); @@ -334,8 +327,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const _vmeMetaObject->setVMEProperty(property->coreIndex, s); } else { QVariant value = QQmlStringConverters::variantFromString(stringValue); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } } } @@ -343,26 +335,19 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const case QVariant::String: { Q_ASSERT(binding->evaluatesToString()); QString value = binding->valueAsString(qmlUnit); - if (property->hasAccessors()) { - property->accessors->write(_qobject, &value); - } else { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::StringList: { Q_ASSERT(binding->evaluatesToString()); QStringList value(binding->valueAsString(qmlUnit)); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::ByteArray: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); QByteArray value(binding->valueAsString(qmlUnit).toUtf8()); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Url: { @@ -374,16 +359,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const // Apply URL interceptor if (engine->urlInterceptor()) value = engine->urlInterceptor()->intercept(value, QQmlAbstractUrlInterceptor::UrlString); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::UInt: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double d = binding->valueAsNumber(); uint value = uint(d); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } break; @@ -391,35 +374,20 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double d = binding->valueAsNumber(); int value = int(d); - if (property->hasAccessors()) { - property->accessors->write(_qobject, &value); - } else { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } break; case QMetaType::Float: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); float value = float(binding->valueAsNumber()); - if (property->hasAccessors()) { - property->accessors->write(_qobject, &value); - } else { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Double: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double value = binding->valueAsNumber(); - if (property->hasAccessors()) { - property->accessors->write(_qobject, &value); - } else { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Color: { @@ -428,8 +396,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const Q_ASSERT(ok); struct { void *data[4]; } buffer; if (QQml_valueTypeProvider()->storeValueType(property->propType, &colorValue, &buffer, sizeof(buffer))) { - argv[0] = reinterpret_cast(&buffer); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &buffer, propertyWriteFlags); } } break; @@ -438,16 +405,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const bool ok = false; QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Time: { bool ok = false; QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::DateTime: { @@ -460,8 +425,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const value = QDateTime(QDate::fromJulianDay(date), QTime::fromMSecsSinceStartOfDay(msecsSinceStartOfDay)); } Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; #endif // QT_NO_DATESTRING @@ -469,59 +433,48 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const bool ok = false; QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok).toPoint(); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::PointF: { bool ok = false; QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Size: { bool ok = false; QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok).toSize(); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::SizeF: { bool ok = false; QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Rect: { bool ok = false; QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok).toRect(); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::RectF: { bool ok = false; QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Bool: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean); bool value = binding->valueAsBoolean(); - if (property->hasAccessors()) { - property->accessors->write(_qobject, &value); - } else { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Vector3D: { @@ -533,8 +486,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec)); Q_ASSERT(ok); Q_UNUSED(ok); - argv[0] = reinterpret_cast(&vec); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &vec, propertyWriteFlags); } break; case QVariant::Vector4D: { @@ -547,8 +499,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec)); Q_ASSERT(ok); Q_UNUSED(ok); - argv[0] = reinterpret_cast(&vec); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &vec, propertyWriteFlags); } break; case QVariant::RegExp: @@ -560,23 +511,20 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); QList value; value.append(binding->valueAsNumber()); - argv[0] = reinterpret_cast(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType == qMetaTypeId >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double n = binding->valueAsNumber(); QList value; value.append(int(n)); - argv[0] = reinterpret_cast(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType == qMetaTypeId >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean); QList value; value.append(binding->valueAsBoolean()); - argv[0] = reinterpret_cast(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType == qMetaTypeId >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); @@ -584,15 +532,13 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QUrl u = urlString.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(urlString)); QList value; value.append(u); - argv[0] = reinterpret_cast(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType == qMetaTypeId >()) { Q_ASSERT(binding->evaluatesToString()); QList value; value.append(binding->valueAsString(qmlUnit)); - argv[0] = reinterpret_cast(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType == qMetaTypeId()) { QJSValue value; @@ -607,8 +553,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } else { value = QJSValue(binding->valueAsString(qmlUnit)); } - argv[0] = reinterpret_cast(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } @@ -624,8 +569,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; } - argv[0] = value.data(); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, value.data(), propertyWriteFlags); } break; } @@ -755,8 +699,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number; ss.d.data()->numberValue = binding->valueAsNumber(); - QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::RemoveBindingOnAliasWrite; + QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | + QQmlPropertyData::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; void *argv[] = { &ss, 0, &propertyWriteStatus, &propertyWriteFlags }; QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); @@ -808,7 +752,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; if (valueType) - valueType->write(_qobject, property->coreIndex, QQmlPropertyPrivate::BypassInterceptor); + valueType->write(_qobject, property->coreIndex, QQmlPropertyData::BypassInterceptor); return true; } @@ -934,8 +878,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return true; } - QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::RemoveBindingOnAliasWrite; + QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | + QQmlPropertyData::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags }; @@ -1213,8 +1157,8 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru QQmlData *data = QQmlData::get(b->targetObject()); Q_ASSERT(data); data->clearPendingBindingBit(b->targetPropertyIndex()); - b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::DontRemoveBinding); + b->setEnabled(true, QQmlPropertyData::BypassInterceptor | + QQmlPropertyData::DontRemoveBinding); if (watcher.hasRecursed() || interrupt.shouldInterrupt()) return 0; diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index e04b1114ad..64852f1cfe 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -42,6 +42,7 @@ #include "qqml.h" #include "qqmlbinding_p.h" +#include "qqmlboundsignal_p.h" #include "qqmlcontext.h" #include "qqmlcontext_p.h" #include "qqmlboundsignal_p.h" @@ -316,8 +317,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) if (!property->isQObject()) return; // Not an object property - void *args[] = { ¤tObject, 0 }; - QMetaObject::metacall(currentObject, QMetaObject::ReadProperty, property->coreIndex, args); + property->readProperty(currentObject, ¤tObject); if (!currentObject) return; // No value } @@ -858,7 +858,7 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, } -void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, WriteFlags writeFlags) +void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags) { Q_ASSERT(binding); @@ -1034,15 +1034,13 @@ QVariant QQmlPropertyPrivate::readValueProperty() } else if (core.isQList()) { QQmlListProperty prop; - void *args[] = { &prop, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); + core.readProperty(object, &prop); return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType, engine)); } else if (core.isQObject()) { QObject *rv = 0; - void *args[] = { &rv, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); + core.readProperty(object, &rv); return QVariant::fromValue(rv); } else { @@ -1059,7 +1057,7 @@ QVariant QQmlPropertyPrivate::readValueProperty() value = QVariant(core.propType, (void*)0); args[0] = value.data(); } - QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); + core.readPropertyWithArgs(object, args); if (core.propType != QMetaType::QVariant && args[0] != value.data()) return QVariant((QVariant::Type)core.propType, args[0]); @@ -1147,7 +1145,7 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, return status; } -bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, WriteFlags flags) +bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, QQmlPropertyData::WriteFlags flags) { return writeValueProperty(object, core, value, effectiveContext(), flags); } @@ -1156,10 +1154,10 @@ bool QQmlPropertyPrivate::writeValueProperty(QObject *object, const QQmlPropertyData &core, const QVariant &value, - QQmlContextData *context, WriteFlags flags) + QQmlContextData *context,QQmlPropertyData::WriteFlags flags) { // Remove any existing bindings on this property - if (!(flags & DontRemoveBinding) && object) + if (!(flags & QQmlPropertyData::DontRemoveBinding) && object) removeBinding(object, core.encodedIndex()); bool rv = false; @@ -1189,10 +1187,9 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, bool QQmlPropertyPrivate::write(QObject *object, const QQmlPropertyData &property, const QVariant &value, QQmlContextData *context, - WriteFlags flags) + QQmlPropertyData::WriteFlags flags) { int coreIdx = property.coreIndex; - int status = -1; //for dbus if (property.isEnum()) { QMetaProperty prop = object->metaObject()->property(property.coreIndex); @@ -1238,24 +1235,18 @@ bool QQmlPropertyPrivate::write(QObject *object, if (context && u.isRelative() && !u.isEmpty()) u = context->resolvedUrl(u); - int status = -1; - void *argv[] = { &u, 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv); + return property.writeProperty(object, &u, flags); } else if (propertyType == qMetaTypeId >()) { QList urlSeq = resolvedUrlSequence(value, context).value >(); - int status = -1; - void *argv[] = { &urlSeq, 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv); + return property.writeProperty(object, &urlSeq, flags); } else if (variantType == propertyType) { - void *a[] = { const_cast(value.constData()), 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + return property.writeProperty(object, const_cast(value.constData()), flags); } else if (qMetaTypeId() == propertyType) { - void *a[] = { const_cast(&value), 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + return property.writeProperty(object, const_cast(&value), flags); } else if (property.isQObject()) { @@ -1270,14 +1261,12 @@ bool QQmlPropertyPrivate::write(QObject *object, if (o) valMo = o; if (QQmlMetaObject::canConvert(valMo, propMo)) { - void *args[] = { &o, 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, args); + return property.writeProperty(object, &o, flags); } else if (!o && QQmlMetaObject::canConvert(propMo, valMo)) { // In the case of a null QObject, we assign the null if there is // any change that the null variant type could be up or down cast to // the property type. - void *args[] = { &o, 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, args); + return property.writeProperty(object, &o, flags); } else { return false; } @@ -1296,8 +1285,7 @@ bool QQmlPropertyPrivate::write(QObject *object, if (listType.isNull()) return false; QQmlListProperty prop; - void *args[] = { &prop, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, coreIdx, args); + property.readProperty(object, &prop); if (!prop.clear) return false; @@ -1410,8 +1398,7 @@ bool QQmlPropertyPrivate::write(QObject *object, } if (ok) { - void *a[] = { const_cast(v.constData()), 0, &status, &flags}; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + return property.writeProperty(object, const_cast(v.constData()), flags); } else { return false; } @@ -1512,7 +1499,7 @@ bool QQmlProperty::reset() const } bool QQmlPropertyPrivate::write(const QQmlProperty &that, - const QVariant &value, WriteFlags flags) + const QVariant &value, QQmlPropertyData::WriteFlags flags) { if (!that.d) return false; diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 58fea9c239..9398c74621 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -68,13 +68,6 @@ class QQmlJavaScriptExpression; class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount { public: - enum WriteFlag { - BypassInterceptor = 0x01, - DontRemoveBinding = 0x02, - RemoveBindingOnAliasWrite = 0x04 - }; - Q_DECLARE_FLAGS(WriteFlags, WriteFlag) - QQmlContextData *context; QPointer engine; QPointer object; @@ -97,7 +90,7 @@ public: QQmlProperty::PropertyTypeCategory propertyTypeCategory() const; QVariant readValueProperty(); - bool writeValueProperty(const QVariant &, WriteFlags); + bool writeValueProperty(const QVariant &, QQmlPropertyData::WriteFlags); static QQmlMetaObject rawMetaObjectForType(QQmlEnginePrivate *, int); static bool writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, @@ -105,9 +98,9 @@ public: static bool writeValueProperty(QObject *, const QQmlPropertyData &, const QVariant &, QQmlContextData *, - WriteFlags flags = 0); + QQmlPropertyData::WriteFlags flags = 0); static bool write(QObject *, const QQmlPropertyData &, const QVariant &, - QQmlContextData *, WriteFlags flags = 0); + QQmlContextData *, QQmlPropertyData::WriteFlags flags = 0); static void findAliasTarget(QObject *, int, QObject **, int *); enum BindingFlag { @@ -116,7 +109,7 @@ public: }; Q_DECLARE_FLAGS(BindingFlags, BindingFlag) - static void setBinding(QQmlAbstractBinding *binding, BindingFlags flags = None, WriteFlags writeFlags = DontRemoveBinding); + static void setBinding(QQmlAbstractBinding *binding, BindingFlags flags = None, QQmlPropertyData::WriteFlags writeFlags = QQmlPropertyData::DontRemoveBinding); static void removeBinding(const QQmlProperty &that); static void removeBinding(QObject *o, int index); @@ -144,7 +137,7 @@ public: QQmlBoundSignalExpression *); static void takeSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *); - static bool write(const QQmlProperty &that, const QVariant &, WriteFlags); + static bool write(const QQmlProperty &that, const QVariant &, QQmlPropertyData::WriteFlags); static int valueTypeCoreIndex(const QQmlProperty &that); static int bindingIndex(const QQmlProperty &that); static int bindingIndex(const QQmlPropertyData &that); @@ -157,7 +150,6 @@ public: static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *context); }; -Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyPrivate::WriteFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyPrivate::BindingFlags) QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index baba5347a7..ad6db9756f 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -61,6 +61,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -228,6 +229,13 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyRawData::Flags) class QQmlPropertyData : public QQmlPropertyRawData { public: + enum WriteFlag { + BypassInterceptor = 0x01, + DontRemoveBinding = 0x02, + RemoveBindingOnAliasWrite = 0x04 + }; + Q_DECLARE_FLAGS(WriteFlags, WriteFlag) + inline QQmlPropertyData(); inline QQmlPropertyData(const QQmlPropertyRawData &); @@ -241,6 +249,33 @@ public: void markAsOverrideOf(QQmlPropertyData *predecessor); + inline void readProperty(QObject *target, void *property) const + { + void *args[] = { property, 0 }; + readPropertyWithArgs(target, args); + } + + inline void readPropertyWithArgs(QObject *target, void *args[]) const + { + if (hasAccessors()) { + accessors->read(target, args[0]); + } else { + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, args); + } + } + + bool writeProperty(QObject *target, void *value, WriteFlags flags) const + { + if (flags.testFlag(BypassInterceptor) && hasAccessors() && accessors->write) { + accessors->write(target, value); + } else { + int status = -1; + void *argv[] = { value, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex, argv); + } + return true; + } + private: friend class QQmlPropertyCache; void lazyLoad(const QMetaProperty &); @@ -770,6 +805,8 @@ private: QVector> data; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags) + QT_END_NAMESPACE #endif // QQMLPROPERTYCACHE_P_H diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 44fd47244d..8e87ec7f63 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -219,7 +219,7 @@ void QQmlValueType::read(QObject *obj, int idx) QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); } -void QQmlValueType::write(QObject *obj, int idx, QQmlPropertyPrivate::WriteFlags flags) +void QQmlValueType::write(QObject *obj, int idx, QQmlPropertyData::WriteFlags flags) { Q_ASSERT(gadgetPtr); int status = -1; diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h index 910d39cf0a..11e1dfdb00 100644 --- a/src/qml/qml/qqmlvaluetype_p.h +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -69,7 +69,7 @@ public: QQmlValueType(int userType, const QMetaObject *metaObject); ~QQmlValueType(); void read(QObject *, int); - void write(QObject *, int, QQmlPropertyPrivate::WriteFlags flags); + void write(QObject *, int, QQmlPropertyData::WriteFlags flags); QVariant value(); void setValue(const QVariant &); diff --git a/src/qml/qml/qqmlvaluetypeproxybinding.cpp b/src/qml/qml/qqmlvaluetypeproxybinding.cpp index 6858215a79..595cd01d05 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding.cpp +++ b/src/qml/qml/qqmlvaluetypeproxybinding.cpp @@ -58,7 +58,7 @@ QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding() } } -void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) +void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) { QQmlAbstractBinding *b = m_bindings.data(); while (b) { diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h index de5acc2984..6e297bb3ea 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h +++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h @@ -63,7 +63,7 @@ public: QQmlAbstractBinding *binding(int targetPropertyIndex); void removeBindings(quint32 mask); - virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags); + virtual void setEnabled(bool, QQmlPropertyData::WriteFlags); virtual bool isValueTypeProxy() const; protected: diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index d7f6c5b3af..d5001674ad 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -220,7 +220,7 @@ int QQmlInterceptorMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) { if (c == QMetaObject::WriteProperty && interceptors && - !(*reinterpret_cast(a[3]) & QQmlPropertyPrivate::BypassInterceptor)) { + !(*reinterpret_cast(a[3]) & QQmlPropertyData::BypassInterceptor)) { for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) { if (vi->m_coreIndex != id) @@ -278,7 +278,7 @@ bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) bool updated = false; if (newComponentValue != prevComponentValue) { valueProp.write(valueType, prevComponentValue); - valueType->write(object, id, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); vi->write(newComponentValue); updated = true; @@ -872,7 +872,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * // Remove binding (if any) on write if(c == QMetaObject::WriteProperty) { int flags = *reinterpret_cast(a[3]); - if (flags & QQmlPropertyPrivate::RemoveBindingOnAliasWrite) { + if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) { QQmlData *targetData = QQmlData::get(target); if (targetData && targetData->hasBindingBit(coreIndex)) QQmlPropertyPrivate::removeBinding(target, aliasData->encodedMetaPropertyIndex); diff --git a/src/quick/designer/qquickdesignercustomobjectdata.cpp b/src/quick/designer/qquickdesignercustomobjectdata.cpp index 42dcb08d45..3c8f4b281c 100644 --- a/src/quick/designer/qquickdesignercustomobjectdata.cpp +++ b/src/quick/designer/qquickdesignercustomobjectdata.cpp @@ -194,7 +194,7 @@ void QQuickDesignerCustomObjectData::doResetProperty(QQmlContext *context, const #endif if (qmlBinding) qmlBinding->setTarget(property); - QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding); if (qmlBinding) qmlBinding->update(); @@ -262,7 +262,7 @@ void QQuickDesignerCustomObjectData::setPropertyBinding(QQmlContext *context, binding->setTarget(property); binding->setNotifyOnValueChanged(true); - QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding); //Refcounting is taking take care of deletion binding->update(); if (binding->hasError()) { diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp index 741a583803..d782f9309f 100644 --- a/src/quick/util/qquickanimation.cpp +++ b/src/quick/util/qquickanimation.cpp @@ -1202,7 +1202,7 @@ QAbstractAnimationJob* QQuickPropertyAction::transition(QQuickStateActions &acti { for (int ii = 0; ii < actions.count(); ++ii) { const QQuickStateAction &action = actions.at(ii); - QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); } } virtual void debugAction(QDebug d, int indentLevel) const { @@ -2535,7 +2535,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v) QQuickStateAction &action = actions[ii]; if (v == 1.) { - QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); } else { if (!fromSourced && !fromDefined) { action.fromValue = action.property.read(); @@ -2551,7 +2551,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v) } } if (interpolator) - QQmlPropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); } if (deleted) return; diff --git a/src/quick/util/qquickbehavior.cpp b/src/quick/util/qquickbehavior.cpp index 147380037d..1d3ee2c4be 100644 --- a/src/quick/util/qquickbehavior.cpp +++ b/src/quick/util/qquickbehavior.cpp @@ -180,7 +180,7 @@ void QQuickBehavior::write(const QVariant &value) if (!d->animation || bypass) { if (d->animationInstance) d->animationInstance->stop(); - QQmlPropertyPrivate::write(d->property, value, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::write(d->property, value, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); d->targetValue = value; return; } @@ -206,7 +206,7 @@ void QQuickBehavior::write(const QVariant &value) // is needed (value has not changed). If the Behavior was already // running, let it continue as normal to ensure correct behavior and state. if (!behaviorActive && d->targetValue == currentValue) { - QQmlPropertyPrivate::write(d->property, value, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::write(d->property, value, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); return; } @@ -234,7 +234,7 @@ void QQuickBehavior::write(const QVariant &value) d->blockRunningChanged = false; } if (!after.contains(d->property)) - QQmlPropertyPrivate::write(d->property, value, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::write(d->property, value, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); } void QQuickBehavior::setTarget(const QQmlProperty &property) diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index a42ec31058..c4be68cd31 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -597,7 +597,7 @@ void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &val state()->addEntryToRevertList(action); QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(action.property); if (oldBinding) - oldBinding->setEnabled(false, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + oldBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); d->property(name).write(value); } } @@ -631,7 +631,7 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString &QQmlPropertyPrivate::get(prop)->core, expression, object(), qmlContext(this)); newBinding->setTarget(prop); - QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); } return; } @@ -644,7 +644,7 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString if (hadValue) { QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(d->property(name)); if (oldBinding) { - oldBinding->setEnabled(false, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + oldBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); state()->changeBindingInRevertList(object(), name, oldBinding); } @@ -653,7 +653,7 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString &QQmlPropertyPrivate::get(prop)->core, expression, object(), qmlContext(this)); newBinding->setTarget(prop); - QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); } else { QQuickStateAction action; action.restore = restoreEntryValues(); @@ -679,9 +679,9 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString state()->addEntryToRevertList(action); QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(action.property); if (oldBinding) - oldBinding->setEnabled(false, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + oldBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); - QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); } } } diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp index 569cb37c95..a992589040 100644 --- a/src/quick/util/qquicksmoothedanimation.cpp +++ b/src/quick/util/qquicksmoothedanimation.cpp @@ -254,8 +254,8 @@ void QSmoothedAnimation::updateCurrentTime(int t) qreal value = easeFollow(time_seconds); value *= (invert? -1.0: 1.0); QQmlPropertyPrivate::write(target, initialValue + value, - QQmlPropertyPrivate::BypassInterceptor - | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyData::BypassInterceptor + | QQmlPropertyData::DontRemoveBinding); } void QSmoothedAnimation::init() @@ -287,8 +287,8 @@ void QSmoothedAnimation::init() break; case QQuickSmoothedAnimation::Sync: QQmlPropertyPrivate::write(target, to, - QQmlPropertyPrivate::BypassInterceptor - | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyData::BypassInterceptor + | QQmlPropertyData::DontRemoveBinding); trackVelocity = 0; stop(); return; @@ -304,8 +304,8 @@ void QSmoothedAnimation::init() if (!recalc()) { QQmlPropertyPrivate::write(target, to, - QQmlPropertyPrivate::BypassInterceptor - | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyData::BypassInterceptor + | QQmlPropertyData::DontRemoveBinding); stop(); return; } diff --git a/src/quick/util/qquickspringanimation.cpp b/src/quick/util/qquickspringanimation.cpp index d2bc3b4ece..294122150a 100644 --- a/src/quick/util/qquickspringanimation.cpp +++ b/src/quick/util/qquickspringanimation.cpp @@ -301,8 +301,8 @@ void QSpringAnimation::updateCurrentTime(int time) qreal old_to = to; QQmlPropertyPrivate::write(target, currentValue, - QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyData::BypassInterceptor | + QQmlPropertyData::DontRemoveBinding); if (stopped && old_to == to) { // do not stop if we got restarted if (animationTemplate) diff --git a/src/quick/util/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp index 55abb0a207..60f710549b 100644 --- a/src/quick/util/qquicktransitionmanager.cpp +++ b/src/quick/util/qquicktransitionmanager.cpp @@ -159,9 +159,9 @@ void QQuickTransitionManager::transition(const QList &list, for (int ii = 0; ii < applyList.size(); ++ii) { const QQuickStateAction &action = applyList.at(ii); if (action.toBinding) { - QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); } else if (!action.event) { - QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); } else if (action.event->isReversable()) { if (action.reverseEvent) action.event->reverse(); @@ -197,7 +197,7 @@ void QQuickTransitionManager::transition(const QList &list, if (action.toBinding) QQmlPropertyPrivate::removeBinding(action.property); // Make sure this is disabled during the transition - QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); + QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); } } diff --git a/tests/auto/qml/qqmlvaluetypes/testtypes.h b/tests/auto/qml/qqmlvaluetypes/testtypes.h index 77d723fbd4..bcfe4028c6 100644 --- a/tests/auto/qml/qqmlvaluetypes/testtypes.h +++ b/tests/auto/qml/qqmlvaluetypes/testtypes.h @@ -194,7 +194,7 @@ class MyOffsetValueInterceptor : public QObject, public QQmlPropertyValueInterce Q_INTERFACES(QQmlPropertyValueInterceptor) public: virtual void setTarget(const QQmlProperty &p) { prop = p; } - virtual void write(const QVariant &value) { QQmlPropertyPrivate::write(prop, value.toInt() + 13, QQmlPropertyPrivate::BypassInterceptor); } + virtual void write(const QVariant &value) { QQmlPropertyPrivate::write(prop, value.toInt() + 13, QQmlPropertyData::BypassInterceptor); } private: QQmlProperty prop; @@ -215,7 +215,7 @@ public: c.getRgb(&r, &g, &b, &a); c.setRgb(a, b, g, r); - QQmlPropertyPrivate::write(prop, c, QQmlPropertyPrivate::BypassInterceptor); + QQmlPropertyPrivate::write(prop, c, QQmlPropertyData::BypassInterceptor); } private: @@ -230,7 +230,7 @@ public: virtual void setTarget(const QQmlProperty &p) { prop = p; } virtual void write(const QVariant &) { - QQmlPropertyPrivate::write(prop, 0.0f, QQmlPropertyPrivate::BypassInterceptor); + QQmlPropertyPrivate::write(prop, 0.0f, QQmlPropertyData::BypassInterceptor); } private: -- cgit v1.2.3 From dbb56ab74a8978a8fb13572af020a63f378fc846 Mon Sep 17 00:00:00 2001 From: Nikita Krupenko Date: Mon, 12 Oct 2015 19:36:31 +0300 Subject: Expose HSV and HSL color values to QML [ChangeLog][QtQuick] Added properties to access color values for the HSV and HSL color models to the basic QML color type. Task-number: QTBUG-48723 Change-Id: I45fa52f9f3878553e1b3d0a34b47804ede7794f8 Reviewed-by: Mitch Curtis --- src/quick/doc/src/qmltypereference.qdoc | 13 ++-- src/quick/util/qquickvaluetypes.cpp | 72 ++++++++++++++++++++++ src/quick/util/qquickvaluetypes_p.h | 18 ++++++ tests/auto/qml/qqmlvaluetypes/data/color_read.qml | 6 ++ .../qml/qqmlvaluetypes/data/color_write_HSL.qml | 8 +++ .../qml/qqmlvaluetypes/data/color_write_HSV.qml | 8 +++ .../auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp | 33 ++++++++++ 7 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml create mode 100644 tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc index 6e6e66e026..2406722dbc 100644 --- a/src/quick/doc/src/qmltypereference.qdoc +++ b/src/quick/doc/src/qmltypereference.qdoc @@ -100,9 +100,9 @@ available when you import \c QtQuick. \li By a hexadecimal triplet or quad in the form \c "#RRGGBB" and \c "#AARRGGBB" respectively. For example, the color red corresponds to a triplet of \c "#FF0000" and a slightly transparent blue to a quad of \c "#800000FF". - \li Using the \l{QtQml::Qt::rgba()}{Qt.rgba()}, \l{QtQml::Qt::hsla()}{Qt.hsla()}, - \l{QtQml::Qt::darker()}{Qt.darker()}, \l{QtQml::Qt::lighter()}{Qt.lighter()} or - \l{QtQml::Qt::tint()}{Qt.tint()} functions. + \li Using the \l{QtQml::Qt::rgba()}{Qt.rgba()}, \l{QtQml::Qt::hsva()}{Qt.hsva()}, + \l{QtQml::Qt::hsla()}{Qt.hsla()}, \l{QtQml::Qt::darker()}{Qt.darker()}, + \l{QtQml::Qt::lighter()}{Qt.lighter()} or \l{QtQml::Qt::tint()}{Qt.tint()} functions. \endlist Example: @@ -112,8 +112,11 @@ available when you import \c QtQuick. \enddiv \snippet qml/colors.qml colors - Additionally, a color type has \c r, \c g, \c b and \c a properties that refer to the - red, green, blue and alpha values of the color, respectively: + A color type has \c r, \c g, \c b and \c a properties that refer to the red, + green, blue and alpha values of the color, respectively. Additionally it has + \c hsvHue, \c hsvSaturation, \c hsvValue and \c hslHue, \c hslSaturation, + \c hslLightness properties, which allow access to color values in HSV and HSL + color models accordingly: \qml Text { diff --git a/src/quick/util/qquickvaluetypes.cpp b/src/quick/util/qquickvaluetypes.cpp index 7b9b6068bd..e673df0451 100644 --- a/src/quick/util/qquickvaluetypes.cpp +++ b/src/quick/util/qquickvaluetypes.cpp @@ -80,6 +80,36 @@ qreal QQuickColorValueType::a() const return v.alphaF(); } +qreal QQuickColorValueType::hsvHue() const +{ + return v.hsvHueF(); +} + +qreal QQuickColorValueType::hsvSaturation() const +{ + return v.hsvSaturationF(); +} + +qreal QQuickColorValueType::hsvValue() const +{ + return v.valueF(); +} + +qreal QQuickColorValueType::hslHue() const +{ + return v.hslHueF(); +} + +qreal QQuickColorValueType::hslSaturation() const +{ + return v.hslSaturationF(); +} + +qreal QQuickColorValueType::hslLightness() const +{ + return v.lightnessF(); +} + void QQuickColorValueType::setR(qreal r) { v.setRedF(r); @@ -100,6 +130,48 @@ void QQuickColorValueType::setA(qreal a) v.setAlphaF(a); } +void QQuickColorValueType::setHsvHue(qreal hsvHue) +{ + qreal hue, saturation, value, alpha; + v.getHsvF(&hue, &saturation, &value, &alpha); + v.setHsvF(hsvHue, saturation, value, alpha); +} + +void QQuickColorValueType::setHsvSaturation(qreal hsvSaturation) +{ + qreal hue, saturation, value, alpha; + v.getHsvF(&hue, &saturation, &value, &alpha); + v.setHsvF(hue, hsvSaturation, value, alpha); +} + +void QQuickColorValueType::setHsvValue(qreal hsvValue) +{ + qreal hue, saturation, value, alpha; + v.getHsvF(&hue, &saturation, &value, &alpha); + v.setHsvF(hue, saturation, hsvValue, alpha); +} + +void QQuickColorValueType::setHslHue(qreal hslHue) +{ + qreal hue, saturation, lightness, alpha; + v.getHslF(&hue, &saturation, &lightness, &alpha); + v.setHslF(hslHue, saturation, lightness, alpha); +} + +void QQuickColorValueType::setHslSaturation(qreal hslSaturation) +{ + qreal hue, saturation, lightness, alpha; + v.getHslF(&hue, &saturation, &lightness, &alpha); + v.setHslF(hue, hslSaturation, lightness, alpha); +} + +void QQuickColorValueType::setHslLightness(qreal hslLightness) +{ + qreal hue, saturation, lightness, alpha; + v.getHslF(&hue, &saturation, &lightness, &alpha); + v.setHslF(hue, saturation, hslLightness, alpha); +} + QString QQuickVector2DValueType::toString() const { return QString(QLatin1String("QVector2D(%1, %2)")).arg(v.x()).arg(v.y()); diff --git a/src/quick/util/qquickvaluetypes_p.h b/src/quick/util/qquickvaluetypes_p.h index 05e954f915..4a1598ec5c 100644 --- a/src/quick/util/qquickvaluetypes_p.h +++ b/src/quick/util/qquickvaluetypes_p.h @@ -78,6 +78,12 @@ class QQuickColorValueType Q_PROPERTY(qreal g READ g WRITE setG FINAL) Q_PROPERTY(qreal b READ b WRITE setB FINAL) Q_PROPERTY(qreal a READ a WRITE setA FINAL) + Q_PROPERTY(qreal hsvHue READ hsvHue WRITE setHsvHue FINAL) + Q_PROPERTY(qreal hsvSaturation READ hsvSaturation WRITE setHsvSaturation FINAL) + Q_PROPERTY(qreal hsvValue READ hsvValue WRITE setHsvValue FINAL) + Q_PROPERTY(qreal hslHue READ hslHue WRITE setHslHue FINAL) + Q_PROPERTY(qreal hslSaturation READ hslSaturation WRITE setHslSaturation FINAL) + Q_PROPERTY(qreal hslLightness READ hslLightness WRITE setHslLightness FINAL) Q_GADGET public: Q_INVOKABLE QString toString() const; @@ -86,10 +92,22 @@ public: qreal g() const; qreal b() const; qreal a() const; + qreal hsvHue() const; + qreal hsvSaturation() const; + qreal hsvValue() const; + qreal hslHue() const; + qreal hslSaturation() const; + qreal hslLightness() const; void setR(qreal); void setG(qreal); void setB(qreal); void setA(qreal); + void setHsvHue(qreal); + void setHsvSaturation(qreal); + void setHsvValue(qreal); + void setHslHue(qreal); + void setHslSaturation(qreal); + void setHslLightness(qreal); }; class QQuickVector2DValueType diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_read.qml b/tests/auto/qml/qqmlvaluetypes/data/color_read.qml index bc92b1e5f9..73d2b921a7 100644 --- a/tests/auto/qml/qqmlvaluetypes/data/color_read.qml +++ b/tests/auto/qml/qqmlvaluetypes/data/color_read.qml @@ -5,5 +5,11 @@ MyTypeObject { property real v_g: color.g property real v_b: color.b property real v_a: color.a + property real hsv_h: color.hsvHue + property real hsv_s: color.hsvSaturation + property real hsv_v: color.hsvValue + property real hsl_h: color.hslHue + property real hsl_s: color.hslSaturation + property real hsl_l: color.hslLightness property variant copy: color } diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml new file mode 100644 index 0000000000..0034163bbe --- /dev/null +++ b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml @@ -0,0 +1,8 @@ +import Test 1.0 + +MyTypeObject { + color.hslHue: if (true) 0.43 + color.hslSaturation: if (true) 0.74 + color.hslLightness: if (true) 0.54 + color.a: if (true) 0.7 +} diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml new file mode 100644 index 0000000000..1fc47d460e --- /dev/null +++ b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml @@ -0,0 +1,8 @@ +import Test 1.0 + +MyTypeObject { + color.hsvHue: if (true) 0.43 + color.hsvSaturation: if (true) 0.77 + color.hsvValue: if (true) 0.88 + color.a: if (true) 0.7 +} diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index f506d0f53a..803bad197a 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -907,6 +907,15 @@ void tst_qqmlvaluetypes::color() QCOMPARE((float)object->property("v_g").toDouble(), (float)0.88); QCOMPARE((float)object->property("v_b").toDouble(), (float)0.6); QCOMPARE((float)object->property("v_a").toDouble(), (float)0.34); + + QCOMPARE(qRound(object->property("hsv_h").toDouble() * 100), 43); + QCOMPARE(qRound(object->property("hsv_s").toDouble() * 100), 77); + QCOMPARE(qRound(object->property("hsv_v").toDouble() * 100), 88); + + QCOMPARE(qRound(object->property("hsl_h").toDouble() * 100), 43); + QCOMPARE(qRound(object->property("hsl_s").toDouble() * 100), 74); + QCOMPARE(qRound(object->property("hsl_l").toDouble() * 100), 54); + QColor comparison; comparison.setRedF(0.2); comparison.setGreenF(0.88); @@ -932,6 +941,30 @@ void tst_qqmlvaluetypes::color() delete object; } + { + QQmlComponent component(&engine, testFileUrl("color_write_HSV.qml")); + MyTypeObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + QColor newColor; + newColor.setHsvF(0.43, 0.77, 0.88, 0.7); + QCOMPARE(object->color(), newColor); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("color_write_HSL.qml")); + MyTypeObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + QColor newColor; + newColor.setHslF(0.43, 0.74, 0.54, 0.7); + QCOMPARE(object->color(), newColor); + + delete object; + } + { QQmlComponent component(&engine, testFileUrl("color_compare.qml")); MyTypeObject *object = qobject_cast(component.create()); -- cgit v1.2.3 From 42f6af4781fc4be3f565881277fab041bcabecec Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 19 Jul 2016 13:32:49 +0200 Subject: QML: Clean up and re-order QQmlPropertyPrivate::write The most common cases of property writes now come first. Change-Id: I225b73746120870d9ca01d09983ad225e32bd99c Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlproperty.cpp | 146 ++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 65 deletions(-) diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 64852f1cfe..b3eb0a5619 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1189,133 +1189,149 @@ bool QQmlPropertyPrivate::write(QObject *object, const QVariant &value, QQmlContextData *context, QQmlPropertyData::WriteFlags flags) { - int coreIdx = property.coreIndex; + const int propertyType = property.propType; + const int variantType = value.userType(); if (property.isEnum()) { QMetaProperty prop = object->metaObject()->property(property.coreIndex); QVariant v = value; // Enum values come through the script engine as doubles - if (value.userType() == QVariant::Double) { + if (variantType == QVariant::Double) { double integral; double fractional = std::modf(value.toDouble(), &integral); if (qFuzzyIsNull(fractional)) v.convert(QVariant::Int); } - return writeEnumProperty(prop, coreIdx, object, v, flags); + return writeEnumProperty(prop, property.coreIndex, object, v, flags); } - int propertyType = property.propType; - int variantType = value.userType(); - QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context); + const bool isUrl = propertyType == QVariant::Url; // handled separately + + // The cases below are in approximate order of likelyhood: + if (propertyType == variantType && !isUrl && propertyType != qMetaTypeId>() && !property.isQList()) { + return property.writeProperty(object, const_cast(value.constData()), flags); + } else if (property.isQObject()) { + QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, variantType); + if (valMo.isNull()) + return false; + QObject *o = *static_cast(value.constData()); + QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType); - if (propertyType == QVariant::Url) { + if (o) + valMo = o; + if (QQmlMetaObject::canConvert(valMo, propMo)) { + return property.writeProperty(object, &o, flags); + } else if (!o && QQmlMetaObject::canConvert(propMo, valMo)) { + // In the case of a null QObject, we assign the null if there is + // any change that the null variant type could be up or down cast to + // the property type. + return property.writeProperty(object, &o, flags); + } else { + return false; + } + } else if (value.canConvert(propertyType) && !isUrl && variantType != QVariant::String && propertyType != qMetaTypeId>() && !property.isQList()) { + // common cases: + switch (propertyType) { + case QMetaType::Bool: { + bool b = value.toBool(); + return property.writeProperty(object, &b, flags); + } + case QMetaType::Int: { + int i = value.toInt(); + return property.writeProperty(object, &i, flags); + } + case QMetaType::Double: { + double d = value.toDouble(); + return property.writeProperty(object, &d, flags); + } + case QMetaType::Float: { + float f = value.toFloat(); + return property.writeProperty(object, &f, flags); + } + case QMetaType::QString: { + QString s = value.toString(); + return property.writeProperty(object, &s, flags); + } + default: { // "fallback": + QVariant v = value; + v.convert(propertyType); + return property.writeProperty(object, const_cast(v.constData()), flags); + } + } + } else if (propertyType == qMetaTypeId()) { + return property.writeProperty(object, const_cast(&value), flags); + } else if (isUrl) { QUrl u; - bool found = false; if (variantType == QVariant::Url) { u = value.toUrl(); - found = true; } else if (variantType == QVariant::ByteArray) { QString input(QString::fromUtf8(value.toByteArray())); // Encoded dir-separators defeat QUrl processing - decode them first input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); u = QUrl(input); - found = true; } else if (variantType == QVariant::String) { QString input(value.toString()); // Encoded dir-separators defeat QUrl processing - decode them first input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); u = QUrl(input); - found = true; - } - - if (!found) + } else { return false; + } if (context && u.isRelative() && !u.isEmpty()) u = context->resolvedUrl(u); return property.writeProperty(object, &u, flags); - - } else if (propertyType == qMetaTypeId >()) { - QList urlSeq = resolvedUrlSequence(value, context).value >(); + } else if (propertyType == qMetaTypeId>()) { + QList urlSeq = resolvedUrlSequence(value, context).value>(); return property.writeProperty(object, &urlSeq, flags); - } else if (variantType == propertyType) { - - return property.writeProperty(object, const_cast(value.constData()), flags); - - } else if (qMetaTypeId() == propertyType) { - - return property.writeProperty(object, const_cast(&value), flags); - - } else if (property.isQObject()) { - - QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, value.userType()); - - if (valMo.isNull()) - return false; - - QObject *o = *(QObject *const *)value.constData(); - QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType); - - if (o) valMo = o; - - if (QQmlMetaObject::canConvert(valMo, propMo)) { - return property.writeProperty(object, &o, flags); - } else if (!o && QQmlMetaObject::canConvert(propMo, valMo)) { - // In the case of a null QObject, we assign the null if there is - // any change that the null variant type could be up or down cast to - // the property type. - return property.writeProperty(object, &o, flags); - } else { - return false; - } - } else if (property.isQList()) { - QQmlMetaObject listType; if (enginePriv) { listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType)); } else { QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType)); - if (!type) return false; + if (!type) + return false; listType = type->baseMetaObject(); } - if (listType.isNull()) return false; + if (listType.isNull()) + return false; QQmlListProperty prop; property.readProperty(object, &prop); - if (!prop.clear) return false; + if (!prop.clear) + return false; prop.clear(&prop); - if (value.userType() == qMetaTypeId()) { + if (variantType == qMetaTypeId()) { QQmlListReference qdlr = value.value(); for (int ii = 0; ii < qdlr.count(); ++ii) { QObject *o = qdlr.at(ii); if (o && !QQmlMetaObject::canConvert(o, listType)) - o = 0; - prop.append(&prop, (void *)o); + o = nullptr; + prop.append(&prop, o); } - } else if (value.userType() == qMetaTypeId >()) { + } else if (variantType == qMetaTypeId >()) { const QList &list = qvariant_cast >(value); for (int ii = 0; ii < list.count(); ++ii) { QObject *o = list.at(ii); if (o && !QQmlMetaObject::canConvert(o, listType)) - o = 0; - prop.append(&prop, (void *)o); + o = nullptr; + prop.append(&prop, o); } } else { QObject *o = enginePriv?enginePriv->toQObject(value):QQmlMetaType::toQObject(value); if (o && !QQmlMetaObject::canConvert(o, listType)) - o = 0; - prop.append(&prop, (void *)o); + o = nullptr; + prop.append(&prop, o); } - } else { Q_ASSERT(variantType != propertyType); @@ -1355,7 +1371,8 @@ bool QQmlPropertyPrivate::write(QObject *object, // successful conversion. Q_ASSERT(v.userType() == propertyType); ok = true; - } else if ((uint)propertyType >= QVariant::UserType && variantType == QVariant::String) { + } else if (static_cast(propertyType) >= QVariant::UserType && + variantType == QVariant::String) { QQmlMetaType::StringConverter con = QQmlMetaType::customStringConverter(propertyType); if (con) { v = con(value.toString()); @@ -1414,10 +1431,9 @@ QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engi return metaType.metaObject(); if (engine) return engine->rawMetaObjectForType(userType); - QQmlType *type = QQmlMetaType::qmlType(userType); - if (type) + if (QQmlType *type = QQmlMetaType::qmlType(userType)) return QQmlMetaObject(type->baseMetaObject()); - return QQmlMetaObject((QObject*)0); + return QQmlMetaObject(); } /*! -- cgit v1.2.3 From 530b1f145d0deb076416fbce6cd02de86e827479 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 21 Jul 2016 13:45:43 +0200 Subject: QmlDebug: Fixes for QQmlDebugConnection Properly close the connection on invalid hello messages and implement flush() also for local sockets. Change-Id: Ie78441b9933c6de053962a11b5d7e2636792a31c Reviewed-by: Simon Hausmann --- src/qmldebug/qqmldebugconnection.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qmldebug/qqmldebugconnection.cpp b/src/qmldebug/qqmldebugconnection.cpp index 35a540bff8..61f178fd28 100644 --- a/src/qmldebug/qqmldebugconnection.cpp +++ b/src/qmldebug/qqmldebugconnection.cpp @@ -159,7 +159,7 @@ void QQmlDebugConnection::protocolReadyRead() if (!validHello) { qWarning("QQmlDebugConnection: Invalid hello message"); - QObject::disconnect(d->protocol, SIGNAL(protocolReadyRead()), this, SLOT(protocolReadyRead())); + close(); return; } d->gotHello = true; @@ -374,8 +374,9 @@ bool QQmlDebugConnection::sendMessage(const QString &name, const QByteArray &mes void QQmlDebugConnectionPrivate::flush() { - QAbstractSocket *socket = qobject_cast(device); - if (socket) + if (QAbstractSocket *socket = qobject_cast(device)) + socket->flush(); + else if (QLocalSocket *socket = qobject_cast(device)) socket->flush(); } -- cgit v1.2.3 From 34c555b55474f49c8ce4d27c0563c023e34bc604 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 25 Jul 2016 14:16:27 +0200 Subject: Fix QQuickPointerTouchEvent::grabbers() The loop would iterate over cached points that were not part of the event. Change-Id: I16a1f52c1616b7bdc5aec2fce4d7d7593edd0e18 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index ea1a6e263e..773ba264ba 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -613,7 +613,8 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const { QVector QQuickPointerTouchEvent::grabbers() const { QVector result; - for (auto point : qAsConst(m_touchPoints)) { + for (int i = 0; i < m_pointCount; ++i) { + auto point = m_touchPoints.at(i); if (QQuickItem *grabber = point->grabber()) { if (!result.contains(grabber)) result << grabber; -- cgit v1.2.3 From 1de769c844982d8a8e26d6b71dcba061c93824f3 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 25 Jul 2016 15:20:03 +0200 Subject: Initialize m_pointCount in QQuickPointerTouchEvent Change-Id: Id9d602a4312db3877e21e449fcd2bbb3b58e1ced Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents_p_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 59b211e43e..63da48e5d0 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -404,7 +404,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent { public: QQuickPointerTouchEvent(QObject *parent = nullptr) - : QQuickPointerEvent(parent) + : QQuickPointerEvent(parent), m_pointCount(0) {} QQuickPointerEvent *reset(QEvent *) override; -- cgit v1.2.3 From 52c66e85f003dbb78b8bd35ce7836b682225b73b Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 25 Jul 2016 15:47:36 +0200 Subject: Fix usage of QTest::touchEvent Change-Id: Ib87f7800ffa18f9a4f905ecd0aa0173f46fa2d90 Reviewed-by: Frederik Gladhorn --- .../quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index c3981c466f..2872556a94 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -915,13 +915,13 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() // Touch both, release one, manipulate other touchpoint with mouse QTest::touchEvent(window.data(), device).press(1, touch1); QQuickTouchUtils::flush(window.data()); - QTest::touchEvent(window.data(), device).press(2, touch2); + QTest::touchEvent(window.data(), device).move(1, touch1).press(2, touch2); QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch2.x()); QCOMPARE(touch2rect->property("y").toInt(), touch2.y()); - QTest::touchEvent(window.data(), device).release(1, touch1); + QTest::touchEvent(window.data(), device).release(1, touch1).move(2, touch2); touch1.setY(20); QTest::mousePress(window.data(), Qt::LeftButton, 0, touch1); QQuickTouchUtils::flush(window.data()); -- cgit v1.2.3 From 16b680b734101db0cb4fa3f313ac563a509ddff4 Mon Sep 17 00:00:00 2001 From: Thomas McGuire Date: Mon, 25 Jul 2016 13:23:34 +0200 Subject: QQmlPropertyMap: Don't spuriously emit valueChanged() signal The valueChanged() signal was emitted when the property was written with the same value. This increased the potential for binding loops in user code. Change-Id: Ifeb8f6f23e2022aa35cb6cac7cf1a3dbc0e8ca2f Task-number: QTBUG-48136 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlopenmetaobject.cpp | 2 +- .../qml/qqmlpropertymap/tst_qqmlpropertymap.cpp | 35 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp index 9188ba6174..0e60cae92f 100644 --- a/src/qml/qml/qqmlopenmetaobject.cpp +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -272,7 +272,7 @@ int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void * propertyRead(propId); *reinterpret_cast(a[0]) = d->getData(propId); } else if (c == QMetaObject::WriteProperty) { - if (propId <= d->data.count() || d->data[propId].first != *reinterpret_cast(a[0])) { + if (propId >= d->data.count() || d->data[propId].first != *reinterpret_cast(a[0])) { propertyWrite(propId); QPair &prop = d->getDataRef(propId); prop.first = propertyWriteValue(propId, *reinterpret_cast(a[0])); diff --git a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp index 2f3754e42d..1649cde4ce 100644 --- a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp +++ b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp @@ -65,6 +65,7 @@ private slots: void QTBUG_35233(); void disallowExtending(); void QTBUG_35906(); + void QTBUG_48136(); }; class LazyPropertyMap : public QQmlPropertyMap, public QQmlParserStatus @@ -467,6 +468,40 @@ void tst_QQmlPropertyMap::QTBUG_35906() QCOMPARE(value.toInt(), 42); } +void tst_QQmlPropertyMap::QTBUG_48136() +{ + static const char key[] = "mykey"; + QQmlPropertyMap map; + + // + // Test that the notify signal is emitted correctly + // + + const int propIndex = map.metaObject()->indexOfProperty(key); + const QMetaProperty prop = map.metaObject()->property(propIndex); + QSignalSpy notifySpy(&map, QByteArray::number(QSIGNAL_CODE) + prop.notifySignal().methodSignature()); + + map.insert(key, 42); + QCOMPARE(notifySpy.count(), 1); + map.insert(key, 43); + QCOMPARE(notifySpy.count(), 2); + map.insert(key, 43); + QCOMPARE(notifySpy.count(), 2); + map.insert(key, 44); + QCOMPARE(notifySpy.count(), 3); + + // + // Test that the valueChanged signal is emitted correctly + // + QSignalSpy valueChangedSpy(&map, &QQmlPropertyMap::valueChanged); + map.setProperty(key, 44); + QCOMPARE(valueChangedSpy.count(), 0); + map.setProperty(key, 45); + QCOMPARE(valueChangedSpy.count(), 1); + map.setProperty(key, 45); + QCOMPARE(valueChangedSpy.count(), 1); +} + QTEST_MAIN(tst_QQmlPropertyMap) #include "tst_qqmlpropertymap.moc" -- cgit v1.2.3 From ed054acb9ca0ca8c86bcba7b290461b3177a7c50 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 25 Jul 2016 23:35:26 +0200 Subject: Fix tst_QQuickPinchArea::cancel The cancel event must have the device set, otherwise it may not be delivered properly. Change-Id: I7f7899fdbfd8fd68e9e19bcd043a46ddb31516cd Reviewed-by: Frederik Gladhorn --- tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp index 1d7273f6df..c1a51fd659 100644 --- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp +++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp @@ -511,6 +511,7 @@ void tst_QQuickPinchArea::cancel() QCOMPARE(blackRect->scale(), 1.5); QTouchEvent cancelEvent(QEvent::TouchCancel); + cancelEvent.setDevice(device); QCoreApplication::sendEvent(window, &cancelEvent); QQuickTouchUtils::flush(window); -- cgit v1.2.3 From eb238e2cf9013f9a6cd20af0a59180896abdc377 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sun, 24 Jul 2016 23:15:31 +0200 Subject: Keep track of grabbing inside QQuickEventPoint With this we can get rid of "itemForTouchPointId" which kept track of the individual grabbing of touch points. The new approach allows big cleanups and makes understanding the code easier. Change-Id: I19f7cda1b430e088ada707b2e75d97ca8dbadcc5 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 37 ++++++++-- src/quick/items/qquickevents_p_p.h | 4 ++ src/quick/items/qquickwindow.cpp | 99 +++++++++++++++----------- src/quick/items/qquickwindow_p.h | 2 - tests/auto/quick/touchmouse/tst_touchmouse.cpp | 18 ++--- 5 files changed, 104 insertions(+), 56 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 773ba264ba..709ba9e69d 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -481,6 +481,11 @@ QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d) return dev; } +QList QQuickPointerDevice::touchDevices() +{ + return g_touchDevices->values(); +} + QQuickPointerDevice *QQuickPointerDevice::genericMouseDevice() { return g_genericMouseDevice; @@ -551,13 +556,28 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) { m_pressedButtons = Qt::NoButton; const QList &tps = ev->touchPoints(); - m_pointCount = tps.count(); - m_touchPoints.reserve(m_pointCount); - for (int i = m_touchPoints.size(); i < m_pointCount; ++i) + int newPointCount = tps.count(); + m_touchPoints.reserve(newPointCount); + + for (int i = m_touchPoints.size(); i < newPointCount; ++i) m_touchPoints.insert(i, new QQuickEventTouchPoint(this)); - for (int i = 0; i < m_pointCount; ++i) + // Make sure the grabbers are right from one event to the next + QVector grabbers; + // Copy all grabbers, because the order of points might have changed in the event. + // The ID is all that we can rely on (release might remove the first point etc). + for (int i = 0; i < newPointCount; ++i) { + QQuickItem *grabber = nullptr; + if (auto point = pointById(tps.at(i).id())) + grabber = point->grabber(); + grabbers.append(grabber); + } + + for (int i = 0; i < newPointCount; ++i) { m_touchPoints.at(i)->reset(tps.at(i), ev->timestamp()); + m_touchPoints.at(i)->setGrabber(grabbers.at(i)); + } + m_pointCount = newPointCount; return this; } @@ -602,6 +622,10 @@ QVector QQuickPointerMouseEvent::grabbers() const return result; } +void QQuickPointerMouseEvent::clearGrabbers() const { + m_mousePoint->setGrabber(nullptr); +} + bool QQuickPointerTouchEvent::allPointsAccepted() const { for (int i = 0; i < m_pointCount; ++i) { if (!m_touchPoints.at(i)->isAccepted()) @@ -623,6 +647,11 @@ QVector QQuickPointerTouchEvent::grabbers() const return result; } +void QQuickPointerTouchEvent::clearGrabbers() const { + for (int i = 0; i < pointCount(); ++i) + point(i)->setGrabber(nullptr); +} + /*! \internal Populate the reusable synth-mouse event from one touchpoint. diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 63da48e5d0..ef6bba50ed 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -365,6 +365,7 @@ public: // helpers for C++ only (during event delivery) virtual QQuickEventPoint *point(int i) const = 0; virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; virtual QVector grabbers() const = 0; + virtual void clearGrabbers() const = 0; protected: @@ -393,6 +394,7 @@ public: QQuickEventPoint *pointById(quint64 pointId) const override; bool allPointsAccepted() const override; QVector grabbers() const override; + void clearGrabbers() const override; QMouseEvent *asMouseEvent() const; @@ -416,6 +418,7 @@ public: const QTouchEvent::TouchPoint *touchPointById(int pointId) const; bool allPointsAccepted() const override; QVector grabbers() const override; + void clearGrabbers() const override; QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; QTouchEvent *touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const; @@ -507,6 +510,7 @@ public: QQuickPointerEvent *pointerEvent() const { return m_event; } static QQuickPointerDevice *touchDevice(QTouchDevice *d); + static QList touchDevices(); static QQuickPointerDevice *genericMouseDevice(); static QQuickPointerDevice *tabletDevice(qint64); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index a8565be414..05f1eb47fc 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -624,6 +624,7 @@ bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp) bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event) { Q_Q(QQuickWindow); + auto device = QQuickPointerDevice::touchDevice(event->device()); // For each point, check if it is accepted, if not, try the next point. // Any of the fingers can become the mouse one. @@ -642,7 +643,10 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e // accepted. Cannot defer setting the new value because otherwise if the event // handler spins the event loop all subsequent moves and releases get lost. touchMouseId = p.id(); - itemForTouchPointId[touchMouseId] = item; + + // FIXME: this is a bit backwards, should just have the pointer event passed into the function + auto pointerEventPoint = device->pointerEvent()->pointById(touchMouseId); + pointerEventPoint->setGrabber(item); qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item; QScopedPointer mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); @@ -655,9 +659,9 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e event->setAccepted(mousePress->isAccepted()); if (!mousePress->isAccepted()) { touchMouseId = -1; - if (itemForTouchPointId.value(p.id()) == item) { + if (pointerEventPoint->grabber() == item) { qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "disassociated"; - itemForTouchPointId.remove(p.id()); + pointerEventPoint->setGrabber(nullptr); } if (q->mouseGrabberItem() == item) @@ -694,7 +698,8 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e event->setAccepted(me->isAccepted()); if (me->isAccepted()) { qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem; - itemForTouchPointId[p.id()] = q->mouseGrabberItem(); // N.B. the mouseGrabberItem may be different after returning from sendEvent() + auto pointerEventPoint = device->pointerEvent()->pointById(p.id()); + pointerEventPoint->setGrabber(q->mouseGrabberItem()); // N.B. the mouseGrabberItem may be different after returning from sendEvent() return true; } } else { @@ -758,7 +763,12 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) if (grabber && touchMouseId != -1) { // update the touch item for mouse touch id to the new grabber qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); - itemForTouchPointId[touchMouseId] = grabber; + // FIXME: it is unclear which touch device the mouse grab is for + for (QQuickPointerDevice *touchDevice: QQuickPointerDevice::touchDevices()) { + auto point = touchDevice->pointerEvent()->pointById(touchMouseId); + if (point) + point->setGrabber(grabber); + } } if (oldGrabber) { @@ -772,13 +782,19 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector ungrab; for (int i = 0; i < ids.count(); ++i) { - QQuickItem *oldGrabber = itemForTouchPointId.value(ids.at(i)); - if (oldGrabber == grabber) - continue; + // FIXME: deprecate this function, we need a device + for (auto device: QQuickPointerDevice::touchDevices()) { + auto point = device->pointerEvent()->pointById(ids.at(i)); + if (!point) + continue; + QQuickItem *oldGrabber = point->grabber(); + if (oldGrabber == grabber) + continue; - itemForTouchPointId[ids.at(i)] = grabber; - if (oldGrabber) - ungrab.insert(oldGrabber); + point->setGrabber(grabber); + if (oldGrabber) + ungrab.insert(oldGrabber); + } QQuickItem *mouseGrabberItem = q->mouseGrabberItem(); if (touchMouseId == ids.at(i) && mouseGrabberItem && mouseGrabberItem != grabber) { @@ -794,11 +810,14 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to { Q_Q(QQuickWindow); if (Q_LIKELY(touch)) { - QMutableHashIterator itemTouchMapIt(itemForTouchPointId); - while (itemTouchMapIt.hasNext()) { - if (itemTouchMapIt.next().value() == grabber) { - itemTouchMapIt.remove(); - grabber->touchUngrabEvent(); + for (auto device: QQuickPointerDevice::touchDevices()) { + auto pointerEvent = device->pointerEvent(); + for (int i = 0; i < pointerEvent->pointCount(); ++i) { + if (pointerEvent->point(i)->grabber() == grabber) { + pointerEvent->point(i)->setGrabber(nullptr); + // FIXME send ungrab event only once + grabber->touchUngrabEvent(); + } } } } @@ -1864,20 +1883,21 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) { qCDebug(DBG_TOUCH) << event; Q_Q(QQuickWindow); + // A TouchCancel event will typically not contain any points. // Deliver it to all items that have active touches. - QSet cancelDelivered; - foreach (QQuickItem *item, itemForTouchPointId) { - if (cancelDelivered.contains(item)) - continue; - cancelDelivered.insert(item); - q->sendEvent(item, event); + QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent(); + QVector grabbers = pointerEvent->grabbers(); + + for (QQuickItem *grabber: qAsConst(grabbers)) { + q->sendEvent(grabber, event); } touchMouseId = -1; if (q->mouseGrabberItem()) q->mouseGrabberItem()->ungrabMouse(); + // The next touch event can only be a TouchBegin so clean up. - itemForTouchPointId.clear(); + pointerEvent->clearGrabbers(); return true; } @@ -2194,11 +2214,8 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerEvent *event) // are also receiving touch points with some other state. // But we have not yet decided which points go to which item, // so for now we must include all non-new points in updatedPoints. - if (itemForTouchPointId.contains(point->pointId())) { - QQuickItem *item = itemForTouchPointId.value(point->pointId()); - if (item) - updatedPoints[item].append(point); - } + if (QQuickItem *grabber = point->grabber()) + updatedPoints[grabber].append(point); } } @@ -2213,24 +2230,23 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerEvent *event) // Remove released points from itemForTouchPointId bool allReleased = true; for (int i = 0; i < pointCount; ++i) { - const QQuickEventPoint *point = event->point(i); - int id = point->pointId(); + QQuickEventPoint *point = event->point(i); if (point->state() == Qt::TouchPointReleased) { + int id = point->pointId(); qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released"; - itemForTouchPointId.remove(id); - if (id == touchMouseId) + point->setGrabber(nullptr); + if (id == touchMouseId) { touchMouseId = -1; + } touchMouseIdCandidates.remove(id); } else { allReleased = false; } } - if (allReleased && !itemForTouchPointId.isEmpty()) { - qWarning() << "No release received for" << itemForTouchPointId.size() - << "touch points over" << itemForTouchPointId.begin().value() - << "on touch end."; - itemForTouchPointId.clear(); + if (allReleased && !event->grabbers().isEmpty()) { + qWarning() << "No release received for some grabbers" << event->grabbers(); + event->clearGrabbers(); } } @@ -2346,7 +2362,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ // Since it can change in sendEvent, update itemForTouchPointId now foreach (int id, matchingNewPoints) { qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "->" << item; - itemForTouchPointId[id] = item; + event->pointById(id)->setGrabber(item); } // Deliver the touch event to the given item @@ -2371,9 +2387,9 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ // But if the event was not accepted then we know this item // will not be interested in further updates for those touchpoint IDs either. foreach (int id, matchingNewPoints) - if (itemForTouchPointId[id] == item) { + if (event->pointById(id)->grabber() == item) { qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "disassociated"; - itemForTouchPointId.remove(id); + event->pointById(id)->setGrabber(nullptr); } } @@ -2675,8 +2691,9 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target; if (t != QEvent::MouseButtonRelease) { qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; - itemForTouchPointId[tp.id()] = target; touchMouseId = tp.id(); + auto touchMouseDevice = QQuickPointerDevice::touchDevice(event->device()); + touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target); target->grabMouse(); } touchMouseIdCandidates.clear(); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 0579295fca..62d0070593 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -266,8 +266,6 @@ public: QOpenGLVertexArrayObjectHelper *vaoHelper; - // Keeps track of which touch point (int) was last accepted by which item - QHash itemForTouchPointId; QSet touchMouseIdCandidates; mutable QQuickWindowIncubationController *incubationController; diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index dc70081f09..24682bb7a3 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -33,12 +33,12 @@ #include #include +#include #include #include #include #include - -#include +#include #include #include @@ -572,7 +572,8 @@ void tst_TouchMouse::buttonOnFlickable() QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); QCOMPARE(windowPriv->touchMouseId, 0); - QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1); + auto pointerEvent = QQuickPointerDevice::touchDevices().at(0)->pointerEvent(); + QCOMPARE(pointerEvent->point(0)->grabber(), eventItem1); QCOMPARE(window->mouseGrabberItem(), eventItem1); p1 += QPoint(0, -10); @@ -593,7 +594,7 @@ void tst_TouchMouse::buttonOnFlickable() QCOMPARE(window->mouseGrabberItem(), flickable); QCOMPARE(windowPriv->touchMouseId, 0); - QCOMPARE(windowPriv->itemForTouchPointId[0], flickable); + QCOMPARE(pointerEvent->point(0)->grabber(), flickable); QVERIFY(flickable->isMovingVertically()); QTest::touchEvent(window, device).release(0, p3, window); @@ -653,11 +654,12 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress); QCOMPARE(filteredEventList.count(), 1); - // eventItem1 should have the mouse grab, and have moved the itemForTouchPointId + // eventItem1 should have the mouse grab, and have moved the grab // for the touchMouseId to the new grabber. QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); QCOMPARE(windowPriv->touchMouseId, 0); - QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1); + auto pointerEvent = QQuickPointerDevice::touchDevices().at(0)->pointerEvent(); + QCOMPARE(pointerEvent->point(0)->grabber(), eventItem1); QCOMPARE(window->mouseGrabberItem(), eventItem1); p1 += QPoint(0, -10); @@ -676,7 +678,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() // for the touchMouseId to the new grabber. QCOMPARE(window->mouseGrabberItem(), flickable); QCOMPARE(windowPriv->touchMouseId, 0); - QCOMPARE(windowPriv->itemForTouchPointId[0], flickable); + QCOMPARE(pointerEvent->point(0)->grabber(), flickable); QTest::touchEvent(window, device).release(0, p3, window); QQuickTouchUtils::flush(window); @@ -1090,8 +1092,6 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() pinchSequence.move(0, p, window).commit(); QQuickTouchUtils::flush(window); - QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); - qDebug() << "Mouse Grabber: " << window->mouseGrabberItem() << " itemForTouchPointId: " << windowPriv->itemForTouchPointId; QCOMPARE(window->mouseGrabberItem(), flickable); // Add a second finger, this should lead to stealing -- cgit v1.2.3 From e94aa8154b9f0f115dd0df0135ddb67534abc88d Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 20 Jul 2016 15:45:05 +0200 Subject: Remove some unnecessary includes Change-Id: I18f2b6f44decf5ecde45e20722541b9a18d60b16 Reviewed-by: Simon Hausmann --- src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp | 1 - src/qml/qml/qqmlengine.cpp | 1 - src/quick/items/qquickview.cpp | 3 --- src/quick/qtquick2.cpp | 1 - 4 files changed, 6 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp index 11b88cfd34..a803f67380 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp @@ -44,7 +44,6 @@ #include "qsgd3d12shadereffectnode_p.h" #include #include -#include #include #include #include diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 7dac164248..c2eef840ed 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -57,7 +57,6 @@ #include "qqmllist_p.h" #include "qqmltypenamecache_p.h" #include "qqmlnotifier_p.h" -#include #include "qqmlincubator.h" #include "qqmlabstracturlinterceptor.h" #include diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp index 1d89c8bfc2..8b74d26576 100644 --- a/src/quick/items/qquickview.cpp +++ b/src/quick/items/qquickview.cpp @@ -44,9 +44,6 @@ #include "qquickitem_p.h" #include "qquickitemchangelistener_p.h" -#include -#include -#include #include #include diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp index bf5b0083e9..ca12155e63 100644 --- a/src/quick/qtquick2.cpp +++ b/src/quick/qtquick2.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 56c39ee55d534fae6bf1ac9113f8ff15fddf520c Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 20 Jul 2016 15:47:13 +0200 Subject: Don't set QT_QML_DEBUG_NO_WARNING on -no-qml-debug It won't work anyway. Change-Id: I2bc8f6b1b72cf30f0314ad551838db4f04c11297 Reviewed-by: Simon Hausmann --- src/qmltest/qmltest.pro | 2 +- tools/qml/qml.pro | 2 +- tools/qmlscene/qmlscene.pro | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro index 42224e9751..ebbcbcd1eb 100644 --- a/src/qmltest/qmltest.pro +++ b/src/qmltest/qmltest.pro @@ -30,6 +30,6 @@ HEADERS += \ $$PWD/quicktestresult_p.h \ $$PWD/qtestoptions_p.h -DEFINES += QT_QML_DEBUG_NO_WARNING +!contains(QT_CONFIG, no-qml-debug): DEFINES += QT_QML_DEBUG_NO_WARNING load(qt_module) diff --git a/tools/qml/qml.pro b/tools/qml/qml.pro index fe90916980..5f05054d04 100644 --- a/tools/qml/qml.pro +++ b/tools/qml/qml.pro @@ -12,6 +12,6 @@ mac { ICON = qml.icns } -DEFINES += QT_QML_DEBUG_NO_WARNING +!contains(QT_CONFIG, no-qml-debug): DEFINES += QT_QML_DEBUG_NO_WARNING load(qt_tool) diff --git a/tools/qmlscene/qmlscene.pro b/tools/qmlscene/qmlscene.pro index 0411fd8e31..b1267612c5 100644 --- a/tools/qmlscene/qmlscene.pro +++ b/tools/qmlscene/qmlscene.pro @@ -4,6 +4,7 @@ CONFIG += no_import_scan SOURCES += main.cpp -DEFINES += QML_RUNTIME_TESTING QT_QML_DEBUG_NO_WARNING +DEFINES += QML_RUNTIME_TESTING +!contains(QT_CONFIG, no-qml-debug): DEFINES += QT_QML_DEBUG_NO_WARNING load(qt_tool) -- cgit v1.2.3 From 0ed6f80544b577538909587bebed674b4a8a3d3b Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 20 Jul 2016 15:50:16 +0200 Subject: Move QQmlMemoryProfiler to debugger directory Change-Id: Ia1b1038a684f6ec34af777090d4d21021eac01f1 Reviewed-by: Simon Hausmann --- src/qml/debugger/debugger.pri | 2 + src/qml/debugger/qqmlmemoryprofiler.cpp | 157 ++++++++++++++++++++++++++++++++ src/qml/debugger/qqmlmemoryprofiler_p.h | 87 ++++++++++++++++++ src/qml/qml/qml.pri | 2 - src/qml/qml/qqmlmemoryprofiler.cpp | 157 -------------------------------- src/qml/qml/qqmlmemoryprofiler_p.h | 87 ------------------ 6 files changed, 246 insertions(+), 246 deletions(-) create mode 100644 src/qml/debugger/qqmlmemoryprofiler.cpp create mode 100644 src/qml/debugger/qqmlmemoryprofiler_p.h delete mode 100644 src/qml/qml/qqmlmemoryprofiler.cpp delete mode 100644 src/qml/qml/qqmlmemoryprofiler_p.h diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri index 30a44eedd1..c893d7b484 100644 --- a/src/qml/debugger/debugger.pri +++ b/src/qml/debugger/debugger.pri @@ -6,6 +6,7 @@ SOURCES += \ $$PWD/qqmldebugservice.cpp \ $$PWD/qqmldebugserviceinterfaces.cpp \ $$PWD/qqmlabstractprofileradapter.cpp \ + $$PWD/qqmlmemoryprofiler.cpp \ $$PWD/qqmlprofiler.cpp HEADERS += \ @@ -16,6 +17,7 @@ HEADERS += \ $$PWD/qqmldebugserviceinterfaces_p.h \ $$PWD/qqmldebugstatesdelegate_p.h \ $$PWD/qqmldebug.h \ + $$PWD/qqmlmemoryprofiler_p.h \ $$PWD/qqmlprofilerdefinitions_p.h \ $$PWD/qqmlabstractprofileradapter_p.h \ $$PWD/qqmlprofiler_p.h diff --git a/src/qml/debugger/qqmlmemoryprofiler.cpp b/src/qml/debugger/qqmlmemoryprofiler.cpp new file mode 100644 index 0000000000..60f6d96eaf --- /dev/null +++ b/src/qml/debugger/qqmlmemoryprofiler.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qqmlmemoryprofiler_p.h" +#include + +QT_BEGIN_NAMESPACE + +enum LibraryState +{ + Unloaded, + Failed, + Loaded +}; + +static LibraryState state = Unloaded; + +typedef void (qmlmemprofile_stats)(int *allocCount, int *bytesAllocated); +typedef void (qmlmemprofile_clear)(); +typedef void (qmlmemprofile_enable)(); +typedef void (qmlmemprofile_disable)(); +typedef void (qmlmemprofile_push_location)(const char *filename, int lineNumber); +typedef void (qmlmemprofile_pop_location)(); +typedef void (qmlmemprofile_save)(const char *filename); +typedef int (qmlmemprofile_is_enabled)(); + +static qmlmemprofile_stats *memprofile_stats; +static qmlmemprofile_clear *memprofile_clear; +static qmlmemprofile_enable *memprofile_enable; +static qmlmemprofile_disable *memprofile_disable; +static qmlmemprofile_push_location *memprofile_push_location; +static qmlmemprofile_pop_location *memprofile_pop_location; +static qmlmemprofile_save *memprofile_save; +static qmlmemprofile_is_enabled *memprofile_is_enabled; + +#ifndef QT_NO_LIBRARY +extern QFunctionPointer qt_linux_find_symbol_sys(const char *symbol); +#endif + +static bool openLibrary() +{ +#if defined(Q_OS_LINUX) && !defined(QT_NO_LIBRARY) + if (state == Unloaded) { + memprofile_stats = (qmlmemprofile_stats *) qt_linux_find_symbol_sys("qmlmemprofile_stats"); + memprofile_clear = (qmlmemprofile_clear *) qt_linux_find_symbol_sys("qmlmemprofile_clear"); + memprofile_enable = (qmlmemprofile_enable *) qt_linux_find_symbol_sys("qmlmemprofile_enable"); + memprofile_disable = (qmlmemprofile_disable *) qt_linux_find_symbol_sys("qmlmemprofile_disable"); + memprofile_push_location = (qmlmemprofile_push_location *) qt_linux_find_symbol_sys("qmlmemprofile_push_location"); + memprofile_pop_location = (qmlmemprofile_pop_location *) qt_linux_find_symbol_sys("qmlmemprofile_pop_location"); + memprofile_save = (qmlmemprofile_save *) qt_linux_find_symbol_sys("qmlmemprofile_save"); + memprofile_is_enabled = (qmlmemprofile_is_enabled *) qt_linux_find_symbol_sys("qmlmemprofile_is_enabled"); + + if (memprofile_stats && memprofile_clear && memprofile_enable && memprofile_disable && + memprofile_push_location && memprofile_pop_location && memprofile_save && memprofile_is_enabled) + state = Loaded; + else + state = Failed; + } +#endif // Q_OS_LINUX + + return state == Loaded; +} + +QQmlMemoryScope::QQmlMemoryScope(const QUrl &url) + : QQmlMemoryScope(url.path().toUtf8().constData()) +{ +} + +QQmlMemoryScope::QQmlMemoryScope(const char *string) : pushed(false) +{ + if (openLibrary() && memprofile_is_enabled()) { + memprofile_push_location(string, 0); + pushed = true; + } +} + +QQmlMemoryScope::~QQmlMemoryScope() +{ + if (pushed) + memprofile_pop_location(); +} + +bool QQmlMemoryProfiler::isEnabled() +{ + if (openLibrary()) + return memprofile_is_enabled(); + + return false; +} + +void QQmlMemoryProfiler::enable() +{ + if (openLibrary()) + memprofile_enable(); +} + +void QQmlMemoryProfiler::disable() +{ + if (openLibrary()) + memprofile_disable(); +} + +void QQmlMemoryProfiler::clear() +{ + if (openLibrary()) + memprofile_clear(); +} + +void QQmlMemoryProfiler::stats(int *allocCount, int *bytesAllocated) +{ + if (openLibrary()) + memprofile_stats(allocCount, bytesAllocated); +} + +void QQmlMemoryProfiler::save(const char *filename) +{ + if (openLibrary()) + memprofile_save(filename); +} + +QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlmemoryprofiler_p.h b/src/qml/debugger/qqmlmemoryprofiler_p.h new file mode 100644 index 0000000000..4b0ba823ba --- /dev/null +++ b/src/qml/debugger/qqmlmemoryprofiler_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QQMLMEMORYPROFILER_H +#define QQMLMEMORYPROFILER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QUrl; + +class Q_QML_PRIVATE_EXPORT QQmlMemoryScope +{ +public: + explicit QQmlMemoryScope(const QUrl &url); + explicit QQmlMemoryScope(const char *string); + ~QQmlMemoryScope(); + +private: + bool pushed; +}; + +class Q_QML_PRIVATE_EXPORT QQmlMemoryProfiler +{ +public: + static void enable(); + static void disable(); + static bool isEnabled(); + + static void clear(); + static void stats(int *allocCount, int *bytesAllocated); + static void save(const char *filename); +}; + +#define QML_MEMORY_SCOPE_URL(url) QQmlMemoryScope _qml_memory_scope(url) +#define QML_MEMORY_SCOPE_STRING(s) QQmlMemoryScope _qml_memory_scope(s) + +QT_END_NAMESPACE +#endif // QQMLMEMORYPROFILER_H diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 75e1eb9345..cc394b78cb 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -38,7 +38,6 @@ SOURCES += \ $$PWD/qqmlvaluetypeproxybinding.cpp \ $$PWD/qqmlglobal.cpp \ $$PWD/qqmlfile.cpp \ - $$PWD/qqmlmemoryprofiler.cpp \ $$PWD/qqmlplatform.cpp \ $$PWD/qqmlbinding.cpp \ $$PWD/qqmlabstracturlinterceptor.cpp \ @@ -107,7 +106,6 @@ HEADERS += \ $$PWD/qqmlabstractbinding_p.h \ $$PWD/qqmlvaluetypeproxybinding_p.h \ $$PWD/qqmlfile.h \ - $$PWD/qqmlmemoryprofiler_p.h \ $$PWD/qqmlplatform_p.h \ $$PWD/qqmlbinding_p.h \ $$PWD/qqmlextensionplugin_p.h \ diff --git a/src/qml/qml/qqmlmemoryprofiler.cpp b/src/qml/qml/qqmlmemoryprofiler.cpp deleted file mode 100644 index 60f6d96eaf..0000000000 --- a/src/qml/qml/qqmlmemoryprofiler.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qqmlmemoryprofiler_p.h" -#include - -QT_BEGIN_NAMESPACE - -enum LibraryState -{ - Unloaded, - Failed, - Loaded -}; - -static LibraryState state = Unloaded; - -typedef void (qmlmemprofile_stats)(int *allocCount, int *bytesAllocated); -typedef void (qmlmemprofile_clear)(); -typedef void (qmlmemprofile_enable)(); -typedef void (qmlmemprofile_disable)(); -typedef void (qmlmemprofile_push_location)(const char *filename, int lineNumber); -typedef void (qmlmemprofile_pop_location)(); -typedef void (qmlmemprofile_save)(const char *filename); -typedef int (qmlmemprofile_is_enabled)(); - -static qmlmemprofile_stats *memprofile_stats; -static qmlmemprofile_clear *memprofile_clear; -static qmlmemprofile_enable *memprofile_enable; -static qmlmemprofile_disable *memprofile_disable; -static qmlmemprofile_push_location *memprofile_push_location; -static qmlmemprofile_pop_location *memprofile_pop_location; -static qmlmemprofile_save *memprofile_save; -static qmlmemprofile_is_enabled *memprofile_is_enabled; - -#ifndef QT_NO_LIBRARY -extern QFunctionPointer qt_linux_find_symbol_sys(const char *symbol); -#endif - -static bool openLibrary() -{ -#if defined(Q_OS_LINUX) && !defined(QT_NO_LIBRARY) - if (state == Unloaded) { - memprofile_stats = (qmlmemprofile_stats *) qt_linux_find_symbol_sys("qmlmemprofile_stats"); - memprofile_clear = (qmlmemprofile_clear *) qt_linux_find_symbol_sys("qmlmemprofile_clear"); - memprofile_enable = (qmlmemprofile_enable *) qt_linux_find_symbol_sys("qmlmemprofile_enable"); - memprofile_disable = (qmlmemprofile_disable *) qt_linux_find_symbol_sys("qmlmemprofile_disable"); - memprofile_push_location = (qmlmemprofile_push_location *) qt_linux_find_symbol_sys("qmlmemprofile_push_location"); - memprofile_pop_location = (qmlmemprofile_pop_location *) qt_linux_find_symbol_sys("qmlmemprofile_pop_location"); - memprofile_save = (qmlmemprofile_save *) qt_linux_find_symbol_sys("qmlmemprofile_save"); - memprofile_is_enabled = (qmlmemprofile_is_enabled *) qt_linux_find_symbol_sys("qmlmemprofile_is_enabled"); - - if (memprofile_stats && memprofile_clear && memprofile_enable && memprofile_disable && - memprofile_push_location && memprofile_pop_location && memprofile_save && memprofile_is_enabled) - state = Loaded; - else - state = Failed; - } -#endif // Q_OS_LINUX - - return state == Loaded; -} - -QQmlMemoryScope::QQmlMemoryScope(const QUrl &url) - : QQmlMemoryScope(url.path().toUtf8().constData()) -{ -} - -QQmlMemoryScope::QQmlMemoryScope(const char *string) : pushed(false) -{ - if (openLibrary() && memprofile_is_enabled()) { - memprofile_push_location(string, 0); - pushed = true; - } -} - -QQmlMemoryScope::~QQmlMemoryScope() -{ - if (pushed) - memprofile_pop_location(); -} - -bool QQmlMemoryProfiler::isEnabled() -{ - if (openLibrary()) - return memprofile_is_enabled(); - - return false; -} - -void QQmlMemoryProfiler::enable() -{ - if (openLibrary()) - memprofile_enable(); -} - -void QQmlMemoryProfiler::disable() -{ - if (openLibrary()) - memprofile_disable(); -} - -void QQmlMemoryProfiler::clear() -{ - if (openLibrary()) - memprofile_clear(); -} - -void QQmlMemoryProfiler::stats(int *allocCount, int *bytesAllocated) -{ - if (openLibrary()) - memprofile_stats(allocCount, bytesAllocated); -} - -void QQmlMemoryProfiler::save(const char *filename) -{ - if (openLibrary()) - memprofile_save(filename); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmemoryprofiler_p.h b/src/qml/qml/qqmlmemoryprofiler_p.h deleted file mode 100644 index 4b0ba823ba..0000000000 --- a/src/qml/qml/qqmlmemoryprofiler_p.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#ifndef QQMLMEMORYPROFILER_H -#define QQMLMEMORYPROFILER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -QT_BEGIN_NAMESPACE - -class QUrl; - -class Q_QML_PRIVATE_EXPORT QQmlMemoryScope -{ -public: - explicit QQmlMemoryScope(const QUrl &url); - explicit QQmlMemoryScope(const char *string); - ~QQmlMemoryScope(); - -private: - bool pushed; -}; - -class Q_QML_PRIVATE_EXPORT QQmlMemoryProfiler -{ -public: - static void enable(); - static void disable(); - static bool isEnabled(); - - static void clear(); - static void stats(int *allocCount, int *bytesAllocated); - static void save(const char *filename); -}; - -#define QML_MEMORY_SCOPE_URL(url) QQmlMemoryScope _qml_memory_scope(url) -#define QML_MEMORY_SCOPE_STRING(s) QQmlMemoryScope _qml_memory_scope(s) - -QT_END_NAMESPACE -#endif // QQMLMEMORYPROFILER_H -- cgit v1.2.3 From 9d292aef75cbef15c5506bd69f5e6defa59499b0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 25 Jul 2016 11:57:19 +0200 Subject: Pass QQuickPointerTouchEvent to deliverTouchEvent We cast inside the function anyway, start cleaning it up. Change-Id: I9687919a87c6cb4b6bd0d68471e76df8d1027b97 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 6 +++--- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 05f1eb47fc..81c36a1b75 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2155,7 +2155,7 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) if (QQuickPointerMouseEvent *mouse = event->asPointerMouseEvent()) { deliverMouseEvent(mouse->asMouseEvent()); } else if (event->asPointerTouchEvent()) { - deliverTouchEvent(event); + deliverTouchEvent(event->asPointerTouchEvent()); } else { Q_ASSERT(false); } @@ -2193,9 +2193,9 @@ QVector QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons return targets; } -void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerEvent *event) +void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) { - qCDebug(DBG_TOUCH) << " - delivering" << event->asPointerTouchEvent()->asTouchEvent(); + qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); // List of all items that received an event before // When we have TouchBegin this is and will stay empty diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 62d0070593..8811cd795b 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -164,7 +164,7 @@ public: // delivery of pointer events: QQuickPointerEvent *pointerEventInstance(QEvent *ev); void deliverPointerEvent(QQuickPointerEvent *); - void deliverTouchEvent(QQuickPointerEvent *); + void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); bool deliverPoints(QQuickItem *, QQuickPointerEvent *, const QList &, QSet *, QHash > *, QSet *); -- cgit v1.2.3 From 65156eace79562e8a4043268eb293fd4d1e14476 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 15 Jul 2016 13:34:08 +0200 Subject: Add QQuickPointerEvent::unacceptedPointScenePositions Returns a list of scene points for all points that have not been accepted yet. Change-Id: I218e0c779d236e1b5d7d16fe1537adf454b94035 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 10 ++++++++++ src/quick/items/qquickevents_p_p.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 709ba9e69d..a83f86017f 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -652,6 +652,16 @@ void QQuickPointerTouchEvent::clearGrabbers() const { point(i)->setGrabber(nullptr); } +QVector QQuickPointerEvent::unacceptedPointScenePositions() const +{ + QVector points; + for (int i = 0; i < pointCount(); ++i) { + if (!point(i)->isAccepted()) + points << point(i)->scenePos(); + } + return points; +} + /*! \internal Populate the reusable synth-mouse event from one touchpoint. diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index ef6bba50ed..5c3d6f105a 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -360,6 +360,7 @@ public: // helpers for C++ only (during event delivery) virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } bool isValid() const { return m_event != nullptr; } virtual bool allPointsAccepted() const = 0; + QVector unacceptedPointScenePositions() const; virtual int pointCount() const = 0; virtual QQuickEventPoint *point(int i) const = 0; -- cgit v1.2.3 From 336374ce638fc68ec5946dedf4a3e951a78a3ff0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 25 Jul 2016 13:08:36 +0200 Subject: Add QQuickPointerEvent::unacceptedPressedPointScenePositions Change-Id: Iecbd4e35faf733c6be84f760d4636d772188283c Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 10 ++++++++++ src/quick/items/qquickevents_p_p.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index a83f86017f..c3e2219cf8 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -662,6 +662,16 @@ QVector QQuickPointerEvent::unacceptedPointScenePositions() const return points; } +QVector QQuickPointerEvent::unacceptedPressedPointScenePositions() const +{ + QVector points; + for (int i = 0; i < pointCount(); ++i) { + if (!point(i)->isAccepted() && point(i)->state() == Qt::TouchPointPressed) + points << point(i)->scenePos(); + } + return points; +} + /*! \internal Populate the reusable synth-mouse event from one touchpoint. diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 5c3d6f105a..82f58efb7a 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -361,6 +361,7 @@ public: // helpers for C++ only (during event delivery) bool isValid() const { return m_event != nullptr; } virtual bool allPointsAccepted() const = 0; QVector unacceptedPointScenePositions() const; + QVector unacceptedPressedPointScenePositions() const; virtual int pointCount() const = 0; virtual QQuickEventPoint *point(int i) const = 0; -- cgit v1.2.3 From 299de54d06b4d939c59a0afa8faba32962a6b63b Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 26 Jul 2016 13:11:12 +0200 Subject: Minor cleanup: pass through pointerTouchEvent Change-Id: I78d6576e43480e9c63f2c2983be662c290acb922 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 10 ++++------ src/quick/items/qquickwindow_p.h | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 81c36a1b75..8a6664d5c3 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2251,7 +2251,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) } // This function recurses and sends the events to the individual items -bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerEvent *event, const QList &newPoints, +bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEvent *event, const QList &newPoints, QSet *acceptedNewPoints, QHash > *updatedPoints, QSet *hasFiltered) { @@ -2335,13 +2335,11 @@ bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerEvent *ev // only the points that are relevant for this item. Thus the need for // matchingPoints to already be that set of interesting points. // They are all pre-transformed, too. -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerEvent *event, QSet *acceptedNewPoints, +bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, const QList &matchingPoints, QSet *hasFiltered) { - auto pointerTouchEvent = event->asPointerTouchEvent(); - Q_ASSERT(pointerTouchEvent); - QScopedPointer touchEvent(pointerTouchEvent->touchEventForItem(matchingPoints, item)); + QScopedPointer touchEvent(event->touchEventForItem(matchingPoints, item)); if (touchEvent.data()->touchPoints().isEmpty()) return false; bool touchEventAccepted = false; @@ -2350,7 +2348,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ // First check whether the parent wants to be a filter, // and if the parent accepts the event we are done. - if (sendFilteredTouchEvent(item->parentItem(), item, pointerTouchEvent->asTouchEvent(), hasFiltered)) { + if (sendFilteredTouchEvent(item->parentItem(), item, event->asTouchEvent(), hasFiltered)) { // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 8811cd795b..3f4fc50e31 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -166,9 +166,9 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPoints(QQuickItem *, QQuickPointerEvent *, const QList &, + bool deliverPoints(QQuickItem *, QQuickPointerTouchEvent *, const QList &, QSet *, QHash > *, QSet *); - bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, const QList &matchingPoints, QSet *filtered); + bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, const QList &matchingPoints, QSet *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); -- cgit v1.2.3 From 1c5737d3cb0502f551f7108feda517b153572954 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 26 Jul 2016 09:43:49 +0200 Subject: Mention Q_FLAG in C++ <=> QML type conversion documentation For a user visiting doc.qt.io/qt-5/qtqml-cppintegration-data.html, it's very difficult to know how to use an enum as flags in QML, because it's not mentioned anywhere on this page. Change-Id: I35065bff825b8aebab1477ec883d17cbab92b3ba Reviewed-by: Frederik Gladhorn --- src/qml/doc/src/cppintegration/data.qdoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index e153ca3d8b..ff30bffa83 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -375,6 +375,8 @@ Message { } \endqml +To use an enum as a \l {QFlags}{flags} type in QML, see \l Q_FLAG(). + \note The names of enum values must begin with a capital letter in order to be accessible from QML. -- cgit v1.2.3 From a05f59388e004160899d5d311884cfabe10c3b8c Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Fri, 1 Jul 2016 16:32:20 +0200 Subject: Fix documentation of Component.createObject The parent argument should really be any QObject, not just Items. Change-Id: I4d105722b9d76585dd353eddf464a1ec39fea75e Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlcomponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 24abf52e38..9d56ea50de 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1145,7 +1145,7 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent) } /*! - \qmlmethod object Component::createObject(Item parent, object properties) + \qmlmethod object Component::createObject(QtObject parent, object properties) Creates and returns an object instance of this component that will have the given \a parent and \a properties. The \a properties argument is optional. -- cgit v1.2.3 From 2aa10c778333908b3cbb5505e45b1d2fdb712529 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 26 Jul 2016 20:56:22 +0200 Subject: Accept individual touch points Change-Id: I6071a72645c528b00b1794fe5b66ce9cedaf15b8 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 8a6664d5c3..baf0e196dd 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2352,8 +2352,10 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; - foreach (int id, matchingNewPoints) + foreach (int id, matchingNewPoints) { + event->pointById(id)->setAccepted(); acceptedNewPoints->insert(id); + } return true; } @@ -2379,8 +2381,10 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ if (touchEventAccepted) { // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints. - foreach (int id, matchingNewPoints) + foreach (int id, matchingNewPoints) { + event->pointById(id)->setAccepted(); acceptedNewPoints->insert(id); + } } else { // But if the event was not accepted then we know this item // will not be interested in further updates for those touchpoint IDs either. -- cgit v1.2.3 From b3324d6285204661d16fd39483758b4076104d83 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 26 Jul 2016 21:27:00 +0200 Subject: Let clear grabbers remove grab from all points There is no harm in clearing the cached points. Even though re-adding points from the cache is also resetting the grab to nullptr. Change-Id: Ia2e0b47199eaee0819bc6baba69bf0436dd2a768 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index c3e2219cf8..4a531b09ba 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -648,8 +648,8 @@ QVector QQuickPointerTouchEvent::grabbers() const } void QQuickPointerTouchEvent::clearGrabbers() const { - for (int i = 0; i < pointCount(); ++i) - point(i)->setGrabber(nullptr); + for (auto point: m_touchPoints) + point->setGrabber(nullptr); } QVector QQuickPointerEvent::unacceptedPointScenePositions() const -- cgit v1.2.3 From ba201c73edb4ff452bfb13fa5afd6af6a02c4af0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 26 Jul 2016 20:56:56 +0200 Subject: Touch handling: remove acceptedNewPoints We keep track of the state inside the individual points, simplify the code. Change-Id: I6716f3ad9bc21ab066888a3b373719c5e4f30af2 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 30 ++++++++++++++++------- src/quick/items/qquickevents_p_p.h | 2 +- src/quick/items/qquickwindow.cpp | 49 +++++++++----------------------------- src/quick/items/qquickwindow_p.h | 2 +- 4 files changed, 35 insertions(+), 48 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 4a531b09ba..393d8345ec 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -764,29 +764,43 @@ const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int point /*! \internal Make a new QTouchEvent, giving it a subset of the original touch points. + + Returns a nullptr if all points are stationary or there are no points inside the item. */ -QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const +QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item) const { QList touchPoints; Qt::TouchPointStates eventStates; // TODO maybe add QQuickItem::mapVector2DFromScene(QVector2D) to avoid needing QQuickItemPrivate here // Or else just document that velocity is always scene-relative and is not scaled and rotated with the item // but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity - QMatrix4x4 transformMatrix(QQuickItemPrivate::get(relativeTo)->windowToItemTransform()); - for (const QQuickEventPoint * p : newPoints) { + + QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform()); + for (int i = 0; i < m_pointCount; ++i) { + auto p = m_touchPoints.at(i); + if (p->isAccepted()) + continue; + bool isGrabber = p->grabber() == item; + bool isPressInside = p->state() == Qt::TouchPointPressed && item->contains(item->mapFromScene(p->scenePos())); + if (!(isGrabber || isPressInside)) + continue; + const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId()); if (tp) { eventStates |= tp->state(); QTouchEvent::TouchPoint tpCopy = *tp; - tpCopy.setPos(relativeTo->mapFromScene(tpCopy.scenePos())); - tpCopy.setLastPos(relativeTo->mapFromScene(tpCopy.lastScenePos())); - tpCopy.setStartPos(relativeTo->mapFromScene(tpCopy.startScenePos())); - tpCopy.setRect(relativeTo->mapRectFromScene(tpCopy.sceneRect())); + tpCopy.setPos(item->mapFromScene(tpCopy.scenePos())); + tpCopy.setLastPos(item->mapFromScene(tpCopy.lastScenePos())); + tpCopy.setStartPos(item->mapFromScene(tpCopy.startScenePos())); + tpCopy.setRect(item->mapRectFromScene(tpCopy.sceneRect())); tpCopy.setVelocity(transformMatrix.mapVector(tpCopy.velocity()).toVector2D()); touchPoints << tpCopy; } } + if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty()) + return nullptr; + // if all points have the same state, set the event type accordingly const QTouchEvent &event = *asTouchEvent(); QEvent::Type eventType = event.type(); @@ -804,7 +818,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(const QListsetWindow(event.window()); - touchEvent->setTarget(relativeTo); + touchEvent->setTarget(item); touchEvent->setDevice(event.device()); touchEvent->setModifiers(event.modifiers()); touchEvent->setTouchPoints(touchPoints); diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 82f58efb7a..2375ae797b 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -423,7 +423,7 @@ public: void clearGrabbers() const override; QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; - QTouchEvent *touchEventForItem(const QList &newPoints, QQuickItem *relativeTo) const; + QTouchEvent *touchEventForItem(QQuickItem *item) const; QTouchEvent *asTouchEvent() const; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index baf0e196dd..805011ca41 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2285,44 +2285,22 @@ bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEven // set of IDs of "interesting" new points QSet matchingNewPoints; - // set of points which this item has previously accepted, for starters - QList matchingPoints = (*updatedPoints)[item]; // now add the new points which are inside this item's bounds if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) { for (int i = 0; i < newPoints.count(); i++) { const QQuickEventPoint * point = newPoints[i]; - if (acceptedNewPoints->contains(point->pointId())) + if (acceptedNewPoints->contains(point->pointId())) { + Q_ASSERT(point->isAccepted()); continue; + } QPointF p = item->mapFromScene(point->scenePos()); - if (item->contains(p)) { + if (item->contains(p)) matchingNewPoints.insert(point->pointId()); - matchingPoints << point; - } - } - } - // If there are no matching new points, and the existing points are all stationary, - // there's no need to send an event to this item. This is required by a test in - // tst_qquickwindow::touchEvent_basic: - // a single stationary press on an item shouldn't cause an event - if (matchingNewPoints.isEmpty()) { - bool stationaryOnly = true; - - for (const QQuickEventPoint *tp : matchingPoints) { - if (tp->state() != Qt::TouchPointStationary) { - stationaryOnly = false; - break; - } } - - if (stationaryOnly) - matchingPoints.clear(); } - if (!matchingPoints.isEmpty()) { - // Now we know this item might be interested in the event. Copy and send it, but - // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints. - deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints, hasFiltered); - } + // This item might be interested in the event. + deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, hasFiltered); // record the fact that this item has been visited already updatedPoints->remove(item); @@ -2336,15 +2314,15 @@ bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEven // matchingPoints to already be that set of interesting points. // They are all pre-transformed, too. bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *acceptedNewPoints, - const QSet &matchingNewPoints, const QList &matchingPoints, + const QSet &matchingNewPoints, QSet *hasFiltered) { - QScopedPointer touchEvent(event->touchEventForItem(matchingPoints, item)); - if (touchEvent.data()->touchPoints().isEmpty()) + QScopedPointer touchEvent(event->touchEventForItem(item)); + if (!touchEvent) return false; - bool touchEventAccepted = false; qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item; + bool touchEventAccepted = false; // First check whether the parent wants to be a filter, // and if the parent accepts the event we are done. @@ -2359,12 +2337,6 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ return true; } - // Since it can change in sendEvent, update itemForTouchPointId now - foreach (int id, matchingNewPoints) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "->" << item; - event->pointById(id)->setGrabber(item); - } - // Deliver the touch event to the given item qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; QCoreApplication::sendEvent(item, touchEvent.data()); @@ -2383,6 +2355,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ // update acceptedNewPoints. foreach (int id, matchingNewPoints) { event->pointById(id)->setAccepted(); + event->pointById(id)->setGrabber(item); acceptedNewPoints->insert(id); } } else { diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 3f4fc50e31..36414f5b6f 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -168,7 +168,7 @@ public: bool deliverTouchCancelEvent(QTouchEvent *); bool deliverPoints(QQuickItem *, QQuickPointerTouchEvent *, const QList &, QSet *, QHash > *, QSet *); - bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, const QList &matchingPoints, QSet *filtered); + bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, QSet *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); -- cgit v1.2.3 From 6129f48e4ad4dd7341eafaa1212855556b5ee787 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Mon, 25 Jul 2016 17:05:24 +0200 Subject: Software Adaptation: Add threaded render loop This is a new attempt at providing a threaded render loop for the software adaptation in scenegraph. This approach is modeled after the D3D12 backend, and provides a separate thread for each window. To enable use the environment variable QSG_RENDER_LOOP=threaded as with the other backends. Change-Id: I6932975dd9c5579d36f0135aa4626dcc43b45ecd Reviewed-by: Laszlo Agocs --- .../adaptations/software/qsgsoftwareadaptation.cpp | 11 + .../software/qsgsoftwarethreadedrenderloop.cpp | 991 +++++++++++++++++++++ .../software/qsgsoftwarethreadedrenderloop_p.h | 118 +++ .../scenegraph/adaptations/software/software.pri | 6 +- 4 files changed, 1124 insertions(+), 2 deletions(-) create mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp create mode 100644 src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp index 0e2f4f5382..144e75d3e6 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp @@ -40,6 +40,7 @@ #include "qsgsoftwareadaptation_p.h" #include "qsgsoftwarecontext_p.h" #include "qsgsoftwarerenderloop_p.h" +#include "qsgsoftwarethreadedrenderloop_p.h" #include #include @@ -73,6 +74,16 @@ QSGContextFactoryInterface::Flags QSGSoftwareAdaptation::flags(const QString &) QSGRenderLoop *QSGSoftwareAdaptation::createWindowManager() { + static bool threaded = false; + static bool envChecked = false; + if (!envChecked) { + envChecked = true; + threaded = qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("threaded"); + } + + if (threaded) + return new QSGSoftwareThreadedRenderLoop; + return new QSGSoftwareRenderLoop(); } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp new file mode 100644 index 0000000000..5d5485ed8f --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp @@ -0,0 +1,991 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qsgsoftwarethreadedrenderloop_p.h" +#include "qsgsoftwarecontext_p.h" +#include "qsgsoftwarerenderer_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// Passed from the RL to the RT when a window is removed obscured and should be +// removed from the render loop. +const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1); + +// Passed from the RL to RT when GUI has been locked, waiting for sync. +const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2); + +// Passed by the RT to itself to trigger another render pass. This is typically +// a result of QQuickWindow::update(). +const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3); + +// Passed by the RL to the RT to maybe release resource if no windows are +// rendering. +const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4); + +// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called. +const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5); + +// Passed by the window when there is a render job to run. +const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6); + +class QSGSoftwareWindowEvent : public QEvent +{ +public: + QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { } + QQuickWindow *window; +}; + +class QSGSoftwareTryReleaseEvent : public QSGSoftwareWindowEvent +{ +public: + QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy) + : QSGSoftwareWindowEvent(win, WM_TryRelease), destroying(destroy) { } + bool destroying; +}; + +class QSGSoftwareSyncEvent : public QSGSoftwareWindowEvent +{ +public: + QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force) + : QSGSoftwareWindowEvent(c, WM_RequestSync) + , size(c->size()) + , dpr(c->effectiveDevicePixelRatio()) + , syncInExpose(inExpose) + , forceRenderPass(force) { } + QSize size; + float dpr; + bool syncInExpose; + bool forceRenderPass; +}; + +class QSGSoftwareGrabEvent : public QSGSoftwareWindowEvent +{ +public: + QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result) + : QSGSoftwareWindowEvent(c, WM_Grab), image(result) { } + QImage *image; +}; + +class QSGSoftwareJobEvent : public QSGSoftwareWindowEvent +{ +public: + QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob) + : QSGSoftwareWindowEvent(c, WM_PostJob), job(postedJob) { } + ~QSGSoftwareJobEvent() { delete job; } + QRunnable *job; +}; + +class QSGSoftwareEventQueue : public QQueue +{ +public: + void addEvent(QEvent *e) { + mutex.lock(); + enqueue(e); + if (waiting) + condition.wakeOne(); + mutex.unlock(); + } + + QEvent *takeEvent(bool wait) { + mutex.lock(); + if (isEmpty() && wait) { + waiting = true; + condition.wait(&mutex); + waiting = false; + } + QEvent *e = dequeue(); + mutex.unlock(); + return e; + } + + bool hasMoreEvents() { + mutex.lock(); + bool has = !isEmpty(); + mutex.unlock(); + return has; + } + +private: + QMutex mutex; + QWaitCondition condition; + bool waiting = false; +}; + +static inline int qsgrl_animation_interval() +{ + const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0; + return refreshRate < 1 ? 16 : int(1000 / refreshRate); +} + +class QSGSoftwareRenderThread : public QThread +{ + Q_OBJECT +public: + QSGSoftwareRenderThread(QSGSoftwareThreadedRenderLoop *rl, QSGRenderContext *renderContext) + : renderLoop(rl) + { + rc = static_cast(renderContext); + vsyncDelta = qsgrl_animation_interval(); + } + + ~QSGSoftwareRenderThread() + { + delete rc; + } + + bool event(QEvent *e); + void run(); + + void syncAndRender(); + void sync(bool inExpose); + + void requestRepaint() + { + if (sleeping) + stopEventProcessing = true; + if (exposedWindow) + pendingUpdate |= RepaintRequest; + } + + void processEventsAndWaitForMore(); + void processEvents(); + void postEvent(QEvent *e); + + enum UpdateRequest { + SyncRequest = 0x01, + RepaintRequest = 0x02, + ExposeRequest = 0x04 | RepaintRequest | SyncRequest + }; + + QSGSoftwareThreadedRenderLoop *renderLoop; + QSGSoftwareRenderContext *rc; + QAnimationDriver *rtAnim = nullptr; + volatile bool active = false; + uint pendingUpdate = 0; + bool sleeping = false; + bool syncResultedInChanges = false; + float vsyncDelta; + QMutex mutex; + QWaitCondition waitCondition; + QQuickWindow *exposedWindow = nullptr; + QBackingStore *backingStore = nullptr; + bool stopEventProcessing = false; + QSGSoftwareEventQueue eventQueue; + QElapsedTimer renderThrottleTimer; + qint64 syncTime; + qint64 renderTime; + qint64 sinceLastTime; + +public slots: + void onSceneGraphChanged() { + syncResultedInChanges = true; + } +}; + +bool QSGSoftwareRenderThread::event(QEvent *e) +{ + switch ((int)e->type()) { + + case WM_Obscure: + Q_ASSERT(!exposedWindow || exposedWindow == static_cast(e)->window); + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_Obscure" << exposedWindow; + mutex.lock(); + if (exposedWindow) { + QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop(); + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Obscure - window removed"); + exposedWindow = nullptr; + delete backingStore; + backingStore = nullptr; + } + waitCondition.wakeOne(); + mutex.unlock(); + return true; + + case WM_RequestSync: { + QSGSoftwareSyncEvent *wme = static_cast(e); + if (sleeping) + stopEventProcessing = true; + exposedWindow = wme->window; + if (backingStore == nullptr) + backingStore = new QBackingStore(exposedWindow); + if (backingStore->size() != exposedWindow->size()) + backingStore->resize(exposedWindow->size()); + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_RequestSync" << exposedWindow; + pendingUpdate |= SyncRequest; + if (wme->syncInExpose) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - triggered from expose"); + pendingUpdate |= ExposeRequest; + } + if (wme->forceRenderPass) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - repaint regardless"); + pendingUpdate |= RepaintRequest; + } + return true; + } + + case WM_TryRelease: { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease"); + mutex.lock(); + renderLoop->lockedForSync = true; + QSGSoftwareTryReleaseEvent *wme = static_cast(e); + // Only when no windows are exposed anymore or we are shutting down. + if (!exposedWindow || wme->destroying) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - invalidating rc"); + if (wme->window) { + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); + if (wme->destroying) { + // Bye bye nodes... + wd->cleanupNodesOnShutdown(); + } + rc->invalidate(); + QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + if (wme->destroying) + delete wd->animationController; + } + if (wme->destroying) + active = false; + if (sleeping) + stopEventProcessing = true; + } else { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - not releasing because window is still active"); + } + waitCondition.wakeOne(); + renderLoop->lockedForSync = false; + mutex.unlock(); + return true; + } + + case WM_Grab: { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab"); + QSGSoftwareGrabEvent *wme = static_cast(e); + Q_ASSERT(wme->window); + Q_ASSERT(wme->window == exposedWindow || !exposedWindow); + mutex.lock(); + if (wme->window) { + // Grabbing is generally done by rendering a frame and reading the + // color buffer contents back, without presenting, and then + // creating a QImage from the returned data. It is terribly + // inefficient since it involves a full blocking wait for the GPU. + // However, our hands are tied by the existing, synchronous APIs of + // QQuickWindow and such. + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); + auto softwareRenderer = static_cast(wd->renderer); + if (softwareRenderer) + softwareRenderer->setBackingStore(backingStore); + rc->initialize(nullptr); + wd->syncSceneGraph(); + wd->renderSceneGraph(wme->window->size()); + *wme->image = backingStore->handle()->toImage(); + } + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab - waking gui to handle result"); + waitCondition.wakeOne(); + mutex.unlock(); + return true; + } + + case WM_PostJob: { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob"); + QSGSoftwareJobEvent *wme = static_cast(e); + Q_ASSERT(wme->window == exposedWindow); + if (exposedWindow) { + wme->job->run(); + delete wme->job; + wme->job = nullptr; + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob - job done"); + } + return true; + } + + case WM_RequestRepaint: + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestPaint"); + // When GUI posts this event, it is followed by a polishAndSync, so we + // must not exit the event loop yet. + pendingUpdate |= RepaintRequest; + break; + + default: + break; + } + + return QThread::event(e); +} + +void QSGSoftwareRenderThread::postEvent(QEvent *e) +{ + eventQueue.addEvent(e); +} + +void QSGSoftwareRenderThread::processEvents() +{ + while (eventQueue.hasMoreEvents()) { + QEvent *e = eventQueue.takeEvent(false); + event(e); + delete e; + } +} + +void QSGSoftwareRenderThread::processEventsAndWaitForMore() +{ + stopEventProcessing = false; + while (!stopEventProcessing) { + QEvent *e = eventQueue.takeEvent(true); + event(e); + delete e; + } +} + +void QSGSoftwareRenderThread::run() +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run()"); + + rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr); + rtAnim->install(); + + if (QQmlDebugConnector::service()) + QQuickProfiler::registerAnimationCallback(); + + renderThrottleTimer.start(); + + while (active) { + if (exposedWindow) + syncAndRender(); + + processEvents(); + QCoreApplication::processEvents(); + + if (pendingUpdate == 0 || !exposedWindow) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - done drawing, sleep"); + sleeping = true; + processEventsAndWaitForMore(); + sleeping = false; + } + } + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run() exiting"); + + delete rtAnim; + rtAnim = nullptr; + + rc->moveToThread(renderLoop->thread()); + moveToThread(renderLoop->thread()); +} + +void QSGSoftwareRenderThread::sync(bool inExpose) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync"); + + mutex.lock(); + Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked"); + + if (exposedWindow) { + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); + bool hadRenderer = wd->renderer != nullptr; + // If the scene graph was touched since the last sync() make sure it sends the + // changed signal. + if (wd->renderer) + wd->renderer->clearChangedFlag(); + + rc->initialize(nullptr); + wd->syncSceneGraph(); + + if (!hadRenderer && wd->renderer) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - created renderer"); + syncResultedInChanges = true; + connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this, + &QSGSoftwareRenderThread::onSceneGraphChanged, Qt::DirectConnection); + } + + // Process deferred deletes now, directly after the sync as deleteLater + // on the GUI must now also have resulted in SG changes and the delete + // is a safe operation. + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } + + if (!inExpose) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync complete, waking gui"); + waitCondition.wakeOne(); + mutex.unlock(); + } +} + +void QSGSoftwareRenderThread::syncAndRender() +{ + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame); + + QElapsedTimer waitTimer; + waitTimer.start(); + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - syncAndRender()"); + + syncResultedInChanges = false; + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); + + const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage; + const bool syncRequested = pendingUpdate & SyncRequest; + const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest; + pendingUpdate = 0; + + if (syncRequested) + sync(exposeRequested); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + if (!syncResultedInChanges && !repaintRequested) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - no changes, render aborted"); + int waitTime = vsyncDelta - (int) waitTimer.elapsed(); + if (waitTime > 0) + msleep(waitTime); + return; + } + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering started"); + + if (rtAnim->isRunning()) { + wd->animationController->lock(); + rtAnim->advance(); + wd->animationController->unlock(); + } + + bool canRender = wd->renderer != nullptr; + + if (canRender) { + auto softwareRenderer = static_cast(wd->renderer); + if (softwareRenderer) + softwareRenderer->setBackingStore(backingStore); + wd->renderSceneGraph(exposedWindow->size()); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + if (softwareRenderer && (!wd->customRenderStage || !wd->customRenderStage->swap())) + backingStore->flush(softwareRenderer->flushRegion()); + + // Since there is no V-Sync with QBackingStore, throttle rendering the refresh + // rate of the current screen the window is on. + int blockTime = vsyncDelta - (int) renderThrottleTimer.elapsed(); + if (blockTime > 0) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - blocking for " << blockTime << "ms"; + msleep(blockTime); + } + renderThrottleTimer.restart(); + + wd->fireFrameSwapped(); + } else { + Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1); + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - window not ready, skipping render"); + } + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering done"); + + if (exposeRequested) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - wake gui after initial expose"); + waitCondition.wakeOne(); + mutex.unlock(); + } + + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame); +} + +template T *windowFor(const QVector &list, QQuickWindow *window) +{ + for (const T &t : list) { + if (t.window == window) + return const_cast(&t); + } + return nullptr; +} + + +QSGSoftwareThreadedRenderLoop::QSGSoftwareThreadedRenderLoop() +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop constructor"); + m_sg = new QSGSoftwareContext; + m_anim = m_sg->createAnimationDriver(this); + connect(m_anim, &QAnimationDriver::started, this, &QSGSoftwareThreadedRenderLoop::onAnimationStarted); + connect(m_anim, &QAnimationDriver::stopped, this, &QSGSoftwareThreadedRenderLoop::onAnimationStopped); + m_anim->install(); +} + +QSGSoftwareThreadedRenderLoop::~QSGSoftwareThreadedRenderLoop() +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop destructor"); + delete m_sg; +} + +void QSGSoftwareThreadedRenderLoop::show(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "show" << window; +} + +void QSGSoftwareThreadedRenderLoop::hide(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "hide" << window; + + if (window->isExposed()) + handleObscurity(windowFor(m_windows, window)); + + releaseResources(window); +} + +void QSGSoftwareThreadedRenderLoop::resize(QQuickWindow *window) +{ + if (!window->isExposed() || window->size().isEmpty()) + return; + + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "resize" << window << window->size(); +} + +void QSGSoftwareThreadedRenderLoop::windowDestroyed(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "window destroyed" << window; + + WindowData *w = windowFor(m_windows, window); + if (!w) + return; + + handleObscurity(w); + handleResourceRelease(w, true); + + QSGSoftwareRenderThread *thread = w->thread; + while (thread->isRunning()) + QThread::yieldCurrentThread(); + + Q_ASSERT(thread->thread() == QThread::currentThread()); + delete thread; + + for (int i = 0; i < m_windows.size(); ++i) { + if (m_windows.at(i).window == window) { + m_windows.removeAt(i); + break; + } + } +} + +void QSGSoftwareThreadedRenderLoop::exposureChanged(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "exposure changed" << window; + + if (window->isExposed()) { + handleExposure(window); + } else { + WindowData *w = windowFor(m_windows, window); + if (w) + handleObscurity(w); + } +} + +QImage QSGSoftwareThreadedRenderLoop::grab(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "grab" << window; + + WindowData *w = windowFor(m_windows, window); + // Have to support invisible (but created()'ed) windows as well. + // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible. + const bool tempExpose = !w; + if (tempExpose) { + handleExposure(window); + w = windowFor(m_windows, window); + Q_ASSERT(w); + } + + if (!w->thread->isRunning()) + return QImage(); + + if (!window->handle()) + window->create(); + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->polishItems(); + + QImage result; + w->thread->mutex.lock(); + lockedForSync = true; + w->thread->postEvent(new QSGSoftwareGrabEvent(window, &result)); + w->thread->waitCondition.wait(&w->thread->mutex); + lockedForSync = false; + w->thread->mutex.unlock(); + + result.setDevicePixelRatio(window->effectiveDevicePixelRatio()); + + if (tempExpose) + handleObscurity(w); + + return result; +} + +void QSGSoftwareThreadedRenderLoop::update(QQuickWindow *window) +{ + WindowData *w = windowFor(m_windows, window); + if (!w) + return; + + if (w->thread == QThread::currentThread()) { + w->thread->requestRepaint(); + return; + } + + // We set forceRenderPass because we want to make sure the QQuickWindow + // actually does a full render pass after the next sync. + w->forceRenderPass = true; + scheduleUpdate(w); +} + +void QSGSoftwareThreadedRenderLoop::maybeUpdate(QQuickWindow *window) +{ + WindowData *w = windowFor(m_windows, window); + if (w) + scheduleUpdate(w); +} + +void QSGSoftwareThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleUpdateRequest" << window; + + WindowData *w = windowFor(m_windows, window); + if (w) + polishAndSync(w, false); +} + +QAnimationDriver *QSGSoftwareThreadedRenderLoop::animationDriver() const +{ + return m_anim; +} + +QSGContext *QSGSoftwareThreadedRenderLoop::sceneGraphContext() const +{ + return m_sg; +} + +QSGRenderContext *QSGSoftwareThreadedRenderLoop::createRenderContext(QSGContext *) const +{ + return m_sg->createRenderContext(); +} + +void QSGSoftwareThreadedRenderLoop::releaseResources(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "releaseResources" << window; + + WindowData *w = windowFor(m_windows, window); + if (w) + handleResourceRelease(w, false); +} + +void QSGSoftwareThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job) +{ + WindowData *w = windowFor(m_windows, window); + if (w && w->thread && w->thread->exposedWindow) + w->thread->postEvent(new QSGSoftwareJobEvent(window, job)); + else + delete job; +} + +QSurface::SurfaceType QSGSoftwareThreadedRenderLoop::windowSurfaceType() const +{ + return QSurface::RasterSurface; +} + +bool QSGSoftwareThreadedRenderLoop::interleaveIncubation() const +{ + bool somethingVisible = false; + for (const WindowData &w : m_windows) { + if (w.window->isVisible() && w.window->isExposed()) { + somethingVisible = true; + break; + } + } + return somethingVisible && m_anim->isRunning(); +} + +int QSGSoftwareThreadedRenderLoop::flags() const +{ + return SupportsGrabWithoutExpose; +} + +bool QSGSoftwareThreadedRenderLoop::event(QEvent *e) +{ + if (e->type() == QEvent::Timer) { + QTimerEvent *te = static_cast(e); + if (te->timerId() == animationTimer) { + m_anim->advance(); + emit timeToIncubate(); + return true; + } + } + + return QObject::event(e); +} + +void QSGSoftwareThreadedRenderLoop::onAnimationStarted() +{ + startOrStopAnimationTimer(); + + for (const WindowData &w : qAsConst(m_windows)) + w.window->requestUpdate(); +} + +void QSGSoftwareThreadedRenderLoop::onAnimationStopped() +{ + startOrStopAnimationTimer(); +} + +void QSGSoftwareThreadedRenderLoop::startOrStopAnimationTimer() +{ + int exposedWindowCount = 0; + const WindowData *exposed = nullptr; + + for (int i = 0; i < m_windows.size(); ++i) { + const WindowData &w(m_windows[i]); + if (w.window->isVisible() && w.window->isExposed()) { + ++exposedWindowCount; + exposed = &w; + } + } + + if (animationTimer && (exposedWindowCount == 1 || !m_anim->isRunning())) { + killTimer(animationTimer); + animationTimer = 0; + // If animations are running, make sure we keep on animating + if (m_anim->isRunning()) + exposed->window->requestUpdate(); + } else if (!animationTimer && exposedWindowCount != 1 && m_anim->isRunning()) { + animationTimer = startTimer(qsgrl_animation_interval()); + } +} + +void QSGSoftwareThreadedRenderLoop::handleExposure(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleExposure" << window; + + WindowData *w = windowFor(m_windows, window); + if (!w) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "adding window to list"); + WindowData win; + win.window = window; + QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership + win.thread = new QSGSoftwareRenderThread(this, rc); + win.updateDuringSync = false; + win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt + m_windows.append(win); + w = &m_windows.last(); + } + + // set this early as we'll be rendering shortly anyway and this avoids + // special casing exposure in polishAndSync. + w->thread->exposedWindow = window; + + if (w->window->size().isEmpty() + || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) { +#ifndef QT_NO_DEBUG + qWarning().noquote().nospace() << "QSGSotwareThreadedRenderLoop: expose event received for window " + << w->window << " with invalid geometry: " << w->window->geometry() + << " on " << w->window->screen(); +#endif + } + + if (!w->window->handle()) + w->window->create(); + + // Start render thread if it is not running + if (!w->thread->isRunning()) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "starting render thread"); + // Push a few things to the render thread. + QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController; + if (controller->thread() != w->thread) + controller->moveToThread(w->thread); + if (w->thread->thread() == QThread::currentThread()) { + w->thread->rc->moveToThread(w->thread); + w->thread->moveToThread(w->thread); + } + + w->thread->active = true; + w->thread->start(); + + if (!w->thread->isRunning()) + qFatal("Render thread failed to start, aborting application."); + } + + polishAndSync(w, true); + + startOrStopAnimationTimer(); +} + +void QSGSoftwareThreadedRenderLoop::handleObscurity(QSGSoftwareThreadedRenderLoop::WindowData *w) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleObscurity" << w->window; + + if (w->thread->isRunning()) { + w->thread->mutex.lock(); + w->thread->postEvent(new QSGSoftwareWindowEvent(w->window, WM_Obscure)); + w->thread->waitCondition.wait(&w->thread->mutex); + w->thread->mutex.unlock(); + } + + startOrStopAnimationTimer(); +} + +void QSGSoftwareThreadedRenderLoop::scheduleUpdate(QSGSoftwareThreadedRenderLoop::WindowData *w) +{ + if (!QCoreApplication::instance()) + return; + + if (!w || !w->thread->isRunning()) + return; + + QThread *current = QThread::currentThread(); + if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) { + qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()"; + return; + } + + if (current == w->thread) { + w->updateDuringSync = true; + return; + } + + w->window->requestUpdate(); +} + +void QSGSoftwareThreadedRenderLoop::handleResourceRelease(QSGSoftwareThreadedRenderLoop::WindowData *w, bool destroying) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window; + + w->thread->mutex.lock(); + if (w->thread->isRunning() && w->thread->active) { + QQuickWindow *window = w->window; + + // Note that window->handle() is typically null by this time because + // the platform window is already destroyed. This should not be a + // problem for the D3D cleanup. + + w->thread->postEvent(new QSGSoftwareTryReleaseEvent(window, destroying)); + w->thread->waitCondition.wait(&w->thread->mutex); + + // Avoid a shutdown race condition. + // If SG is invalidated and 'active' becomes false, the thread's run() + // method will exit. handleExposure() relies on QThread::isRunning() (because it + // potentially needs to start the thread again) and our mutex cannot be used to + // track the thread stopping, so we wait a few nanoseconds extra so the thread + // can exit properly. + if (!w->thread->active) + w->thread->wait(); + } + w->thread->mutex.unlock(); +} + +void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop::WindowData *w, bool inExpose) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window; + + QQuickWindow *window = w->window; + if (!w->thread || !w->thread->exposedWindow) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - not exposed, abort"); + return; + } + + // Flush pending touch events. + QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); + // The delivery of the event might have caused the window to stop rendering + w = windowFor(m_windows, window); + if (!w || !w->thread || !w->thread->exposedWindow) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - removed after touch event flushing, abort"); + return; + } + + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync); + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->polishItems(); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + + w->updateDuringSync = false; + + emit window->afterAnimating(); + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - lock for sync"); + w->thread->mutex.lock(); + lockedForSync = true; + w->thread->postEvent(new QSGSoftwareSyncEvent(window, inExpose, w->forceRenderPass)); + w->forceRenderPass = false; + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - wait for sync"); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + w->thread->waitCondition.wait(&w->thread->mutex); + lockedForSync = false; + w->thread->mutex.unlock(); + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - unlock after sync"); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + + if (!animationTimer && m_anim->isRunning()) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - advancing animations"); + m_anim->advance(); + // We need to trigger another sync to keep animations running... + w->window->requestUpdate(); + emit timeToIncubate(); + } else if (w->updateDuringSync) { + w->window->requestUpdate(); + } + + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync); +} + +#include "qsgsoftwarethreadedrenderloop.moc" + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h new file mode 100644 index 0000000000..99993d651c --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARETHREADEDRENDERLOOP_H +#define QSGSOFTWARETHREADEDRENDERLOOP_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QSGSoftwareRenderThread; +class QSGSoftwareContext; + +class QSGSoftwareThreadedRenderLoop : public QSGRenderLoop +{ + Q_OBJECT +public: + QSGSoftwareThreadedRenderLoop(); + ~QSGSoftwareThreadedRenderLoop(); + + void show(QQuickWindow *window) override; + void hide(QQuickWindow *window) override; + void resize(QQuickWindow *window) override; + void windowDestroyed(QQuickWindow *window) override; + void exposureChanged(QQuickWindow *window) override; + QImage grab(QQuickWindow *window) override; + void update(QQuickWindow *window) override; + void maybeUpdate(QQuickWindow *window) override; + void handleUpdateRequest(QQuickWindow *window) override; + QAnimationDriver *animationDriver() const override; + QSGContext *sceneGraphContext() const override; + QSGRenderContext *createRenderContext(QSGContext *) const override; + void releaseResources(QQuickWindow *window) override; + void postJob(QQuickWindow *window, QRunnable *job) override; + QSurface::SurfaceType windowSurfaceType() const override; + bool interleaveIncubation() const override; + int flags() const override; + + bool event(QEvent *e) override; + +public Q_SLOTS: + void onAnimationStarted(); + void onAnimationStopped(); + +private: + struct WindowData { + QQuickWindow *window; + QSGSoftwareRenderThread *thread; + uint updateDuringSync : 1; + uint forceRenderPass : 1; + }; + + void startOrStopAnimationTimer(); + void handleExposure(QQuickWindow *window); + void handleObscurity(WindowData *w); + void scheduleUpdate(WindowData *w); + void handleResourceRelease(WindowData *w, bool destroying); + void polishAndSync(WindowData *w, bool inExpose); + + QSGSoftwareContext *m_sg; + QAnimationDriver *m_anim; + int animationTimer = 0; + bool lockedForSync = false; + QVector m_windows; + + friend class QSGSoftwareRenderThread; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWARETHREADEDRENDERLOOP_H diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri index 1933a37d48..97644fc36a 100644 --- a/src/quick/scenegraph/adaptations/software/software.pri +++ b/src/quick/scenegraph/adaptations/software/software.pri @@ -19,7 +19,8 @@ SOURCES += \ $$PWD/qsgsoftwarerenderloop.cpp \ $$PWD/qsgsoftwarelayer.cpp \ $$PWD/qsgsoftwareadaptation.cpp \ - $$PWD/qsgsoftwarespritenode.cpp + $$PWD/qsgsoftwarespritenode.cpp \ + $$PWD/qsgsoftwarethreadedrenderloop.cpp HEADERS += \ $$PWD/qsgsoftwarecontext_p.h \ @@ -38,4 +39,5 @@ HEADERS += \ $$PWD/qsgsoftwarerenderloop_p.h \ $$PWD/qsgsoftwarelayer_p.h \ $$PWD/qsgsoftwareadaptation_p.h \ - $$PWD/qsgsoftwarespritenode_p.h + $$PWD/qsgsoftwarespritenode_p.h \ + $$PWD/qsgsoftwarethreadedrenderloop_p.h -- cgit v1.2.3 From cab152f331b41a4afd73b715950e22ee36662429 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 20 Jul 2016 10:30:19 +0200 Subject: Keep track of the device which was grabbed as mouse for touch Change-Id: I695e2b4663abea5d227b8d8d9dff8a2342238db9 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 23 ++++++++++++++--------- src/quick/items/qquickwindow_p.h | 1 + 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 805011ca41..cc86ba2fb6 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -478,6 +478,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , dragGrabber(0) #endif , touchMouseId(-1) + , touchMouseDevice(nullptr) , touchMousePressTimestamp(0) , dirtyItemList(0) , devicePixelRatio(0) @@ -643,6 +644,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e // accepted. Cannot defer setting the new value because otherwise if the event // handler spins the event loop all subsequent moves and releases get lost. touchMouseId = p.id(); + touchMouseDevice = device; // FIXME: this is a bit backwards, should just have the pointer event passed into the function auto pointerEventPoint = device->pointerEvent()->pointById(touchMouseId); @@ -659,6 +661,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e event->setAccepted(mousePress->isAccepted()); if (!mousePress->isAccepted()) { touchMouseId = -1; + touchMouseDevice = nullptr; if (pointerEventPoint->grabber() == item) { qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "disassociated"; pointerEventPoint->setGrabber(nullptr); @@ -677,6 +680,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e return true; } else { touchMouseId = -1; + touchMouseDevice = nullptr; } } // The event was accepted, we are done. @@ -690,7 +694,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e // try the next point // Touch point was there before and moved - } else if (p.id() == touchMouseId) { + } else if (touchMouseDevice == device && p.id() == touchMouseId) { if (p.state() & Qt::TouchPointMoved) { if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { QScopedPointer me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem, false)); @@ -724,6 +728,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e } else if (p.state() & Qt::TouchPointReleased) { // currently handled point was released touchMouseId = -1; + touchMouseDevice = nullptr; if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { QScopedPointer me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); @@ -760,15 +765,12 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) Q_ASSERT(event->pointCount() == 1); event->point(0)->setGrabber(grabber); - if (grabber && touchMouseId != -1) { + if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); - // FIXME: it is unclear which touch device the mouse grab is for - for (QQuickPointerDevice *touchDevice: QQuickPointerDevice::touchDevices()) { - auto point = touchDevice->pointerEvent()->pointById(touchMouseId); - if (point) - point->setGrabber(grabber); - } + auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); + if (point) + point->setGrabber(grabber); } if (oldGrabber) { @@ -1893,6 +1895,7 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) q->sendEvent(grabber, event); } touchMouseId = -1; + touchMouseDevice = nullptr; if (q->mouseGrabberItem()) q->mouseGrabberItem()->ungrabMouse(); @@ -2237,6 +2240,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) point->setGrabber(nullptr); if (id == touchMouseId) { touchMouseId = -1; + touchMouseDevice = nullptr; } touchMouseIdCandidates.remove(id); } else { @@ -2630,6 +2634,7 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem if (q->mouseGrabberItem()) { q->mouseGrabberItem()->ungrabMouse(); touchMouseId = -1; + touchMouseDevice = nullptr; } filtered = true; } @@ -2667,7 +2672,7 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem if (t != QEvent::MouseButtonRelease) { qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; touchMouseId = tp.id(); - auto touchMouseDevice = QQuickPointerDevice::touchDevice(event->device()); + touchMouseDevice = QQuickPointerDevice::touchDevice(event->device()); touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target); target->grabMouse(); } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 36414f5b6f..194a4040d9 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -132,6 +132,7 @@ public: QQuickDragGrabber *dragGrabber; #endif int touchMouseId; + QQuickPointerDevice *touchMouseDevice; bool checkIfDoubleClicked(ulong newPressEventTimestamp); ulong touchMousePressTimestamp; -- cgit v1.2.3 From c809e56591862a874a347745e829a05e9036130a Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Wed, 27 Jul 2016 12:17:35 +0200 Subject: Software Adaptation: Fix issue with clip node handling Under certain circumstances the clip node state would not get propagated properly to child nodes. Change-Id: I6f34690c7cc6f2837854fbcaff9aa4d7522e47c3 Reviewed-by: Laszlo Agocs --- .../adaptations/software/qsgsoftwarerenderablenodeupdater.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp index 82f8623b74..12dbf63353 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp @@ -218,12 +218,13 @@ void QSGSoftwareRenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRem m_opacityState.push(state.opacity); m_transformState.push(state.transform); m_clipState.push(state.clip); - + m_hasClip = state.hasClip; } else { // There is no parent, and no previous parent, so likely a root node m_opacityState.push(1.0f); m_transformState.push(QTransform()); m_clipState.push(QRegion()); + m_hasClip = false; } // If the node is being removed, then cleanup the state data -- cgit v1.2.3 From 2190e3771887058587d1b7e0acbdd633aa6f5dad Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 27 Jul 2016 01:00:28 +0200 Subject: MouseArea: press not accepted means not pressed This changes that items that explicitly do not accept a press event, will no longer get a cancel event. [ChangeLog][QtQuick] MouseArea no longer thinks it's pressed when it does not accept the press event. When a press event is not accepted, MouseArea also no longer receives a cancel event. Change-Id: I8419cef60d7cc32aab15e9027fafb73cc08001ba Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickmousearea.cpp | 8 ++++++-- tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 33cc6c9a63..0118d882af 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -405,8 +405,7 @@ bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *i /*! \qmlsignal QtQuick::MouseArea::canceled() - This signal is emitted when mouse events have been canceled, either because an event was not accepted, or - because another item stole the mouse event handling. + This signal is emitted when mouse events have been canceled, because another item stole the mouse event handling. This signal is for advanced use: it is useful when there is more than one MouseArea that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter @@ -1198,6 +1197,11 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS emit mouseXChanged(&me); me.setPosition(d->lastPos); emit mouseYChanged(&me); + + if (!me.isAccepted()) { + d->pressed = Qt::NoButton; + } + if (!oldPressed) { emit pressedChanged(); emit containsPressChanged(); diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 9c627ad69c..0ef17267b7 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -769,7 +769,7 @@ void tst_QQuickMouseArea::onMousePressRejected() QVERIFY(!window.rootObject()->property("mr1_canceled").toBool()); QVERIFY(window.rootObject()->property("mr2_pressed").toBool()); QVERIFY(!window.rootObject()->property("mr2_released").toBool()); - QVERIFY(window.rootObject()->property("mr2_canceled").toBool()); + QVERIFY(!window.rootObject()->property("mr2_canceled").toBool()); QTest::qWait(200); -- cgit v1.2.3 From 5a2ea0c7ee293de0f2a8da424c2890e317924263 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 26 Jul 2016 22:14:44 +0200 Subject: Touch handling: Remove acceptedNewPoints The state is moved into the points, continue cleanup of no longer needed hard to understand code. Change-Id: I1f63f3ac844ac240bc8595bdc936778800726863 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 24 +++++++++--------------- src/quick/items/qquickwindow_p.h | 5 ++--- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index cc86ba2fb6..c0fdc91699 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2225,9 +2225,8 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) // Deliver the event, but only if there is at least one new point // or some item accepted a point and should receive an update if (newPoints.count() > 0 || updatedPoints.count() > 0) { - QSet acceptedNewPoints; QSet hasFiltered; - deliverPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints, &hasFiltered); + deliverPoints(contentItem, event, newPoints, &updatedPoints, &hasFiltered); } // Remove released points from itemForTouchPointId @@ -2256,7 +2255,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) // This function recurses and sends the events to the individual items bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEvent *event, const QList &newPoints, - QSet *acceptedNewPoints, QHash > *updatedPoints, + QHash > *updatedPoints, QSet *hasFiltered) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -2276,7 +2275,7 @@ bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEven QQuickItem *child = children.at(ii); if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled) continue; - if (deliverPoints(child, event, newPoints, acceptedNewPoints, updatedPoints, hasFiltered)) + if (deliverPoints(child, event, newPoints, updatedPoints, hasFiltered)) return true; } @@ -2290,13 +2289,11 @@ bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEven // set of IDs of "interesting" new points QSet matchingNewPoints; // now add the new points which are inside this item's bounds - if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) { + if (newPoints.count() > 0) { for (int i = 0; i < newPoints.count(); i++) { const QQuickEventPoint * point = newPoints[i]; - if (acceptedNewPoints->contains(point->pointId())) { - Q_ASSERT(point->isAccepted()); + if (point->isAccepted()) continue; - } QPointF p = item->mapFromScene(point->scenePos()); if (item->contains(p)) matchingNewPoints.insert(point->pointId()); @@ -2304,20 +2301,20 @@ bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEven } // This item might be interested in the event. - deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, hasFiltered); + deliverMatchingPointsToItem(item, event, matchingNewPoints, hasFiltered); // record the fact that this item has been visited already updatedPoints->remove(item); // recursion is done only if ALL touch points have been delivered - return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty()); + return event->allPointsAccepted(); } // touchEventForItem has no means to generate a touch event that contains // only the points that are relevant for this item. Thus the need for // matchingPoints to already be that set of interesting points. // They are all pre-transformed, too. -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *acceptedNewPoints, +bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, const QSet &matchingNewPoints, QSet *hasFiltered) { @@ -2334,10 +2331,8 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; - foreach (int id, matchingNewPoints) { + foreach (int id, matchingNewPoints) event->pointById(id)->setAccepted(); - acceptedNewPoints->insert(id); - } return true; } @@ -2360,7 +2355,6 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ foreach (int id, matchingNewPoints) { event->pointById(id)->setAccepted(); event->pointById(id)->setGrabber(item); - acceptedNewPoints->insert(id); } } else { // But if the event was not accepted then we know this item diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 194a4040d9..eff23b1b9a 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -167,9 +167,8 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPoints(QQuickItem *, QQuickPointerTouchEvent *, const QList &, - QSet *, QHash > *, QSet *); - bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *acceptedNewPoints, const QSet &matchingNewPoints, QSet *filtered); + bool deliverPoints(QQuickItem *, QQuickPointerTouchEvent *, const QList &, QHash > *, QSet *); + bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, const QSet &matchingNewPoints, QSet *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); -- cgit v1.2.3 From 3934ae38cd22f202508555014bca015ea8c7bf2e Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Jul 2016 17:21:06 +0200 Subject: Remove some dead code Change-Id: I1fa95d3c91e7f3ad5f2bf8f9d732ef72b74a98c8 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlobjectcreator_p.h | 1 - src/quick/util/qquickprofiler_p.h | 5 ----- 2 files changed, 6 deletions(-) diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 2320edf809..4aa3f634ed 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -65,7 +65,6 @@ QT_BEGIN_NAMESPACE class QQmlAbstractBinding; struct QQmlTypeCompiler; class QQmlInstantiationInterrupt; -struct QQmlVmeProfiler; struct QQmlObjectCreatorSharedState : public QSharedData { diff --git a/src/quick/util/qquickprofiler_p.h b/src/quick/util/qquickprofiler_p.h index f1af87f4e6..54d734a1c2 100644 --- a/src/quick/util/qquickprofiler_p.h +++ b/src/quick/util/qquickprofiler_p.h @@ -319,12 +319,7 @@ public: qint64 timestamp() { return m_timer.nsecsElapsed(); } - static quint64 featuresEnabled; - static bool profilingSceneGraph() - { - return featuresEnabled & (1 << QQuickProfiler::ProfileSceneGraph); - } static void initialize(QObject *parent); -- cgit v1.2.3 From ffdf0ed0f85207751eb2f90970dd63eb1b1c58a4 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Jul 2016 17:24:11 +0200 Subject: Don't needlessly look up debug services Change-Id: Idcca82b85e07ca0a8a9a2f370af90403bc4342c7 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlcomponent.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 854090f20b..175b84e7c5 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -874,11 +874,13 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) depthIncreased = false; } - QQmlEngineDebugService *service = QQmlDebugConnector::service(); - if (service && rv) { - if (!context->isInternal) - context->asQQmlContextPrivate()->instances.append(rv); - service->objectCreated(engine, rv); + if (rv) { + if (QQmlEngineDebugService *service = + QQmlDebugConnector::service()) { + if (!context->isInternal) + context->asQQmlContextPrivate()->instances.append(rv); + service->objectCreated(engine, rv); + } } return rv; -- cgit v1.2.3 From 40c16d15156604a6fe4ac214b8c08dcf61ef57e7 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Jul 2016 15:26:01 +0200 Subject: V4: Remove empty file Change-Id: Ibae2334336f5cc4f16cd9c9d8d142ea4b013462b Reviewed-by: Simon Hausmann --- src/qml/jsruntime/jsruntime.pri | 1 - src/qml/jsruntime/qv4debugging.cpp | 60 -------------------------------------- 2 files changed, 61 deletions(-) delete mode 100644 src/qml/jsruntime/qv4debugging.cpp diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 7b58e60b9d..6bd18dabb5 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -6,7 +6,6 @@ SOURCES += \ $$PWD/qv4engine.cpp \ $$PWD/qv4context.cpp \ $$PWD/qv4persistent.cpp \ - $$PWD/qv4debugging.cpp \ $$PWD/qv4lookup.cpp \ $$PWD/qv4identifier.cpp \ $$PWD/qv4identifiertable.cpp \ diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp deleted file mode 100644 index 9fcba64038..0000000000 --- a/src/qml/jsruntime/qv4debugging.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#include "qv4debugging_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4function_p.h" -#include "qv4instr_moth_p.h" -#include "qv4runtime_p.h" -#include "qv4script_p.h" -#include "qv4identifier_p.h" -#include "qv4string_p.h" -#include "qv4objectiterator_p.h" - -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -QT_END_NAMESPACE -- cgit v1.2.3 From 40ada6f8c83a0eada12966623a4019062f3f84be Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Jul 2016 15:19:29 +0200 Subject: QML: Remove QQML_NO_DEBUG_PROTOCOL There is no sane way to set this. Change-Id: I5f7b70d1947d469dd01a1454180f01ad0d1c099d Reviewed-by: Simon Hausmann --- src/qml/debugger/qqmldebug.cpp | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp index 9276bd0544..386fb60b3a 100644 --- a/src/qml/debugger/qqmldebug.cpp +++ b/src/qml/debugger/qqmldebug.cpp @@ -47,15 +47,11 @@ QT_BEGIN_NAMESPACE QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning) { -#ifndef QQML_NO_DEBUG_PROTOCOL if (!QQmlEnginePrivate::qml_debugging_enabled && printWarning) { qDebug("QML debugging is enabled. Only use this in a safe environment."); } QQmlEnginePrivate::qml_debugging_enabled = true; -#else - Q_UNUSED(printWarning); -#endif } /*! @@ -105,11 +101,7 @@ QStringList QQmlDebuggingEnabler::profilerServices() */ void QQmlDebuggingEnabler::setServices(const QStringList &services) { -#ifndef QQML_NO_DEBUG_PROTOCOL QQmlDebugConnector::setServices(services); -#else - Q_UNUSED(services); -#endif } /*! @@ -172,16 +164,9 @@ bool QQmlDebuggingEnabler::connectToLocalDebugger(const QString &socketFileName, bool QQmlDebuggingEnabler::startDebugConnector(const QString &pluginName, const QVariantHash &configuration) { -#ifndef QQML_NO_DEBUG_PROTOCOL QQmlDebugConnector::setPluginKey(pluginName); QQmlDebugConnector *connector = QQmlDebugConnector::instance(); - if (connector) - return connector->open(configuration); -#else - Q_UNUSED(pluginName); - Q_UNUSED(configuration); -#endif - return false; + return connector ? connector->open(configuration) : false; } QT_END_NAMESPACE -- cgit v1.2.3 From b096d9e4e7187a1965bd15d1c5a55f228ec3ae00 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Jul 2016 15:23:53 +0200 Subject: QmlDebug: Inline most of QQmlDebugService The compiler might be able to optimize better if it knows the methods are empty. Change-Id: Ib730e857d1ab948bb2f5c5006f4911d2e6db282d Reviewed-by: Simon Hausmann --- src/qml/debugger/qqmldebugservice.cpp | 30 ------------------------------ src/qml/debugger/qqmldebugservice_p.h | 16 ++++++++-------- 2 files changed, 8 insertions(+), 38 deletions(-) diff --git a/src/qml/debugger/qqmldebugservice.cpp b/src/qml/debugger/qqmldebugservice.cpp index b780735f48..44311de8b5 100644 --- a/src/qml/debugger/qqmldebugservice.cpp +++ b/src/qml/debugger/qqmldebugservice.cpp @@ -176,36 +176,6 @@ const QHash &QQmlDebugService::objectsForIds() return objectReferenceHash()->ids; } -void QQmlDebugService::stateAboutToBeChanged(State) -{ -} - -void QQmlDebugService::stateChanged(State) -{ -} - -void QQmlDebugService::messageReceived(const QByteArray &) -{ -} - -void QQmlDebugService::engineAboutToBeAdded(QJSEngine *engine) -{ - emit attachedToEngine(engine); -} - -void QQmlDebugService::engineAboutToBeRemoved(QJSEngine *engine) -{ - emit detachedFromEngine(engine); -} - -void QQmlDebugService::engineAdded(QJSEngine *) -{ -} - -void QQmlDebugService::engineRemoved(QJSEngine *) -{ -} - QT_END_NAMESPACE #include "qqmldebugservice.moc" diff --git a/src/qml/debugger/qqmldebugservice_p.h b/src/qml/debugger/qqmldebugservice_p.h index 9ddc692ecc..6b4ef38ef1 100644 --- a/src/qml/debugger/qqmldebugservice_p.h +++ b/src/qml/debugger/qqmldebugservice_p.h @@ -65,7 +65,6 @@ class Q_QML_PRIVATE_EXPORT QQmlDebugService : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QQmlDebugService) - Q_DISABLE_COPY(QQmlDebugService) public: ~QQmlDebugService(); @@ -77,14 +76,15 @@ public: State state() const; void setState(State newState); - virtual void stateAboutToBeChanged(State); - virtual void stateChanged(State); - virtual void messageReceived(const QByteArray &); + virtual void stateAboutToBeChanged(State) {} + virtual void stateChanged(State) {} + virtual void messageReceived(const QByteArray &) {} - virtual void engineAboutToBeAdded(QJSEngine *); - virtual void engineAboutToBeRemoved(QJSEngine *); - virtual void engineAdded(QJSEngine *); - virtual void engineRemoved(QJSEngine *); + virtual void engineAboutToBeAdded(QJSEngine *engine) { emit attachedToEngine(engine); } + virtual void engineAboutToBeRemoved(QJSEngine *engine) { emit detachedFromEngine(engine); } + + virtual void engineAdded(QJSEngine *) {} + virtual void engineRemoved(QJSEngine *) {} static const QHash &objectsForIds(); static int idForObject(QObject *); -- cgit v1.2.3 From d4a1b34e335d124ac044a37c43e68565eebf7efc Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 27 Jul 2016 14:39:43 -0700 Subject: tst_qmlproperty: Fix access of dangling pointer const char *name = propertyName.toLatin1().constData(); big no-no... valgrind was quite angry: ==49215== Invalid read of size 1 ==49215== at 0x4C304F2: strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==49215== by 0x5E991C5: QMetaObject::indexOfProperty(char const*) const (qmetaobject.cpp:1007) ==49215== by 0x5ED1273: QObject::property(char const*) const (qobject.cpp:3912) ==49215== by 0x42AAA5: tst_qqmlproperty::floatToStringPrecision() (tst_qqmlproperty.cpp:2082) ==49215== by 0x42BB37: tst_qqmlproperty::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) (tst_qqmlproperty.moc:533) ==49215== by 0x5E9B32D: QMetaMethod::invoke(QObject*, Qt::ConnectionType, QGenericReturnArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument) const (qmetaobject.cpp:2237) ==49215== by 0x40434FB: QMetaMethod::invoke(QObject*, Qt::ConnectionType, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument) const (qmetaobject.h:123) ==49215== by 0x403C39C: QTest::TestMethods::invokeTestOnData(int) const (qtestcase.cpp:838) ==49215== by 0x403CE98: QTest::TestMethods::invokeTest(int, char const*, QTest::WatchDog*) const (qtestcase.cpp:1019) ==49215== by 0x403DF45: QTest::TestMethods::invokeTests(QObject*) const (qtestcase.cpp:1321) ==49215== by 0x403EA9F: QTest::qExec(QObject*, int, char**) (qtestcase.cpp:1733) ==49215== by 0x42B219: main (tst_qqmlproperty.cpp:2104) ==49215== Address 0x155bb3d0 is 16 bytes inside a block of size 18 free'd ==49215== at 0x4C2E38B: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==49215== by 0x5C3F9FC: QArrayData::deallocate(QArrayData*, unsigned long, unsigned long) (qarraydata.cpp:215) ==49215== by 0x42F2CB: QTypedArrayData::deallocate(QArrayData*) (qarraydata.h:459) ==49215== by 0x42CC7A: QByteArray::~QByteArray() (in /home/tjmaciei/obj/qt/qt5/qtdeclarative/tests/auto/qml/qqmlproperty/tst_qqmlproperty) ==49215== by 0x42AA8B: tst_qqmlproperty::floatToStringPrecision() (tst_qqmlproperty.cpp:2081) ==49215== by 0x42BB37: tst_qqmlproperty::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) (tst_qqmlproperty.moc:533) ==49215== by 0x5E9B32D: QMetaMethod::invoke(QObject*, Qt::ConnectionType, QGenericReturnArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument) const (qmetaobject.cpp:2237) ==49215== by 0x40434FB: QMetaMethod::invoke(QObject*, Qt::ConnectionType, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument) const (qmetaobject.h:123) ==49215== by 0x403C39C: QTest::TestMethods::invokeTestOnData(int) const (qtestcase.cpp:838) ==49215== by 0x403CE98: QTest::TestMethods::invokeTest(int, char const*, QTest::WatchDog*) const (qtestcase.cpp:1019) ==49215== by 0x403DF45: QTest::TestMethods::invokeTests(QObject*) const (qtestcase.cpp:1321) ==49215== by 0x403EA9F: QTest::qExec(QObject*, int, char**) (qtestcase.cpp:1733) ==49215== Block was alloc'd at ==49215== at 0x4C2D12F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==49215== by 0x5C3F4DE: allocateData(unsigned long, unsigned int) (qarraydata.cpp:107) ==49215== by 0x5C3F6DE: QArrayData::allocate(QArrayData**, unsigned long, unsigned long, unsigned long, QFlags) (qarraydata.cpp:155) ==49215== by 0x5C47BED: QTypedArrayData::allocate(unsigned long, QFlags) (qarraydata.h:436) ==49215== by 0x5C42123: QByteArray::QByteArray(int, Qt::Initialization) (qbytearray.cpp:1627) ==49215== by 0x5D15F95: QString::toLatin1_helper(QChar const*, int) (qstring.cpp:4635) ==49215== by 0x5D15F5F: QString::toLatin1_helper(QString const&) (qstring.cpp:4630) ==49215== by 0x42D076: QString::toLatin1() const & (qstring.h:521) ==49215== by 0x42AA69: tst_qqmlproperty::floatToStringPrecision() (tst_qqmlproperty.cpp:2081) ==49215== by 0x42BB37: tst_qqmlproperty::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) (tst_qqmlproperty.moc:533) ==49215== by 0x5E9B32D: QMetaMethod::invoke(QObject*, Qt::ConnectionType, QGenericReturnArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument) const (qmetaobject.cpp:2237) ==49215== by 0x40434FB: QMetaMethod::invoke(QObject*, Qt::ConnectionType, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument) const (qmetaobject.h:123) Change-Id: I0031aa609e714ae983c3fffd146543f79048468f Reviewed-by: Simon Hausmann --- tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 5a8f2747a9..e140747881 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -2078,15 +2078,15 @@ void tst_qqmlproperty::floatToStringPrecision() QFETCH(QString, qtString); QFETCH(QString, jsString); - const char *name = propertyName.toLatin1().constData(); + QByteArray name = propertyName.toLatin1(); QCOMPARE(obj->property(name).toDouble(), number); QCOMPARE(obj->property(name).toString(), qtString); - const char *name1 = (propertyName + QLatin1Char('1')).toLatin1().constData(); + QByteArray name1 = (propertyName + QLatin1Char('1')).toLatin1(); QCOMPARE(obj->property(name1).toDouble(), number); QCOMPARE(obj->property(name1).toString(), qtString); - const char *name2 = (propertyName + QLatin1Char('2')).toLatin1().constData(); + QByteArray name2 = (propertyName + QLatin1Char('2')).toLatin1(); QCOMPARE(obj->property(name2).toDouble(), number); QCOMPARE(obj->property(name2).toString(), jsString); -- cgit v1.2.3 From bd48f0e5edb1d5ce10529360fb9d14e7b7135022 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 20 Jul 2016 15:35:50 +0200 Subject: Tooling: Convert connects to Qt5 style Change-Id: I6746b777f73d047f5cf610bfca9b320ac1e13676 Reviewed-by: Simon Hausmann --- .../qmltooling/packetprotocol/qpacketprotocol.cpp | 18 ++--- .../qmltooling/packetprotocol/qpacketprotocol_p.h | 2 +- .../qmldbg_debugger/qqmlenginedebugservice.cpp | 10 ++- .../qmldbg_debugger/qqmlenginedebugservice.h | 10 +-- .../qmltooling/qmldbg_debugger/qqmlwatcher.cpp | 5 +- .../qmltooling/qmldbg_debugger/qv4debugger.cpp | 4 +- .../qmltooling/qmldbg_debugger/qv4debugger.h | 5 +- .../qmltooling/qmldbg_debugger/qv4debuggeragent.h | 1 - .../qmldbg_inspector/globalinspector.cpp | 9 ++- .../qmltooling/qmldbg_inspector/globalinspector.h | 4 +- .../qmltooling/qmldbg_inspector/highlight.cpp | 26 ++++---- .../qmltooling/qmldbg_inspector/highlight.h | 7 +- .../qmldbg_inspector/qqmlinspectorservice.cpp | 14 ++-- .../qmldbg_local/qlocalclientconnection.cpp | 7 +- .../qmldbg_native/qqmlnativedebugconnector.h | 4 +- .../qmldbg_profiler/qqmlprofileradapter.cpp | 27 ++++---- .../qmldbg_profiler/qqmlprofileradapter.h | 1 - .../qmldbg_profiler/qqmlprofilerservice.cpp | 14 ++-- .../qmldbg_profiler/qqmlprofilerservice.h | 4 +- .../qmldbg_profiler/qv4profileradapter.cpp | 37 +++++------ .../qmldbg_profiler/qv4profileradapter.h | 13 ++-- .../qmldbg_quickprofiler/qquickprofileradapter.cpp | 28 ++++---- .../qmldbg_quickprofiler/qquickprofileradapter.h | 2 - .../qmltooling/qmldbg_server/qqmldebugserver.cpp | 76 ++++++++++++---------- .../qmltooling/qmldbg_tcp/qtcpserverconnection.cpp | 7 +- src/plugins/qmltooling/shared/qqmldebugserver.h | 2 +- src/qml/debugger/qqmldebugservice.cpp | 3 +- src/qml/debugger/qqmldebugserviceinterfaces_p.h | 14 ++-- src/qml/debugger/qqmlprofiler_p.h | 3 +- src/qml/jsruntime/qv4profiling_p.h | 1 - src/qmldebug/qqmldebugconnection.cpp | 53 +++++++-------- src/qmldebug/qqmldebugconnection_p.h | 2 +- src/qmldebug/qqmlprofilerclient_p.h | 2 - src/quick/util/qquickprofiler.cpp | 9 ++- src/quick/util/qquickprofiler_p.h | 7 +- tests/auto/qml/debugger/shared/debugutil_p.h | 2 - tools/qmlprofiler/commandlistener.h | 2 +- tools/qmlprofiler/main.cpp | 6 +- tools/qmlprofiler/qmlprofilerapplication.cpp | 28 ++++---- tools/qmlprofiler/qmlprofilerapplication.h | 11 ++-- tools/qmlprofiler/qmlprofilerdata.h | 11 ++-- 41 files changed, 241 insertions(+), 250 deletions(-) diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp index d20ddf9dc0..e541810330 100644 --- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp +++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp @@ -124,12 +124,9 @@ QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent) Q_ASSERT(4 == sizeof(qint32)); Q_ASSERT(dev); - QObject::connect(dev, SIGNAL(readyRead()), - this, SLOT(readyToRead())); - QObject::connect(dev, SIGNAL(aboutToClose()), - this, SLOT(aboutToClose())); - QObject::connect(dev, SIGNAL(bytesWritten(qint64)), - this, SLOT(bytesWritten(qint64))); + QObject::connect(dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead); + QObject::connect(dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose); + QObject::connect(dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten); } /*! @@ -247,12 +244,9 @@ void QPacketProtocol::readyToRead() // Check sizing constraints if (d->inProgressSize > MAX_PACKET_SIZE) { - QObject::disconnect(d->dev, SIGNAL(readyRead()), - this, SLOT(readyToRead())); - QObject::disconnect(d->dev, SIGNAL(aboutToClose()), - this, SLOT(aboutToClose())); - QObject::disconnect(d->dev, SIGNAL(bytesWritten(qint64)), - this, SLOT(bytesWritten(qint64))); + disconnect(d->dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead); + disconnect(d->dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose); + disconnect(d->dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten); d->dev = 0; emit invalidPacket(); return; diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h index 8f95a081e9..7fd722f17f 100644 --- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h +++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h @@ -74,7 +74,7 @@ Q_SIGNALS: void readyRead(); void invalidPacket(); -private Q_SLOTS: +private: void aboutToClose(); void bytesWritten(qint64 bytes); void readyToRead(); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index e49b15218e..12ed987ca0 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -63,8 +63,12 @@ QT_BEGIN_NAMESPACE QQmlEngineDebugServiceImpl::QQmlEngineDebugServiceImpl(QObject *parent) : QQmlEngineDebugService(2, parent), m_watch(new QQmlWatcher(this)), m_statesDelegate(0) { - QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)), - this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant))); + connect(m_watch, &QQmlWatcher::propertyChanged, + this, &QQmlEngineDebugServiceImpl::propertyChanged); + + // Move the message into the correct thread for processing + connect(this, &QQmlEngineDebugServiceImpl::scheduleMessage, + this, &QQmlEngineDebugServiceImpl::processMessage, Qt::QueuedConnection); } QQmlEngineDebugServiceImpl::~QQmlEngineDebugServiceImpl() @@ -420,7 +424,7 @@ QQmlEngineDebugServiceImpl::objectData(QObject *object) void QQmlEngineDebugServiceImpl::messageReceived(const QByteArray &message) { - QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message)); + emit scheduleMessage(message); } /*! diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h index cb75a63850..2e40eb4de8 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h @@ -101,16 +101,18 @@ public: void setStatesDelegate(QQmlDebugStatesDelegate *) Q_DECL_OVERRIDE; +signals: + void scheduleMessage(const QByteArray &); + protected: virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; -private Q_SLOTS: - void processMessage(const QByteArray &msg); - void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); - private: friend class QQmlDebuggerServiceFactory; + void processMessage(const QByteArray &msg); + void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); + void prepareDeferredObjects(QObject *); void buildObjectList(QDataStream &, QQmlContext *, const QList > &instances); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp index 392080dd51..1214212727 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp @@ -69,7 +69,7 @@ public: QQmlWatcher *parent = 0); public slots: - void notifyValueChanged(); + void notifyValueChanged(); // Needs to be a slot because of QQmlPropertyPrivate::connect() private: friend class QQmlWatcher; @@ -88,7 +88,8 @@ QQmlWatchProxy::QQmlWatchProxy(int id, QQmlWatcher *parent) : QObject(parent), m_id(id), m_watch(parent), m_object(0), m_debugId(debugId), m_expr(exp) { - QObject::connect(m_expr, SIGNAL(valueChanged()), this, SLOT(notifyValueChanged())); + QObject::connect(m_expr, &QQmlExpression::valueChanged, + this, &QQmlWatchProxy::notifyValueChanged); } QQmlWatchProxy::QQmlWatchProxy(int id, diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp index 53f2eab5ff..44810dd4cb 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp @@ -79,6 +79,8 @@ QV4Debugger::QV4Debugger(QV4::ExecutionEngine *engine) static int pauseReasonId = qRegisterMetaType(); Q_UNUSED(debuggerId); Q_UNUSED(pauseReasonId); + connect(this, &QV4Debugger::scheduleJob, + this, &QV4Debugger::runJobUnpaused, Qt::QueuedConnection); } QV4::ExecutionEngine *QV4Debugger::engine() const @@ -320,7 +322,7 @@ void QV4Debugger::runInEngine_havingLock(QV4DebugJob *job) if (state() == Paused) m_runningCondition.wakeAll(); else - QMetaObject::invokeMethod(this, "runJobUnpaused", Qt::QueuedConnection); + emit scheduleJob(); m_jobIsRunning.wait(&m_lock); m_runningJob = 0; } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h index 3a5b6080cb..cd412e573d 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h @@ -140,15 +140,14 @@ public: signals: void debuggerPaused(QV4Debugger *self, QV4Debugger::PauseReason reason); - -private slots: - void runJobUnpaused(); + void scheduleJob(); private: // requires lock to be held void pauseAndWait(PauseReason reason); bool reallyHitTheBreakPoint(const QString &filename, int linenr); void runInEngine_havingLock(QV4DebugJob *job); + void runJobUnpaused(); QV4::ExecutionEngine *m_engine; QV4::PersistentValue m_currentContext; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h index 1c7eb50ac7..39ac4d4dcb 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h @@ -72,7 +72,6 @@ public: void setBreakOnThrow(bool onoff); void clearAllPauseRequests(); -public slots: void debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseReason reason); void handleDebuggerDeleted(QObject *debugger); diff --git a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp index 2150b68f32..107d54c626 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp @@ -148,10 +148,6 @@ public: m_component.setData(qml, filename); } -signals: - void result(int requestId, bool success); - -public slots: void tryCreateObject(QQmlComponent::Status status) { switch (status) { @@ -171,7 +167,7 @@ public slots: else emit result(m_requestId, false); } - delete this; + deleteLater(); // The component might send more signals return; } default: @@ -179,6 +175,9 @@ public slots: } } +signals: + void result(int requestId, bool success); + private: QQmlComponent m_component; int m_requestId; diff --git a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h index 7bbe6d6aa2..338eee14c3 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h +++ b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h @@ -72,10 +72,8 @@ public: signals: void messageToClient(const QString &name, const QByteArray &data); -private slots: - void sendResult(int requestId, bool success); - private: + void sendResult(int requestId, bool success); void sendCurrentObjects(const QList &objects); void removeFromSelectedItems(QObject *object); QString titleForItem(QQuickItem *item) const; diff --git a/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp index 26eb0f8ed8..88a6ea6b6d 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp @@ -72,24 +72,22 @@ void Highlight::setItem(QQuickItem *item) m_item->disconnect(this); if (item) { - connect(item, SIGNAL(xChanged()), SLOT(adjust())); - connect(item, SIGNAL(yChanged()), SLOT(adjust())); - connect(item, SIGNAL(widthChanged()), SLOT(adjust())); - connect(item, SIGNAL(heightChanged()), SLOT(adjust())); - connect(item, SIGNAL(rotationChanged()), SLOT(adjust())); - connect(item, SIGNAL(transformOriginChanged(TransformOrigin)), - SLOT(adjust())); + connect(item, &QQuickItem::xChanged, this, &Highlight::adjust); + connect(item, &QQuickItem::yChanged, this, &Highlight::adjust); + connect(item, &QQuickItem::widthChanged, this, &Highlight::adjust); + connect(item, &QQuickItem::heightChanged, this, &Highlight::adjust); + connect(item, &QQuickItem::rotationChanged, this, &Highlight::adjust); + connect(item, &QQuickItem::transformOriginChanged, this, &Highlight::adjust); } QQuickWindow *view = item->window(); QQuickItem * contentItem = view->contentItem(); if (contentItem) { - connect(contentItem, SIGNAL(xChanged()), SLOT(adjust())); - connect(contentItem, SIGNAL(yChanged()), SLOT(adjust())); - connect(contentItem, SIGNAL(widthChanged()), SLOT(adjust())); - connect(contentItem, SIGNAL(heightChanged()), SLOT(adjust())); - connect(contentItem, SIGNAL(rotationChanged()), SLOT(adjust())); - connect(contentItem, SIGNAL(transformOriginChanged(TransformOrigin)), - SLOT(adjust())); + connect(contentItem, &QQuickItem::xChanged, this, &Highlight::adjust); + connect(contentItem, &QQuickItem::yChanged, this, &Highlight::adjust); + connect(contentItem, &QQuickItem::widthChanged, this, &Highlight::adjust); + connect(contentItem, &QQuickItem::heightChanged, this, &Highlight::adjust); + connect(contentItem, &QQuickItem::rotationChanged, this, &Highlight::adjust); + connect(contentItem, &QQuickItem::transformOriginChanged, this, &Highlight::adjust); } m_item = item; setContentsSize(view->size()); diff --git a/src/plugins/qmltooling/qmldbg_inspector/highlight.h b/src/plugins/qmltooling/qmldbg_inspector/highlight.h index 4a85cb4d50..2bf4fc1ad5 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/highlight.h +++ b/src/plugins/qmltooling/qmldbg_inspector/highlight.h @@ -65,8 +65,6 @@ protected: private: void initRenderDetails(); - -private slots: void adjust(); private: @@ -86,13 +84,12 @@ public: void paint(QPainter *painter); void showName(const QPointF &displayPoint); -private slots: - void disableNameDisplay(); - private: QPointF m_displayPoint; QString m_name; bool m_nameDisplayActive; + + void disableNameDisplay(); }; /** diff --git a/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp index 48a3f656b0..ab1aeebf64 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp @@ -55,23 +55,27 @@ public: void setParentWindow(QQuickWindow *window, QWindow *parent) Q_DECL_OVERRIDE; void removeWindow(QQuickWindow *window) Q_DECL_OVERRIDE; +signals: + void scheduleMessage(const QByteArray &message); + protected: virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; -private slots: - void messageFromClient(const QByteArray &message); - private: friend class QQmlInspectorServiceFactory; QmlJSDebugger::GlobalInspector *checkInspector(); QmlJSDebugger::GlobalInspector *m_globalInspector; QHash m_waitingWindows; + + void messageFromClient(const QByteArray &message); }; QQmlInspectorServiceImpl::QQmlInspectorServiceImpl(QObject *parent): QQmlInspectorService(1, parent), m_globalInspector(0) { + connect(this, &QQmlInspectorServiceImpl::scheduleMessage, + this, &QQmlInspectorServiceImpl::messageFromClient, Qt::QueuedConnection); } QmlJSDebugger::GlobalInspector *QQmlInspectorServiceImpl::checkInspector() @@ -122,8 +126,8 @@ void QQmlInspectorServiceImpl::setParentWindow(QQuickWindow *window, QWindow *pa void QQmlInspectorServiceImpl::messageReceived(const QByteArray &message) { - QMetaObject::invokeMethod(this, "messageFromClient", Qt::QueuedConnection, - Q_ARG(QByteArray, message)); + // Move the message to the right thread via queued signal + emit scheduleMessage(message); } void QQmlInspectorServiceImpl::messageFromClient(const QByteArray &message) diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp index 01c24f2395..64b26bdd0d 100644 --- a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp @@ -65,10 +65,8 @@ public: void waitForConnection(); void flush(); -private slots: - void connectionEstablished(); - private: + void connectionEstablished(); bool connectToServer(); bool m_block; @@ -135,7 +133,8 @@ bool QLocalClientConnection::connectToServer() { m_socket = new QLocalSocket; m_socket->setParent(this); - QObject::connect(m_socket, SIGNAL(connected()), this, SLOT(connectionEstablished())); + QObject::connect(m_socket, &QLocalSocket::connected, + this, &QLocalClientConnection::connectionEstablished); m_socket->connectToServer(m_filename); qDebug("QML Debugger: Connecting to socket %s...", m_filename.toLatin1().constData()); return true; diff --git a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h index 03b5b5eb1e..1184925e53 100644 --- a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h +++ b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h @@ -63,11 +63,9 @@ public: bool open(const QVariantHash &configuration) Q_DECL_OVERRIDE; static void setDataStreamVersion(int version); -private slots: +private: void sendMessage(const QString &name, const QByteArray &message); void sendMessages(const QString &name, const QList &messages); - -private: void announceObjectAvailability(const QString &objectType, QObject *object, bool available); QVector m_services; diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp index f161f988de..ea052f0475 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp @@ -49,19 +49,20 @@ QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEngin { setService(service); engine->enableProfiler(); - connect(this, SIGNAL(profilingEnabled(quint64)), engine->profiler, SLOT(startProfiling(quint64))); - connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)), - engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling())); - connect(this, SIGNAL(profilingDisabledWhileWaiting()), - engine->profiler, SLOT(stopProfiling()), Qt::DirectConnection); - connect(this, SIGNAL(dataRequested(bool)), engine->profiler, SLOT(reportData(bool))); - connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), - engine->profiler, SLOT(setTimer(QElapsedTimer))); - connect(engine->profiler, - SIGNAL(dataReady(QVector,QQmlProfiler::LocationHash)), - this, - SLOT(receiveData(QVector,QQmlProfiler::LocationHash))); + connect(this, &QQmlProfilerAdapter::profilingEnabled, + engine->profiler, &QQmlProfiler::startProfiling); + connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, + engine->profiler, &QQmlProfiler::startProfiling, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled, + engine->profiler, &QQmlProfiler::stopProfiling); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting, + engine->profiler, &QQmlProfiler::stopProfiling, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::dataRequested, + engine->profiler, &QQmlProfiler::reportData); + connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown, + engine->profiler, &QQmlProfiler::setTimer); + connect(engine->profiler, &QQmlProfiler::dataReady, + this, &QQmlProfilerAdapter::receiveData); } // convert to QByteArrays that can be sent to the debug client diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h index 96cdcd6d38..1fee5c389f 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h @@ -63,7 +63,6 @@ public: qint64 sendMessages(qint64 until, QList &messages, bool trackLocations) Q_DECL_OVERRIDE; -public slots: void receiveData(const QVector &new_data, const QQmlProfiler::LocationHash &locations); diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index 425543d27a..dba2fd3cc3 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -409,14 +409,16 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message) if (!stream.atEnd()) { stream >> flushInterval; m_flushTimer.setInterval(flushInterval); + auto timerStart = static_cast(&QTimer::start); if (flushInterval > 0) { - connect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush())); - connect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start())); - connect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop())); + connect(&m_flushTimer, &QTimer::timeout, this, &QQmlProfilerServiceImpl::flush); + connect(this, &QQmlProfilerServiceImpl::startFlushTimer, &m_flushTimer, timerStart); + connect(this, &QQmlProfilerServiceImpl::stopFlushTimer, &m_flushTimer, &QTimer::stop); } else { - disconnect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush())); - disconnect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start())); - disconnect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop())); + disconnect(&m_flushTimer, &QTimer::timeout, this, &QQmlProfilerServiceImpl::flush); + disconnect(this, &QQmlProfilerServiceImpl::startFlushTimer, &m_flushTimer, timerStart); + disconnect(this, &QQmlProfilerServiceImpl::stopFlushTimer, + &m_flushTimer, &QTimer::stop); } } if (!stream.atEnd()) diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h index 42efdefd12..bbfc32b681 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h @@ -99,9 +99,6 @@ signals: void startFlushTimer(); void stopFlushTimer(); -private slots: - void flush(); - protected: virtual void stateAboutToBeChanged(State state) Q_DECL_OVERRIDE; virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; @@ -112,6 +109,7 @@ private: void sendMessages(); void addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QJSEngine *engine); void removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler); + void flush(); QElapsedTimer m_timer; QTimer m_flushTimer; diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp index c3bbb86e3a..084ac9e489 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp @@ -47,26 +47,25 @@ QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::Execut { setService(service); engine->enableProfiler(); - connect(this, SIGNAL(profilingEnabled(quint64)), - this, SLOT(forwardEnabled(quint64))); - connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)), - this, SLOT(forwardEnabledWhileWaiting(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(v4ProfilingEnabled(quint64)), - engine->profiler, SLOT(startProfiling(quint64))); - connect(this, SIGNAL(v4ProfilingEnabledWhileWaiting(quint64)), - engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling())); - connect(this, SIGNAL(profilingDisabledWhileWaiting()), engine->profiler, SLOT(stopProfiling()), + connect(this, &QQmlAbstractProfilerAdapter::profilingEnabled, + this, &QV4ProfilerAdapter::forwardEnabled); + connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, + this, &QV4ProfilerAdapter::forwardEnabledWhileWaiting, Qt::DirectConnection); + connect(this, &QV4ProfilerAdapter::v4ProfilingEnabled, + engine->profiler, &QV4::Profiling::Profiler::startProfiling); + connect(this, &QV4ProfilerAdapter::v4ProfilingEnabledWhileWaiting, + engine->profiler, &QV4::Profiling::Profiler::startProfiling, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled, + engine->profiler, &QV4::Profiling::Profiler::stopProfiling); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting, + engine->profiler, &QV4::Profiling::Profiler::stopProfiling, Qt::DirectConnection); - connect(this, SIGNAL(dataRequested(bool)), engine->profiler, SLOT(reportData(bool))); - connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), - engine->profiler, SLOT(setTimer(QElapsedTimer))); - connect(engine->profiler, SIGNAL(dataReady(QV4::Profiling::FunctionLocationHash, - QVector, - QVector)), - this, SLOT(receiveData(QV4::Profiling::FunctionLocationHash, - QVector, - QVector))); + connect(this, &QQmlAbstractProfilerAdapter::dataRequested, + engine->profiler, &QV4::Profiling::Profiler::reportData); + connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown, + engine->profiler, &QV4::Profiling::Profiler::setTimer); + connect(engine->profiler, &QV4::Profiling::Profiler::dataReady, + this, &QV4ProfilerAdapter::receiveData); } qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList &messages, diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h index 13a595f6eb..5d5b83f7ca 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h @@ -70,18 +70,13 @@ public: virtual qint64 sendMessages(qint64 until, QList &messages, bool trackLocations) override; -signals: - void v4ProfilingEnabled(quint64 v4Features); - void v4ProfilingEnabledWhileWaiting(quint64 v4Features); - -public slots: void receiveData(const QV4::Profiling::FunctionLocationHash &, const QVector &, const QVector &); -private slots: - void forwardEnabled(quint64 features); - void forwardEnabledWhileWaiting(quint64 features); +signals: + void v4ProfilingEnabled(quint64 v4Features); + void v4ProfilingEnabledWhileWaiting(quint64 v4Features); private: QV4::Profiling::FunctionLocationHash m_functionLocations; @@ -93,6 +88,8 @@ private: qint64 appendMemoryEvents(qint64 until, QList &messages, QQmlDebugPacket &d); qint64 finalizeMessages(qint64 until, QList &messages, qint64 callNext, QQmlDebugPacket &d); + void forwardEnabled(quint64 features); + void forwardEnabledWhileWaiting(quint64 features); static quint64 translateFeatures(quint64 qmlFeatures); }; diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp index bebf8806c6..0c9fc36463 100644 --- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp @@ -51,20 +51,20 @@ QQuickProfilerAdapter::QQuickProfilerAdapter(QObject *parent) : QQuickProfiler::initialize(this); // We can always do DirectConnection here as all methods are protected by mutexes - connect(this, SIGNAL(profilingEnabled(quint64)), - QQuickProfiler::s_instance, SLOT(startProfilingImpl(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)), - QQuickProfiler::s_instance, SLOT(startProfilingImpl(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), - QQuickProfiler::s_instance, SLOT(setTimer(QElapsedTimer)), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabled()), - QQuickProfiler::s_instance, SLOT(stopProfilingImpl()), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabledWhileWaiting()), - QQuickProfiler::s_instance, SLOT(stopProfilingImpl()), Qt::DirectConnection); - connect(this, SIGNAL(dataRequested(bool)), - QQuickProfiler::s_instance, SLOT(reportDataImpl(bool)), Qt::DirectConnection); - connect(QQuickProfiler::s_instance, SIGNAL(dataReady(QVector)), - this, SLOT(receiveData(QVector)), Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingEnabled, + QQuickProfiler::s_instance, &QQuickProfiler::startProfilingImpl, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, + QQuickProfiler::s_instance, &QQuickProfiler::startProfilingImpl, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown, + QQuickProfiler::s_instance, &QQuickProfiler::setTimer, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled, + QQuickProfiler::s_instance, &QQuickProfiler::stopProfilingImpl, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting, + QQuickProfiler::s_instance, &QQuickProfiler::stopProfilingImpl, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::dataRequested, + QQuickProfiler::s_instance, &QQuickProfiler::reportDataImpl, Qt::DirectConnection); + connect(QQuickProfiler::s_instance, &QQuickProfiler::dataReady, + this, &QQuickProfilerAdapter::receiveData, Qt::DirectConnection); } QQuickProfilerAdapter::~QQuickProfilerAdapter() diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h index f1ba411ac5..1ad020afd6 100644 --- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h +++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h @@ -62,8 +62,6 @@ public: QQuickProfilerAdapter(QObject *parent = 0); ~QQuickProfilerAdapter(); qint64 sendMessages(qint64 until, QList &messages, bool trackLocations) override; - -public slots: void receiveData(const QVector &new_data); private: diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp index cbde86e389..96b3455790 100644 --- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp +++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp @@ -149,15 +149,6 @@ public: static void cleanup(); -private slots: - void wakeEngine(QJSEngine *engine); - void sendMessage(const QString &name, const QByteArray &message); - void sendMessages(const QString &name, const QList &messages); - void changeServiceState(const QString &serviceName, QQmlDebugService::State state); - void removeThread(); - void receiveMessage(); - void invalidPacket(); - private: friend class QQmlDebugServerThread; friend class QQmlDebugServerFactory; @@ -179,6 +170,13 @@ private: bool canSendMessage(const QString &name); void doSendMessage(const QString &name, const QByteArray &message); + void wakeEngine(QJSEngine *engine); + void sendMessage(const QString &name, const QByteArray &message); + void sendMessages(const QString &name, const QList &messages); + void changeServiceState(const QString &serviceName, QQmlDebugService::State state); + void removeThread(); + void receiveMessage(); + void invalidPacket(); QQmlDebugServerConnection *m_connection; QHash m_plugins; @@ -203,18 +201,22 @@ void QQmlDebugServerImpl::cleanup() if (!server) return; - for (QHash::ConstIterator i = server->m_plugins.constBegin(); - i != server->m_plugins.constEnd(); ++i) { - server->m_changeServiceStateCalls.ref(); - QMetaObject::invokeMethod(server, "changeServiceState", Qt::QueuedConnection, - Q_ARG(QString, i.key()), - Q_ARG(QQmlDebugService::State, - QQmlDebugService::NotConnected)); + { + QObject signalSource; + for (QHash::ConstIterator i = server->m_plugins.constBegin(); + i != server->m_plugins.constEnd(); ++i) { + server->m_changeServiceStateCalls.ref(); + QString key = i.key(); + // Process this in the server's thread. + connect(&signalSource, &QObject::destroyed, server, [key, server](){ + server->changeServiceState(key, QQmlDebugService::NotConnected); + }, Qt::QueuedConnection); + } } // Wait for changeServiceState calls to finish // (while running an event loop because some services - // might again use slots to execute stuff in the GUI thread) + // might again defer execution of stuff in the GUI thread) QEventLoop loop; while (!server->m_changeServiceStateCalls.testAndSetOrdered(0, 0)) loop.processEvents(); @@ -293,7 +295,7 @@ QQmlDebugServerImpl::QQmlDebugServerImpl() : // Remove the thread immmediately when it finishes, so that we don't have to wait for the // event loop to signal that. - QObject::connect(&m_thread, SIGNAL(finished()), this, SLOT(removeThread()), + QObject::connect(&m_thread, &QThread::finished, this, &QQmlDebugServerImpl::removeThread, Qt::DirectConnection); m_thread.setObjectName(QStringLiteral("QQmlDebugServerThread")); parseArguments(); @@ -631,15 +633,15 @@ bool QQmlDebugServerImpl::addService(const QString &name, QQmlDebugService *serv if (!service || m_plugins.contains(name)) return false; - connect(service, SIGNAL(messageToClient(QString,QByteArray)), - this, SLOT(sendMessage(QString,QByteArray))); - connect(service, SIGNAL(messagesToClient(QString,QList)), - this, SLOT(sendMessages(QString,QList))); + connect(service, &QQmlDebugService::messageToClient, + this, &QQmlDebugServerImpl::sendMessage); + connect(service, &QQmlDebugService::messagesToClient, + this, &QQmlDebugServerImpl::sendMessages); - connect(service, SIGNAL(attachedToEngine(QJSEngine*)), - this, SLOT(wakeEngine(QJSEngine*)), Qt::QueuedConnection); - connect(service, SIGNAL(detachedFromEngine(QJSEngine*)), - this, SLOT(wakeEngine(QJSEngine*)), Qt::QueuedConnection); + connect(service, &QQmlDebugService::attachedToEngine, + this, &QQmlDebugServerImpl::wakeEngine, Qt::QueuedConnection); + connect(service, &QQmlDebugService::detachedFromEngine, + this, &QQmlDebugServerImpl::wakeEngine, Qt::QueuedConnection); service->setState(QQmlDebugService::Unavailable); m_plugins.insert(name, service); @@ -659,15 +661,15 @@ bool QQmlDebugServerImpl::removeService(const QString &name) m_plugins.remove(name); service->setState(QQmlDebugService::NotConnected); - disconnect(service, SIGNAL(detachedFromEngine(QJSEngine*)), - this, SLOT(wakeEngine(QJSEngine*))); - disconnect(service, SIGNAL(attachedToEngine(QJSEngine*)), - this, SLOT(wakeEngine(QJSEngine*))); + disconnect(service, &QQmlDebugService::detachedFromEngine, + this, &QQmlDebugServerImpl::wakeEngine); + disconnect(service, &QQmlDebugService::attachedToEngine, + this, &QQmlDebugServerImpl::wakeEngine); - disconnect(service, SIGNAL(messagesToClient(QString,QList)), - this, SLOT(sendMessages(QString,QList))); - disconnect(service, SIGNAL(messageToClient(QString,QByteArray)), - this, SLOT(sendMessage(QString,QByteArray))); + disconnect(service, &QQmlDebugService::messagesToClient, + this, &QQmlDebugServerImpl::sendMessages); + disconnect(service, &QQmlDebugService::messageToClient, + this, &QQmlDebugServerImpl::sendMessage); return true; } @@ -738,8 +740,10 @@ void QQmlDebugServerImpl::EngineCondition::wake() void QQmlDebugServerImpl::setDevice(QIODevice *socket) { m_protocol = new QPacketProtocol(socket, this); - QObject::connect(m_protocol, SIGNAL(readyRead()), this, SLOT(receiveMessage())); - QObject::connect(m_protocol, SIGNAL(invalidPacket()), this, SLOT(invalidPacket())); + QObject::connect(m_protocol, &QPacketProtocol::readyRead, + this, &QQmlDebugServerImpl::receiveMessage); + QObject::connect(m_protocol, &QPacketProtocol::invalidPacket, + this, &QQmlDebugServerImpl::invalidPacket); if (blockingMode()) m_protocol->waitForReadyRead(-1); diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp index 3d64312b16..b305c3f535 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp @@ -65,10 +65,8 @@ public: void waitForConnection(); void flush(); -private slots: - void newConnection(); - private: + void newConnection(); bool listen(); int m_portFrom; @@ -152,7 +150,8 @@ void QTcpServerConnection::flush() bool QTcpServerConnection::listen() { m_tcpServer = new QTcpServer(this); - QObject::connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); + QObject::connect(m_tcpServer, &QTcpServer::newConnection, + this, &QTcpServerConnection::newConnection); QHostAddress hostaddress; if (!m_hostaddress.isEmpty()) { if (!hostaddress.setAddress(m_hostaddress)) { diff --git a/src/plugins/qmltooling/shared/qqmldebugserver.h b/src/plugins/qmltooling/shared/qqmldebugserver.h index 424c7c4120..109f1e246c 100644 --- a/src/plugins/qmltooling/shared/qqmldebugserver.h +++ b/src/plugins/qmltooling/shared/qqmldebugserver.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE -class QQmlDebugServer : protected QQmlDebugConnector +class QQmlDebugServer : public QQmlDebugConnector { Q_OBJECT public: diff --git a/src/qml/debugger/qqmldebugservice.cpp b/src/qml/debugger/qqmldebugservice.cpp index 44311de8b5..b576c3bb85 100644 --- a/src/qml/debugger/qqmldebugservice.cpp +++ b/src/qml/debugger/qqmldebugservice.cpp @@ -132,7 +132,6 @@ public: int nextId; -private slots: void remove(QObject *obj); }; } @@ -163,7 +162,7 @@ int QQmlDebugService::idForObject(QObject *object) int id = hash->nextId++; hash->ids.insert(id, object); iter = hash->objects.insert(object, id); - connect(object, SIGNAL(destroyed(QObject*)), hash, SLOT(remove(QObject*))); + connect(object, &QObject::destroyed, hash, &ObjectReferenceHash::remove); } return iter.value(); } diff --git a/src/qml/debugger/qqmldebugserviceinterfaces_p.h b/src/qml/debugger/qqmldebugserviceinterfaces_p.h index 8f66779872..77fde02386 100644 --- a/src/qml/debugger/qqmldebugserviceinterfaces_p.h +++ b/src/qml/debugger/qqmldebugserviceinterfaces_p.h @@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE -class Q_QML_PRIVATE_EXPORT QV4DebugService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QV4DebugService : public QQmlDebugService { Q_OBJECT public: @@ -77,7 +77,7 @@ protected: QQmlDebugService(s_key, version, parent) {} }; -class Q_QML_PRIVATE_EXPORT QQmlProfilerService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QQmlProfilerService : public QQmlDebugService { Q_OBJECT public: @@ -99,7 +99,7 @@ protected: QQmlDebugService(s_key, version, parent) {} }; -class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : public QQmlDebugService { Q_OBJECT public: @@ -119,7 +119,7 @@ protected: class QWindow; class QQuickWindow; -class Q_QML_PRIVATE_EXPORT QQmlInspectorService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QQmlInspectorService : public QQmlDebugService { Q_OBJECT public: @@ -136,7 +136,7 @@ protected: QQmlDebugService(s_key, version, parent) {} }; -class Q_QML_PRIVATE_EXPORT QDebugMessageService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QDebugMessageService : public QQmlDebugService { Q_OBJECT public: @@ -151,7 +151,7 @@ protected: QQmlDebugService(s_key, version, parent) {} }; -class Q_QML_PRIVATE_EXPORT QQmlEngineControlService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QQmlEngineControlService : public QQmlDebugService { Q_OBJECT public: @@ -165,7 +165,7 @@ protected: }; -class Q_QML_PRIVATE_EXPORT QQmlNativeDebugService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QQmlNativeDebugService : public QQmlDebugService { Q_OBJECT diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 707901063c..08606814a9 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -94,7 +94,7 @@ struct Q_AUTOTEST_EXPORT QQmlProfilerData : public QQmlProfilerDefinitions Q_DECLARE_TYPEINFO(QQmlProfilerData, Q_MOVABLE_TYPE); -class QQmlProfiler : public QObject, public QQmlProfilerDefinitions { +class Q_QML_PRIVATE_EXPORT QQmlProfiler : public QObject, public QQmlProfilerDefinitions { Q_OBJECT public: @@ -250,7 +250,6 @@ public: return reinterpret_cast(pointer); } -public slots: void startProfiling(quint64 features); void stopProfiling(); void reportData(bool trackLocations); diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index 01fdf2951e..a422dd0fb6 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -228,7 +228,6 @@ public: quint64 featuresEnabled; -public slots: void stopProfiling(); void startProfiling(quint64 features); void reportData(bool trackLocations); diff --git a/src/qmldebug/qqmldebugconnection.cpp b/src/qmldebug/qqmldebugconnection.cpp index 61f178fd28..b5db71557f 100644 --- a/src/qmldebug/qqmldebugconnection.cpp +++ b/src/qmldebug/qqmldebugconnection.cpp @@ -77,7 +77,7 @@ public: QStringList removedPlugins; void advertisePlugins(); - void connectDeviceSignals(); + void createProtocol(); void flush(); }; @@ -254,7 +254,7 @@ QQmlDebugConnection::QQmlDebugConnection(QObject *parent) : QObject(*(new QQmlDebugConnectionPrivate), parent) { Q_D(QQmlDebugConnection); - connect(&d->handshakeTimer, SIGNAL(timeout()), this, SLOT(handshakeTimeout())); + connect(&d->handshakeTimer, &QTimer::timeout, this, &QQmlDebugConnection::handshakeTimeout); } QQmlDebugConnection::~QQmlDebugConnection() @@ -387,12 +387,12 @@ void QQmlDebugConnection::connectToHost(const QString &hostName, quint16 port) close(); QTcpSocket *socket = new QTcpSocket(this); d->device = socket; - d->connectDeviceSignals(); - connect(socket, SIGNAL(connected()), this, SLOT(socketConnected())); - connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SIGNAL(socketError(QAbstractSocket::SocketError))); - connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), - this, SIGNAL(socketStateChanged(QAbstractSocket::SocketState))); + d->createProtocol(); + connect(socket, &QAbstractSocket::disconnected, this, &QQmlDebugConnection::socketDisconnected); + connect(socket, &QAbstractSocket::connected, this, &QQmlDebugConnection::socketConnected); + connect(socket, static_cast( + &QAbstractSocket::error), this, &QQmlDebugConnection::socketError); + connect(socket, &QAbstractSocket::stateChanged, this, &QQmlDebugConnection::socketStateChanged); socket->connectToHost(hostName, port); } @@ -405,7 +405,8 @@ void QQmlDebugConnection::startLocalServer(const QString &fileName) d->server->deleteLater(); d->server = new QLocalServer(this); // QueuedConnection so that waitForNewConnection() returns true. - connect(d->server, SIGNAL(newConnection()), this, SLOT(newConnection()), Qt::QueuedConnection); + connect(d->server, &QLocalServer::newConnection, + this, &QQmlDebugConnection::newConnection, Qt::QueuedConnection); d->server->listen(fileName); } @@ -415,17 +416,12 @@ class LocalSocketSignalTranslator : public QObject public: LocalSocketSignalTranslator(QLocalSocket *parent) : QObject(parent) { - connect(parent, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)), - this, SLOT(onStateChanged(QLocalSocket::LocalSocketState))); - connect(parent, SIGNAL(error(QLocalSocket::LocalSocketError)), - this, SLOT(onError(QLocalSocket::LocalSocketError))); + connect(parent, &QLocalSocket::stateChanged, + this, &LocalSocketSignalTranslator::onStateChanged); + connect(parent, static_cast( + &QLocalSocket::error), this, &LocalSocketSignalTranslator::onError); } -signals: - void socketError(QAbstractSocket::SocketError error); - void socketStateChanged(QAbstractSocket::SocketState state); - -public slots: void onError(QLocalSocket::LocalSocketError error) { emit socketError(static_cast(error)); @@ -435,6 +431,10 @@ public slots: { emit socketStateChanged(static_cast(state)); } + +signals: + void socketError(QAbstractSocket::SocketError error); + void socketStateChanged(QAbstractSocket::SocketState state); }; void QQmlDebugConnection::newConnection() @@ -444,23 +444,24 @@ void QQmlDebugConnection::newConnection() QLocalSocket *socket = d->server->nextPendingConnection(); d->server->close(); d->device = socket; - d->connectDeviceSignals(); + d->createProtocol(); + connect(socket, &QLocalSocket::disconnected, this, &QQmlDebugConnection::socketDisconnected); LocalSocketSignalTranslator *translator = new LocalSocketSignalTranslator(socket); - QObject::connect(translator, SIGNAL(socketError(QAbstractSocket::SocketError)), - this, SIGNAL(socketError(QAbstractSocket::SocketError))); - QObject::connect(translator, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)), - this, SIGNAL(socketStateChanged(QAbstractSocket::SocketState))); + connect(translator, &LocalSocketSignalTranslator::socketError, + this, &QQmlDebugConnection::socketError); + connect(translator, &LocalSocketSignalTranslator::socketStateChanged, + this, &QQmlDebugConnection::socketStateChanged); socketConnected(); } -void QQmlDebugConnectionPrivate::connectDeviceSignals() +void QQmlDebugConnectionPrivate::createProtocol() { Q_Q(QQmlDebugConnection); delete protocol; protocol = new QPacketProtocol(device, q); - QObject::connect(protocol, SIGNAL(readyRead()), q, SLOT(protocolReadyRead())); - QObject::connect(device, SIGNAL(disconnected()), q, SLOT(socketDisconnected())); + QObject::connect(protocol, &QPacketProtocol::readyRead, + q, &QQmlDebugConnection::protocolReadyRead); } QT_END_NAMESPACE diff --git a/src/qmldebug/qqmldebugconnection_p.h b/src/qmldebug/qqmldebugconnection_p.h index 40753fc998..be425b6cbf 100644 --- a/src/qmldebug/qqmldebugconnection_p.h +++ b/src/qmldebug/qqmldebugconnection_p.h @@ -92,7 +92,7 @@ signals: void socketError(QAbstractSocket::SocketError socketError); void socketStateChanged(QAbstractSocket::SocketState socketState); -private Q_SLOTS: +private: void newConnection(); void socketConnected(); void socketDisconnected(); diff --git a/src/qmldebug/qqmlprofilerclient_p.h b/src/qmldebug/qqmlprofilerclient_p.h index 832c05fef7..b4054ed0d9 100644 --- a/src/qmldebug/qqmlprofilerclient_p.h +++ b/src/qmldebug/qqmlprofilerclient_p.h @@ -67,8 +67,6 @@ class QQmlProfilerClient : public QQmlDebugClient public: QQmlProfilerClient(QQmlDebugConnection *connection); void setFeatures(quint64 features); - -public slots: void sendRecordingStatus(bool record, int engineId = -1, quint32 flushInterval = 0); protected: diff --git a/src/quick/util/qquickprofiler.cpp b/src/quick/util/qquickprofiler.cpp index 659ffe1d84..841a1c9bcf 100644 --- a/src/quick/util/qquickprofiler.cpp +++ b/src/quick/util/qquickprofiler.cpp @@ -70,7 +70,7 @@ void QQuickProfiler::registerAnimationCallback() class CallbackRegistrationHelper : public QObject { Q_OBJECT -public slots: +public: void registerAnimationTimerCallback() { QQuickProfiler::registerAnimationCallback(); @@ -86,7 +86,12 @@ QQuickProfiler::QQuickProfiler(QObject *parent) : QObject(parent) m_timer.start(); CallbackRegistrationHelper *helper = new CallbackRegistrationHelper; // will delete itself helper->moveToThread(QCoreApplication::instance()->thread()); - QMetaObject::invokeMethod(helper, "registerAnimationTimerCallback", Qt::QueuedConnection); + + // Queue the signal to have the animation timer registration run in the right thread; + QObject signalSource; + connect(&signalSource, &QObject::destroyed, + helper, &CallbackRegistrationHelper::registerAnimationTimerCallback, + Qt::QueuedConnection); } QQuickProfiler::~QQuickProfiler() diff --git a/src/quick/util/qquickprofiler_p.h b/src/quick/util/qquickprofiler_p.h index 54d734a1c2..480a2ac3fb 100644 --- a/src/quick/util/qquickprofiler_p.h +++ b/src/quick/util/qquickprofiler_p.h @@ -325,6 +325,9 @@ public: virtual ~QQuickProfiler(); +signals: + void dataReady(const QVector &data); + protected: friend class QQuickProfilerAdapter; @@ -342,10 +345,6 @@ protected: m_data.append(message); } -signals: - void dataReady(const QVector &data); - -protected slots: void startProfilingImpl(quint64 features); void stopProfilingImpl(); void reportDataImpl(bool trackLocations); diff --git a/tests/auto/qml/debugger/shared/debugutil_p.h b/tests/auto/qml/debugger/shared/debugutil_p.h index 6fe5d897e0..1ec0a6513d 100644 --- a/tests/auto/qml/debugger/shared/debugutil_p.h +++ b/tests/auto/qml/debugger/shared/debugutil_p.h @@ -135,8 +135,6 @@ public: int lastResponseId; bool lastResult; -public slots: - void recordResponse(int requestId, bool result) { lastResponseId = requestId; diff --git a/tools/qmlprofiler/commandlistener.h b/tools/qmlprofiler/commandlistener.h index c10b199daa..2a994bf449 100644 --- a/tools/qmlprofiler/commandlistener.h +++ b/tools/qmlprofiler/commandlistener.h @@ -33,7 +33,7 @@ class CommandListener : public QObject { Q_OBJECT -public slots: +public: void readCommand(); signals: diff --git a/tools/qmlprofiler/main.cpp b/tools/qmlprofiler/main.cpp index d3e2beb83f..c7cb979ff8 100644 --- a/tools/qmlprofiler/main.cpp +++ b/tools/qmlprofiler/main.cpp @@ -39,8 +39,10 @@ int main(int argc, char *argv[]) QThread listenerThread; CommandListener listener; listener.moveToThread(&listenerThread); - QObject::connect(&listener, SIGNAL(command(QString)), &app, SLOT(userCommand(QString))); - QObject::connect(&app, SIGNAL(readyForCommand()), &listener, SLOT(readCommand())); + QObject::connect(&listener, &CommandListener::command, + &app, &QmlProfilerApplication::userCommand); + QObject::connect(&app, &QmlProfilerApplication::readyForCommand, + &listener, &CommandListener::readCommand); listenerThread.start(); int exitValue = app.exec(); listenerThread.quit(); diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp index b04ff7e558..063e5e2961 100644 --- a/tools/qmlprofiler/qmlprofilerapplication.cpp +++ b/tools/qmlprofiler/qmlprofilerapplication.cpp @@ -87,17 +87,21 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) : m_connectionAttempts(0) { m_connectTimer.setInterval(1000); - connect(&m_connectTimer, SIGNAL(timeout()), this, SLOT(tryToConnect())); + connect(&m_connectTimer, &QTimer::timeout, this, &QmlProfilerApplication::tryToConnect); - connect(&m_connection, SIGNAL(connected()), this, SLOT(connected())); + connect(&m_connection, &QQmlDebugConnection::connected, + this, &QmlProfilerApplication::connected); - connect(&m_qmlProfilerClient, SIGNAL(enabledChanged(bool)), - this, SLOT(traceClientEnabledChanged(bool))); - connect(&m_qmlProfilerClient, SIGNAL(recordingStarted()), this, SLOT(notifyTraceStarted())); - connect(&m_qmlProfilerClient, SIGNAL(error(QString)), this, SLOT(logError(QString))); + connect(&m_qmlProfilerClient, &QmlProfilerClient::enabledChanged, + this, &QmlProfilerApplication::traceClientEnabledChanged); + connect(&m_qmlProfilerClient, &QmlProfilerClient::recordingStarted, + this, &QmlProfilerApplication::notifyTraceStarted); + connect(&m_qmlProfilerClient, &QmlProfilerClient::error, + this, &QmlProfilerApplication::logError); - connect(&m_profilerData, SIGNAL(error(QString)), this, SLOT(logError(QString))); - connect(&m_profilerData, SIGNAL(dataReady()), this, SLOT(traceFinished())); + connect(&m_profilerData, &QmlProfilerData::error, this, &QmlProfilerApplication::logError); + connect(&m_profilerData, &QmlProfilerData::dataReady, + this, &QmlProfilerApplication::traceFinished); } @@ -257,7 +261,7 @@ void QmlProfilerApplication::parseArguments() int QmlProfilerApplication::exec() { - QTimer::singleShot(0, this, SLOT(run())); + QTimer::singleShot(0, this, &QmlProfilerApplication::run); return QCoreApplication::exec(); } @@ -460,9 +464,9 @@ void QmlProfilerApplication::run() arguments << m_programArguments; m_process->setProcessChannelMode(QProcess::MergedChannels); - connect(m_process, SIGNAL(readyRead()), this, SLOT(processHasOutput())); - connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, - SLOT(processFinished())); + connect(m_process, &QIODevice::readyRead, this, &QmlProfilerApplication::processHasOutput); + connect(m_process, static_cast(&QProcess::finished), + this, [this](int){ processFinished(); }); logStatus(QString("Starting '%1 %2' ...").arg(m_programPath, arguments.join(QLatin1Char(' ')))); m_process->start(m_programPath, arguments); diff --git a/tools/qmlprofiler/qmlprofilerapplication.h b/tools/qmlprofiler/qmlprofilerapplication.h index 04f9d43c87..13f0f041f0 100644 --- a/tools/qmlprofiler/qmlprofilerapplication.h +++ b/tools/qmlprofiler/qmlprofilerapplication.h @@ -58,16 +58,14 @@ public: void parseArguments(); int exec(); bool isInteractive() const; - -signals: - void readyForCommand(); - -public slots: void userCommand(const QString &command); void notifyTraceStarted(); void outputData(); -private slots: +signals: + void readyForCommand(); + +private: void run(); void tryToConnect(); void connected(); @@ -81,7 +79,6 @@ private slots: void logError(const QString &error); void logStatus(const QString &status); -private: quint64 parseFeatures(const QStringList &featureList, const QString &values, bool exclude); bool checkOutputFile(PendingRequest pending); void flush(); diff --git a/tools/qmlprofiler/qmlprofilerdata.h b/tools/qmlprofiler/qmlprofilerdata.h index 2570513d93..00ef037071 100644 --- a/tools/qmlprofiler/qmlprofilerdata.h +++ b/tools/qmlprofiler/qmlprofilerdata.h @@ -58,12 +58,6 @@ public: bool isEmpty() const; -signals: - void error(QString); - void stateChanged(); - void dataReady(); - -public slots: void clear(); void setTraceEndTime(qint64 time); void setTraceStartTime(qint64 time); @@ -83,6 +77,11 @@ public slots: void complete(); bool save(const QString &filename); +signals: + void error(QString); + void stateChanged(); + void dataReady(); + private: void sortStartTimes(); void computeQmlTime(); -- cgit v1.2.3 From c685165038e10464da877896d1accb7b75d9086e Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 20 Jul 2016 15:38:08 +0200 Subject: Qml: Create profiler adapters in the plugins This way QtQml doesn't need to know the ctors. Change-Id: Ie74049092b5eb9837537591c0cf37ad1487e4066 Reviewed-by: Simon Hausmann --- src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp | 2 +- src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp | 2 +- src/qml/jsruntime/qv4engine.cpp | 4 ++-- src/qml/jsruntime/qv4engine_p.h | 2 +- src/qml/qml/qqmlengine.cpp | 5 ----- src/qml/qml/qqmlengine_p.h | 1 - 6 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp index ea052f0475..a4320098c0 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp @@ -48,7 +48,7 @@ QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEngin next(0) { setService(service); - engine->enableProfiler(); + engine->profiler = new QQmlProfiler; connect(this, &QQmlProfilerAdapter::profilingEnabled, engine->profiler, &QQmlProfiler::startProfiling); connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp index 084ac9e489..c88cf559f7 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp @@ -46,7 +46,7 @@ QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::Execut m_functionCallPos(0), m_memoryPos(0) { setService(service); - engine->enableProfiler(); + engine->setProfiler(new QV4::Profiling::Profiler(engine)); connect(this, &QQmlAbstractProfilerAdapter::profilingEnabled, this, &QV4ProfilerAdapter::forwardEnabled); connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index fe2d4e6575..bff0df58cf 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -473,10 +473,10 @@ void ExecutionEngine::setDebugger(Debugging::Debugger *debugger_) debugger = debugger_; } -void ExecutionEngine::enableProfiler() +void ExecutionEngine::setProfiler(Profiling::Profiler *profiler_) { Q_ASSERT(!profiler); - profiler = new QV4::Profiling::Profiler(this); + profiler = profiler_; } void ExecutionEngine::initRootContext() diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 8743df771c..208598efd5 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -383,7 +383,7 @@ public: ~ExecutionEngine(); void setDebugger(Debugging::Debugger *debugger); - void enableProfiler(); + void setProfiler(Profiling::Profiler *profiler); ExecutionContext *pushGlobalContext(); void pushContext(Heap::ExecutionContext *context); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index c2eef840ed..6e77bc551c 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -648,11 +648,6 @@ QQmlEnginePrivate::~QQmlEnginePrivate() delete profiler; } -void QQmlEnginePrivate::enableProfiler() -{ - profiler = new QQmlProfiler(); -} - void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) { QObjectPrivate *p = QObjectPrivate::get(o); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index bd4b4e536e..4269392743 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -135,7 +135,6 @@ public: QQmlContext *rootContext; QQmlProfiler *profiler; - void enableProfiler(); bool outputWarningsToMsgLog; -- cgit v1.2.3 From 901b975fb5be147b9bb446c7b9f61c2d448a94ab Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Jul 2016 17:09:38 +0200 Subject: V4: Don't pass size and pointer through allocation trackers By not relying on the return value of the macros we can #define them away later, when compiling with -no-qml-debug Change-Id: I24d50fa3f5d8e8765a42b050c81ddfae20f20a23 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4profiling_p.h | 41 +++++++++++++++++++------------------- src/qml/memory/qv4mm.cpp | 24 +++++++++++----------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index a422dd0fb6..c684d48368 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -59,6 +59,22 @@ QT_BEGIN_NAMESPACE +#define Q_V4_PROFILE_ALLOC(engine, size, type)\ + (engine->profiler &&\ + (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ + engine->profiler->trackAlloc(size, type) : false) + +#define Q_V4_PROFILE_DEALLOC(engine, size, type) \ + (engine->profiler &&\ + (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ + engine->profiler->trackDealloc(size, type) : false) + +#define Q_V4_PROFILE(engine, function)\ + (engine->profiler &&\ + (engine->profiler->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\ + Profiling::FunctionCallProfiler::profileCall(engine->profiler, engine, function) :\ + function->code(engine, function->codeData)) + namespace QV4 { namespace Profiling { @@ -150,25 +166,8 @@ private: qint64 m_end; }; -#define Q_V4_PROFILE_ALLOC(engine, size, type)\ - (engine->profiler &&\ - (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ - engine->profiler->trackAlloc(size, type) : size) - -#define Q_V4_PROFILE_DEALLOC(engine, pointer, size, type) \ - (engine->profiler &&\ - (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ - engine->profiler->trackDealloc(pointer, size, type) : pointer) - -#define Q_V4_PROFILE(engine, function)\ - (engine->profiler &&\ - (engine->profiler->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\ - Profiling::FunctionCallProfiler::profileCall(engine->profiler, engine, function) :\ - function->code(engine, function->codeData)) - class Q_QML_EXPORT Profiler : public QObject { Q_OBJECT - Q_DISABLE_COPY(Profiler) public: struct SentMarker { SentMarker() : m_function(nullptr) {} @@ -212,18 +211,18 @@ public: Profiler(QV4::ExecutionEngine *engine); - size_t trackAlloc(size_t size, MemoryType type) + bool trackAlloc(size_t size, MemoryType type) { MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), (qint64)size, type}; m_memory_data.append(allocation); - return size; + return true; } - void *trackDealloc(void *pointer, size_t size, MemoryType type) + bool trackDealloc(size_t size, MemoryType type) { MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), -(qint64)size, type}; m_memory_data.append(allocation); - return pointer; + return true; } quint64 featuresEnabled; diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index a8d5624550..a6d7c3b1ed 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -185,7 +185,7 @@ struct MemoryManager::Data ~Data() { for (std::vector::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) { - Q_V4_PROFILE_DEALLOC(engine, 0, i->size(), Profiling::HeapPage); + Q_V4_PROFILE_DEALLOC(engine, i->size(), Profiling::HeapPage); i->deallocate(); } } @@ -239,7 +239,7 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec #ifdef V4_USE_HEAPTRACK heaptrack_report_free(m); #endif - Q_V4_PROFILE_DEALLOC(engine, m, header->itemSize, Profiling::SmallItem); + Q_V4_PROFILE_DEALLOC(engine, header->itemSize, Profiling::SmallItem); ++(*itemsInUse); } // Relink all free blocks to rewrite references to any released chunk. @@ -302,10 +302,11 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize runGC(); // we use malloc for this - MemoryManager::Data::LargeItem *item = static_cast( - malloc(Q_V4_PROFILE_ALLOC(engine, size + sizeof(MemoryManager::Data::LargeItem), - Profiling::LargeItem))); - memset(item, 0, size + sizeof(MemoryManager::Data::LargeItem)); + const size_t totalSize = size + sizeof(MemoryManager::Data::LargeItem); + Q_V4_PROFILE_ALLOC(engine, totalSize, Profiling::LargeItem); + MemoryManager::Data::LargeItem *item = + static_cast(malloc(totalSize)); + memset(item, 0, totalSize); item->next = m_d->largeItems; item->size = size; m_d->largeItems = item; @@ -338,9 +339,8 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize shift = m_d->maxShift; std::size_t allocSize = m_d->maxChunkSize*(size_t(1) << shift); allocSize = roundUpToMultipleOf(m_d->pageSize, allocSize); - PageAllocation allocation = PageAllocation::allocate( - Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage), - OSAllocator::JSGCHeapPages); + Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage); + PageAllocation allocation = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); m_d->heapChunks.push_back(allocation); header = reinterpret_cast(allocation.base()); @@ -507,7 +507,7 @@ void MemoryManager::sweep(bool lastSweep) // Release that chunk if it could have been spared since the last GC run without any difference. if (chunkIsEmpty[i] && m_d->availableItems[pos] - decrease >= itemsInUse[pos]) { - Q_V4_PROFILE_DEALLOC(engine, 0, chunkIter->size(), Profiling::HeapPage); + Q_V4_PROFILE_DEALLOC(engine, chunkIter->size(), Profiling::HeapPage); #ifdef V4_USE_VALGRIND VALGRIND_MEMPOOL_FREE(this, header); #endif @@ -542,8 +542,8 @@ void MemoryManager::sweep(bool lastSweep) m->vtable()->destroy(m); *last = i->next; - free(Q_V4_PROFILE_DEALLOC(engine, i, i->size + sizeof(Data::LargeItem), - Profiling::LargeItem)); + Q_V4_PROFILE_DEALLOC(engine, i->size + sizeof(Data::LargeItem), Profiling::LargeItem); + free(i); i = *last; } -- cgit v1.2.3 From cc5ead1b3c8ce79c240b70bdfcfb687fe60e50f5 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 28 Jul 2016 13:50:41 +0200 Subject: Bump version Change-Id: I9857609a360664d1f88b4e6594a9adcd8a995e06 --- .qmake.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.qmake.conf b/.qmake.conf index 40e57878b5..45d16f2971 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,4 +1,4 @@ load(qt_build_config) CONFIG += warning_clean -MODULE_VERSION = 5.7.0 +MODULE_VERSION = 5.7.1 -- cgit v1.2.3 From 85e60c4591b61a648f2fc0c96687d7c9440e712d Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 26 Jul 2016 23:30:21 +0200 Subject: tst_qquickwindow: Fix touch tests: add release There is already a comment in the test that points that are pressed must be released again. Change-Id: I3190b9f98769f7a3838ff703e5557ee321c71604 Reviewed-by: Frederik Gladhorn --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 1e1197637b..025057d113 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -533,9 +533,10 @@ void tst_qquickwindow::touchEvent_basic() // would put the decorated window at that position rather than the window itself. COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos))); topItem->reset(); + QTest::touchEvent(window, touchDevice).release(0, topItem->mapToScene(pos).toPoint(), window); // press multiple points - QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window) + QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window) .press(1, bottomItem->mapToScene(pos).toPoint(), window); QQuickTouchUtils::flush(window); QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); @@ -545,6 +546,7 @@ void tst_qquickwindow::touchEvent_basic() COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos))); topItem->reset(); bottomItem->reset(); + QTest::touchEvent(window, touchDevice).release(0, topItem->mapToScene(pos).toPoint(), window).release(1, bottomItem->mapToScene(pos).toPoint(), window); // touch point on top item moves to bottom item, but top item should still receive the event QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window); @@ -555,6 +557,7 @@ void tst_qquickwindow::touchEvent_basic() COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved, makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos))); topItem->reset(); + QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), window); // touch point on bottom item moves to top item, but bottom item should still receive the event QTest::touchEvent(window, touchDevice).press(0, bottomItem->mapToScene(pos).toPoint(), window); @@ -565,6 +568,7 @@ void tst_qquickwindow::touchEvent_basic() COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved, makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos))); bottomItem->reset(); + QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), window); // a single stationary press on an item shouldn't cause an event QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window); @@ -669,6 +673,7 @@ void tst_qquickwindow::touchEvent_propagation() QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos)))); + QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window); // touch top and middle items, middle item should get both events QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window) @@ -680,6 +685,8 @@ void tst_qquickwindow::touchEvent_propagation() COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, (QList() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos)) << makeTouchPoint(middleItem, pos) ))); + QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window) + .release(1, pointInMiddleItem, window); middleItem->reset(); // disable middleItem as well @@ -704,6 +711,8 @@ void tst_qquickwindow::touchEvent_propagation() bottomItem->acceptTouchEvents = acceptTouchEvents; bottomItem->setEnabled(enableItem); bottomItem->setVisible(showItem); + QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window) + .release(1, pointInMiddleItem, window); // no events should be received QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window) @@ -713,7 +722,9 @@ void tst_qquickwindow::touchEvent_propagation() QVERIFY(topItem->lastEvent.touchPoints.isEmpty()); QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); - + QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window) + .release(1, pointInMiddleItem, window) + .release(2, pointInBottomItem, window); topItem->reset(); middleItem->reset(); bottomItem->reset(); @@ -739,6 +750,7 @@ void tst_qquickwindow::touchEvent_propagation() COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos))); } + QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window); delete topItem; delete middleItem; -- cgit v1.2.3 From 3c9a70eec93ad3155708686b4aa8493599341a6b Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 28 Jul 2016 12:50:38 +0200 Subject: MSVC: Make Lancelot's scenegrabber compile The extern declaration needs Q_CORE_EXPORT (which resolves to an import declaration on MSVC). Also, the type of the qt_qhash_seed variable is a QBasicAtomicInt, not int, so with proper signature mangling it would not resolve (since memory layout is the same for an int and a QBasicAtomicInt, it would just work for linkers that did not detect it.) Change-Id: I92375afcfc13e045e78a4d6cfdd539bd01b66136 Reviewed-by: Eirik Aavitsland --- tests/manual/scenegraph_lancelot/scenegrabber/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp b/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp index d8eedd5afb..8231c7ffef 100644 --- a/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp +++ b/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp @@ -139,7 +139,7 @@ private: }; -extern uint qt_qhash_seed; +Q_CORE_EXPORT extern QBasicAtomicInt qt_qhash_seed; int main(int argc, char *argv[]) { -- cgit v1.2.3 From d32319eca446d5583af9081a0056dea4667d6096 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Jul 2016 17:20:04 +0200 Subject: Replace debugger and profiler with stubs on -no-qml-debug Change-Id: I0f029d92366b3b508bf024c67b877a14bae27cd6 Reviewed-by: Simon Hausmann --- src/qml/debugger/debugger.pri | 26 ++++--- src/qml/debugger/qqmlabstractprofileradapter_p.h | 4 ++ src/qml/debugger/qqmldebug.h | 3 + src/qml/debugger/qqmldebugconnector_p.h | 25 +++++++ src/qml/debugger/qqmldebugservice_p.h | 4 ++ src/qml/debugger/qqmldebugserviceinterfaces_p.h | 43 ++++++++++- src/qml/debugger/qqmldebugstatesdelegate_p.h | 7 ++ src/qml/debugger/qqmlmemoryprofiler_p.h | 9 +++ src/qml/debugger/qqmlprofiler_p.h | 56 ++++++++++++++- src/qml/debugger/qqmlprofilerdefinitions_p.h | 4 ++ src/qml/jsruntime/jsruntime.pri | 7 +- src/qml/jsruntime/qv4debugging_p.h | 15 ++++ src/qml/jsruntime/qv4profiling_p.h | 20 ++++++ src/qml/qml/qqmlobjectcreator.cpp | 9 ++- src/quick/qtquick2.cpp | 7 ++ src/quick/util/qquickprofiler_p.h | 92 +++++++++++++----------- src/quick/util/util.pri | 7 +- src/quickwidgets/quickwidgets.pro | 1 + 18 files changed, 278 insertions(+), 61 deletions(-) diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri index c893d7b484..74dcb250a8 100644 --- a/src/qml/debugger/debugger.pri +++ b/src/qml/debugger/debugger.pri @@ -1,19 +1,23 @@ -contains(QT_CONFIG, no-qml-debug):DEFINES += QT_NO_QML_DEBUGGER +contains(QT_CONFIG, no-qml-debug) { + DEFINES += QT_NO_QML_DEBUGGER +} else { + HEADERS += \ + $$PWD/qqmldebugpluginmanager_p.h \ + $$PWD/qqmldebugservicefactory_p.h -SOURCES += \ - $$PWD/qqmldebug.cpp \ - $$PWD/qqmldebugconnector.cpp \ - $$PWD/qqmldebugservice.cpp \ - $$PWD/qqmldebugserviceinterfaces.cpp \ - $$PWD/qqmlabstractprofileradapter.cpp \ - $$PWD/qqmlmemoryprofiler.cpp \ - $$PWD/qqmlprofiler.cpp + SOURCES += \ + $$PWD/qqmldebug.cpp \ + $$PWD/qqmldebugconnector.cpp \ + $$PWD/qqmldebugservice.cpp \ + $$PWD/qqmlabstractprofileradapter.cpp \ + $$PWD/qqmlmemoryprofiler.cpp \ + $$PWD/qqmlprofiler.cpp \ + $$PWD/qqmldebugserviceinterfaces.cpp +} HEADERS += \ $$PWD/qqmldebugconnector_p.h \ - $$PWD/qqmldebugpluginmanager_p.h \ $$PWD/qqmldebugservice_p.h \ - $$PWD/qqmldebugservicefactory_p.h \ $$PWD/qqmldebugserviceinterfaces_p.h \ $$PWD/qqmldebugstatesdelegate_p.h \ $$PWD/qqmldebug.h \ diff --git a/src/qml/debugger/qqmlabstractprofileradapter_p.h b/src/qml/debugger/qqmlabstractprofileradapter_p.h index 8820c4311a..6a05a80f37 100644 --- a/src/qml/debugger/qqmlabstractprofileradapter_p.h +++ b/src/qml/debugger/qqmlabstractprofileradapter_p.h @@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_QML_DEBUGGER + class QQmlProfilerService; class Q_QML_PRIVATE_EXPORT QQmlAbstractProfilerAdapter : public QObject, public QQmlProfilerDefinitions { Q_OBJECT @@ -114,6 +116,8 @@ public: #define QQmlAbstractProfilerAdapterFactory_iid "org.qt-project.Qt.QQmlAbstractProfilerAdapterFactory" +#endif // QT_NO_QML_DEBUGGER + QT_END_NAMESPACE #endif // QQMLABSTRACTPROFILERADAPTER_P_H diff --git a/src/qml/debugger/qqmldebug.h b/src/qml/debugger/qqmldebug.h index 660b9e4d46..fb41039867 100644 --- a/src/qml/debugger/qqmldebug.h +++ b/src/qml/debugger/qqmldebug.h @@ -46,6 +46,7 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_QML_DEBUGGER struct Q_QML_EXPORT QQmlDebuggingEnabler { @@ -77,6 +78,8 @@ static QQmlDebuggingEnabler qQmlEnableDebuggingHelper(false); static QQmlDebuggingEnabler qQmlEnableDebuggingHelper(true); #endif +#endif + QT_END_NAMESPACE #endif // QQMLDEBUG_H diff --git a/src/qml/debugger/qqmldebugconnector_p.h b/src/qml/debugger/qqmldebugconnector_p.h index 05755250bd..0d3e2e2e47 100644 --- a/src/qml/debugger/qqmldebugconnector_p.h +++ b/src/qml/debugger/qqmldebugconnector_p.h @@ -59,6 +59,29 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER + +class Q_QML_PRIVATE_EXPORT QQmlDebugConnector +{ +public: + static QQmlDebugConnector *instance() { return nullptr; } + + template + static Service *service() { return nullptr; } + + bool hasEngine(QJSEngine *) const { return false; } + void addEngine(QJSEngine *) {} + void removeEngine(QJSEngine *) {} + + bool open(const QVariantHash &configuration = QVariantHash()) + { + Q_UNUSED(configuration); + return false; + } +}; + +#else + class QQmlDebugService; class Q_QML_PRIVATE_EXPORT QQmlDebugConnector : public QObject { @@ -106,6 +129,8 @@ public: #define QQmlDebugConnectorFactory_iid "org.qt-project.Qt.QQmlDebugConnectorFactory" +#endif + QT_END_NAMESPACE #endif // QQMLDEBUGCONNECTOR_H diff --git a/src/qml/debugger/qqmldebugservice_p.h b/src/qml/debugger/qqmldebugservice_p.h index 6b4ef38ef1..42a57a39f2 100644 --- a/src/qml/debugger/qqmldebugservice_p.h +++ b/src/qml/debugger/qqmldebugservice_p.h @@ -58,6 +58,8 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_QML_DEBUGGER + class QJSEngine; class QQmlDebugServicePrivate; @@ -101,6 +103,8 @@ signals: void messagesToClient(const QString &name, const QList &messages); }; +#endif + QT_END_NAMESPACE #endif // QQMLDEBUGSERVICE_H diff --git a/src/qml/debugger/qqmldebugserviceinterfaces_p.h b/src/qml/debugger/qqmldebugserviceinterfaces_p.h index 77fde02386..ca6293c3ec 100644 --- a/src/qml/debugger/qqmldebugserviceinterfaces_p.h +++ b/src/qml/debugger/qqmldebugserviceinterfaces_p.h @@ -62,6 +62,45 @@ QT_BEGIN_NAMESPACE +class QWindow; +class QQuickWindow; + +#ifdef QT_NO_QML_DEBUGGER + +struct QV4DebugService +{ + void signalEmitted(const QString &) {} +}; + +struct QQmlProfilerService +{ + void startProfiling(QJSEngine *engine, quint64 features = std::numeric_limits::max()) + { + Q_UNUSED(engine); + Q_UNUSED(features); + } + + void stopProfiling(QJSEngine *) {} +}; + +struct QQmlEngineDebugService +{ + void objectCreated(QJSEngine *, QObject *) {} + virtual void setStatesDelegate(QQmlDebugStatesDelegate *) {} +}; + +struct QQmlInspectorService { + void addWindow(QQuickWindow *) {} + void setParentWindow(QQuickWindow *, QWindow *) {} + void removeWindow(QQuickWindow *) {} +}; + +struct QDebugMessageService {}; +struct QQmlEngineControlService {}; +struct QQmlNativeDebugService {}; + +#else + class Q_QML_PRIVATE_EXPORT QV4DebugService : public QQmlDebugService { Q_OBJECT @@ -117,8 +156,6 @@ protected: QQmlBoundSignal *nextSignal(QQmlBoundSignal *prev) { return prev->m_nextSignal; } }; -class QWindow; -class QQuickWindow; class Q_QML_PRIVATE_EXPORT QQmlInspectorService : public QQmlDebugService { Q_OBJECT @@ -178,6 +215,8 @@ protected: static const QString s_key; }; +#endif + QT_END_NAMESPACE #endif // QQMLDEBUGSERVICEINTERFACES_P_H diff --git a/src/qml/debugger/qqmldebugstatesdelegate_p.h b/src/qml/debugger/qqmldebugstatesdelegate_p.h index 42c4e94b50..95f727fb2d 100644 --- a/src/qml/debugger/qqmldebugstatesdelegate_p.h +++ b/src/qml/debugger/qqmldebugstatesdelegate_p.h @@ -57,6 +57,11 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER + +class QQmlDebugStatesDelegate {}; + +#else class QQmlContext; class QQmlProperty; @@ -90,6 +95,8 @@ private: Q_DISABLE_COPY(QQmlDebugStatesDelegate) }; +#endif + QT_END_NAMESPACE #endif // QQMLDEBUGSTATESDELEGATE_P_H diff --git a/src/qml/debugger/qqmlmemoryprofiler_p.h b/src/qml/debugger/qqmlmemoryprofiler_p.h index 4b0ba823ba..59f08704ca 100644 --- a/src/qml/debugger/qqmlmemoryprofiler_p.h +++ b/src/qml/debugger/qqmlmemoryprofiler_p.h @@ -55,6 +55,13 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER + +#define QML_MEMORY_SCOPE_URL(url) +#define QML_MEMORY_SCOPE_STRING(s) + +#else + class QUrl; class Q_QML_PRIVATE_EXPORT QQmlMemoryScope @@ -83,5 +90,7 @@ public: #define QML_MEMORY_SCOPE_URL(url) QQmlMemoryScope _qml_memory_scope(url) #define QML_MEMORY_SCOPE_STRING(s) QQmlMemoryScope _qml_memory_scope(s) +#endif + QT_END_NAMESPACE #endif // QQMLMEMORYPROFILER_H diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 08606814a9..483252df6d 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -63,6 +63,54 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER + +#define Q_QML_PROFILE_IF_ENABLED(feature, profiler, Code) +#define Q_QML_PROFILE(feature, profiler, Method) +#define Q_QML_OC_PROFILE(member, Code) + +struct QQmlProfiler {}; + +struct QQmlBindingProfiler +{ + QQmlBindingProfiler(QQmlProfiler *, QQmlBinding *, QV4::FunctionObject *) {} +}; + +struct QQmlHandlingSignalProfiler +{ + QQmlHandlingSignalProfiler(QQmlProfiler *, QQmlBoundSignalExpression *) {} +}; + +struct QQmlCompilingProfiler +{ + QQmlCompilingProfiler(QQmlProfiler *, QQmlDataBlob *) {} +}; + +struct QQmlVmeProfiler { + QQmlVmeProfiler() {} + + void init(QQmlProfiler *, int) {} + + const QV4::CompiledData::Object *pop() { return nullptr; } + void push(const QV4::CompiledData::Object *) {} + + static const quintptr profiler = 0; +}; + +struct QQmlObjectCreationProfiler +{ + QQmlObjectCreationProfiler(quintptr, const QV4::CompiledData::Object *) {} + void update(QV4::CompiledData::CompilationUnit *, const QV4::CompiledData::Object *, + const QString &, const QUrl &) {} +}; + +struct QQmlObjectCompletionProfiler +{ + QQmlObjectCompletionProfiler(QQmlVmeProfiler *) {} +}; + +#else + #define Q_QML_PROFILE_IF_ENABLED(feature, profiler, Code)\ if (profiler && (profiler->featuresEnabled & (1 << feature))) {\ Code;\ @@ -72,6 +120,9 @@ QT_BEGIN_NAMESPACE #define Q_QML_PROFILE(feature, profiler, Method)\ Q_QML_PROFILE_IF_ENABLED(feature, profiler, profiler->Method) +#define Q_QML_OC_PROFILE(member, Code)\ + Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, member.profiler, Code) + // This struct is somewhat dangerous to use: // The messageType is a bit field. You can pack multiple messages into // one object, e.g. RangeStart and RangeLocation. Each one will be read @@ -348,9 +399,6 @@ private: QFiniteStack ranges; }; -#define Q_QML_OC_PROFILE(member, Code)\ - Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, member.profiler, Code) - class QQmlObjectCreationProfiler { public: @@ -398,4 +446,6 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QQmlProfiler::LocationHash) +#endif // QT_NO_QML_DEBUGGER + #endif // QQMLPROFILER_P_H diff --git a/src/qml/debugger/qqmlprofilerdefinitions_p.h b/src/qml/debugger/qqmlprofilerdefinitions_p.h index 2b2eda22e1..c6ae4593a9 100644 --- a/src/qml/debugger/qqmlprofilerdefinitions_p.h +++ b/src/qml/debugger/qqmlprofilerdefinitions_p.h @@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_QML_DEBUGGER + struct QQmlProfilerDefinitions { enum Message { Event, @@ -161,6 +163,8 @@ struct QQmlProfilerDefinitions { }; }; +#endif // QT_NO_QML_DEBUGGER + QT_END_NAMESPACE #endif diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 6bd18dabb5..cf434ee2ed 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -39,11 +39,16 @@ SOURCES += \ $$PWD/qv4include.cpp \ $$PWD/qv4qobjectwrapper.cpp \ $$PWD/qv4vme_moth.cpp \ - $$PWD/qv4profiling.cpp \ $$PWD/qv4arraybuffer.cpp \ $$PWD/qv4typedarray.cpp \ $$PWD/qv4dataview.cpp +!contains(QT_CONFIG, no-qml-debug) { + SOURCES += $$PWD/qv4profiling.cpp +} else { + DEFINES += QT_NO_QML_DEBUGGER +} + HEADERS += \ $$PWD/qv4global_p.h \ $$PWD/qv4engine_p.h \ diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h index 9dca7e9979..3b589a41f1 100644 --- a/src/qml/jsruntime/qv4debugging_p.h +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -59,6 +59,19 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace Debugging { +#ifdef QT_NO_QML_DEBUGGER + +struct Debugger +{ + bool pauseAtNextOpportunity() const { return false; } + void maybeBreakAtInstruction() {} + void enteringFunction() {} + void leavingFunction(const ReturnedValue &) {} + void aboutToThrow() {} +}; + +#else + class Q_QML_EXPORT Debugger : public QObject { Q_OBJECT @@ -72,6 +85,8 @@ public: virtual void aboutToThrow() = 0; }; +#endif // QT_NO_QML_DEBUGGING + } // namespace Debugging } // namespace QV4 diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index c684d48368..fd14c0ae93 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -57,8 +57,24 @@ #include +#ifdef QT_NO_QML_DEBUGGER + +#define Q_V4_PROFILE_ALLOC(engine, size, type) (!engine) +#define Q_V4_PROFILE_DEALLOC(engine, size, type) (!engine) +#define Q_V4_PROFILE(engine, function) (function->code(engine, function->codeData)) + QT_BEGIN_NAMESPACE +namespace QV4 { +namespace Profiling { +struct Profiler {}; +} +} + +QT_END_NAMESPACE + +#else + #define Q_V4_PROFILE_ALLOC(engine, size, type)\ (engine->profiler &&\ (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ @@ -75,6 +91,8 @@ QT_BEGIN_NAMESPACE Profiling::FunctionCallProfiler::profileCall(engine->profiler, engine, function) :\ function->code(engine, function->codeData)) +QT_BEGIN_NAMESPACE + namespace QV4 { namespace Profiling { @@ -288,4 +306,6 @@ Q_DECLARE_METATYPE(QV4::Profiling::FunctionLocationHash) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) +#endif // QT_NO_QML_DEBUGGER + #endif // QV4PROFILING_H diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 87212ef713..4e98e04ce4 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -89,9 +89,12 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::Compil sharedState->creationContext = creationContext; sharedState->rootContext = 0; - QQmlProfiler *profiler = QQmlEnginePrivate::get(engine)->profiler; - Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler, - sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount)); + if (QQmlProfiler *profiler = QQmlEnginePrivate::get(engine)->profiler) { + Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler, + sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount)); + } else { + Q_UNUSED(profiler); + } } QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp index ca12155e63..c36adf56ec 100644 --- a/src/quick/qtquick2.cpp +++ b/src/quick/qtquick2.cpp @@ -62,6 +62,12 @@ static void initResources() QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER + +class QQmlQtQuick2DebugStatesDelegate : public QQmlDebugStatesDelegate {}; + +#else + class QQmlQtQuick2DebugStatesDelegate : public QQmlDebugStatesDelegate { public: @@ -174,6 +180,7 @@ void QQmlQtQuick2DebugStatesDelegate::resetBindingForInvalidProperty(QObject *ob } } +#endif // QT_NO_QML_DEBUGGER void QQmlQtQuick2Module::defineModule() { diff --git a/src/quick/util/qquickprofiler_p.h b/src/quick/util/qquickprofiler_p.h index 480a2ac3fb..66ed212722 100644 --- a/src/quick/util/qquickprofiler_p.h +++ b/src/quick/util/qquickprofiler_p.h @@ -62,52 +62,22 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER + +#define Q_QUICK_PROFILE_IF_ENABLED(feature, Code) + +struct QQuickProfiler { + static void registerAnimationCallback() {} +}; + +#else + #define Q_QUICK_PROFILE_IF_ENABLED(feature, Code)\ if (QQuickProfiler::featuresEnabled & (1 << feature)) {\ Code;\ } else\ (void)0 -#define Q_QUICK_PROFILE(feature, Method)\ - Q_QUICK_PROFILE_IF_ENABLED(feature, QQuickProfiler::Method) - -#define Q_QUICK_SG_PROFILE_START(Type)\ - Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ - (QQuickProfiler::startSceneGraphFrame())) - -#define Q_QUICK_SG_PROFILE_RECORD(Type)\ - Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ - (QQuickProfiler::recordSceneGraphTimestamp())) - -#define Q_QUICK_SG_PROFILE_SKIP(Type, Skip)\ - Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ - (QQuickProfiler::skipSceneGraphTimestamps())) - -#define Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(Type1, Type2)\ - Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ - (QQuickProfiler::startSceneGraphFrame())) - -#define Q_QUICK_SG_PROFILE_SWITCH(Type1, Type2) \ - Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ - (QQuickProfiler::reportSceneGraphFrame())) - -#define Q_QUICK_SG_PROFILE_REPORT(Type)\ - Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ - (QQuickProfiler::reportSceneGraphFrame())) - -#define Q_QUICK_SG_PROFILE_END(Type)\ - Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ - (QQuickProfiler::reportSceneGraphFrame())) - -#define Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(Type, Payload)\ - Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ - (QQuickProfiler::reportSceneGraphFrame(Payload))) - - -#define Q_QUICK_INPUT_PROFILE(Type, DetailType, A, B)\ - Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileInputEvents,\ - (QQuickProfiler::inputEvent(A, B))) - // This struct is somewhat dangerous to use: // You can save values either with 32 or 64 bit precision. toByteArrays will // guess the precision from messageType. If you state the wrong messageType @@ -351,6 +321,48 @@ protected: void setTimer(const QElapsedTimer &t); }; +#endif // QT_NO_QML_DEBUGGER + +#define Q_QUICK_PROFILE(feature, Method)\ + Q_QUICK_PROFILE_IF_ENABLED(feature, QQuickProfiler::Method) + +#define Q_QUICK_SG_PROFILE_START(Type)\ + Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ + (QQuickProfiler::startSceneGraphFrame())) + +#define Q_QUICK_SG_PROFILE_RECORD(Type)\ + Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ + (QQuickProfiler::recordSceneGraphTimestamp())) + +#define Q_QUICK_SG_PROFILE_SKIP(Type, Skip)\ + Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ + (QQuickProfiler::skipSceneGraphTimestamps())) + +#define Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(Type1, Type2)\ + Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ + (QQuickProfiler::startSceneGraphFrame())) + +#define Q_QUICK_SG_PROFILE_SWITCH(Type1, Type2) \ + Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ + (QQuickProfiler::reportSceneGraphFrame())) + +#define Q_QUICK_SG_PROFILE_REPORT(Type)\ + Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ + (QQuickProfiler::reportSceneGraphFrame())) + +#define Q_QUICK_SG_PROFILE_END(Type)\ + Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ + (QQuickProfiler::reportSceneGraphFrame())) + +#define Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(Type, Payload)\ + Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\ + (QQuickProfiler::reportSceneGraphFrame(Payload))) + + +#define Q_QUICK_INPUT_PROFILE(Type, DetailType, A, B)\ + Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileInputEvents,\ + (QQuickProfiler::inputEvent(A, B))) + QT_END_NAMESPACE #endif diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index ffb31ae75e..2c31f6feed 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -26,12 +26,17 @@ SOURCES += \ $$PWD/qquickanimator.cpp \ $$PWD/qquickanimatorjob.cpp \ $$PWD/qquickanimatorcontroller.cpp \ - $$PWD/qquickprofiler.cpp \ $$PWD/qquickfontmetrics.cpp \ $$PWD/qquicktextmetrics.cpp \ $$PWD/qquickshortcut.cpp \ $$PWD/qquickvalidator.cpp +contains(QT_CONFIG, no-qml-debug) { + DEFINES += QT_NO_QML_DEBUGGER +} else { + SOURCES += $$PWD/qquickprofiler.cpp +} + HEADERS += \ $$PWD/qquickapplication_p.h\ $$PWD/qquickutilmodule_p.h\ diff --git a/src/quickwidgets/quickwidgets.pro b/src/quickwidgets/quickwidgets.pro index 87409e31c5..0e45e63307 100644 --- a/src/quickwidgets/quickwidgets.pro +++ b/src/quickwidgets/quickwidgets.pro @@ -3,6 +3,7 @@ TARGET = QtQuickWidgets QT = core-private gui-private qml-private quick-private widgets-private DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES +contains(QT_CONFIG, no-qml-debug): DEFINES += QT_NO_QML_DEBUGGER HEADERS += \ qquickwidget.h \ -- cgit v1.2.3 From ab976add7f62891038d97edc29c9d57168fbefb4 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 28 Jul 2016 13:15:50 +0200 Subject: Unbreak build on Android 6a1667bc added usage of QDateTime without an include. Change-Id: I6e93a27d6e05dced4cdb5c842a03615852802bce Reviewed-by: Simon Hausmann --- src/qml/qml/qqmltypeloader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 851a18fa5f..09e10005a0 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -54,6 +54,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3 From 75d6392d6c5efa6fdd1f63cac6a2438fedb2f2a9 Mon Sep 17 00:00:00 2001 From: Venugopal Shivashankar Date: Tue, 1 Mar 2016 15:31:52 +0100 Subject: Doc: Describe how to implement custom SQL models for QML Change-Id: I31781f32c2f9699f386a326f18cb5cc705582a89 Reviewed-by: Leena Miettinen Reviewed-by: Mitch Curtis --- .../doc/src/concepts/modelviewsdata/cppmodels.qdoc | 105 +++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc index cb281a2d4a..a764402c2f 100644 --- a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc +++ b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc @@ -156,6 +156,111 @@ with list models of QAbstractItemModel type: \li \l DelegateModel::parentModelIndex() returns a QModelIndex which can be assigned to DelegateModel::rootIndex \endlist +\section2 SQL Models + +Qt provides C++ classes that support SQL data models. These classes work +transparently on the underlying SQL data, reducing the need to run SQL +queries for basic SQL operations such as create, insert, or update. +For more details about these classes, see \l{Using the SQL Model Classes}. + +Although the C++ classes provide complete feature sets to operate on SQL +data, they do not provide data access to QML. So you must implement a +C++ custom data model as a subclass of one of these classes, and expose it +to QML either as a type or context property. + +\section3 Read-only Data Model + +The custom model must reimplement the following methods to enable read-only +access to the data from QML: + +\list +\li \l{QAbstractItemModel::}{roleNames}() to expose the role names to the + QML frontend. For example, the following version returns the selected + table's field names as role names: + \code + QHash SqlQueryModel::roleNames() const + { + QHash roles; + // record() returns an empty QSqlRecord + for (int i = 0; i < this->record().count(); i ++) { + roles.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8()); + } + return roles; + } + \endcode +\li \l{QSqlQueryModel::}{data}() to expose SQL data to the QML frontend. + For example, the following implementation returns data for the given + model index: + \code + QVariant SqlQueryModel::data(const QModelIndex &index, int role) const + { + QVariant value; + + if (index.isValid()) { + if (role < Qt::UserRole) { + value = QSqlQueryModel::data(index, role); + } else { + int columnIdx = role - Qt::UserRole - 1; + QModelIndex modelIndex = this->index(index.row(), columnIdx); + value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole); + } + } + return value; + } + \endcode +\endlist + +The QSqlQueryModel class is good enough to implement a custom read-only +model that represents data in an SQL database. The +\l{Qt Quick Controls 2 - Chat Tutorial}{chat tutorial} example +demonstrates this very well by implementing a custom model to fetch the +contact details from an SQLite database. + +\section3 Editable Data Model + +Besides the \c roleNames() and \c data(), the editable models must reimplement +the \l{QSqlTableModel::}{setData} method to save changes to existing SQL data. +The following version of the method checks if the given model index is valid +and the \c role is equal to \l Qt::EditRole, before calling the parent class +version: + +\code +bool SqlEditableModel::setData(const QModelIndex &item, const QVariant &value, int role) +{ + if (item.isValid() && role == Qt::EditRole) { + QSqlTableModel::setData(item, value,role); + emit dataChanged(item, item); + return true; + } + return false; + +} +\endcode + +\note It is important to emit the \l{QAbstractItemModel::}{dataChanged}() +signal after saving the changes. + +Unlike the C++ item views such as QListView or QTableView, the \c setData() +method must be explicitly invoked from QML whenever appropriate. For example, +on the \l[QML]{TextField::}{editingFinished}() or \l[QML]{TextField::}{accepted}() +signal of \l[QtQuickControls]{TextField}. Depending on the +\l{QSqlTableModel::}{EditStrategy} used by the model, the changes are either +queued for submission later or submitted immediately. + +You can also insert new data into the model by calling +\l {QSqlTableModel::insertRecord}(). In the following example snippet, +a QSqlRecord is populated with book details and appended to the +model: + +\code + ... + QSqlRecord newRecord = record(); + newRecord.setValue("author", "John Grisham"); + newRecord.setValue("booktitle", "The Litigators"); + insertRecord(rowCount(), newRecord); + ... +\endcode + \section2 Exposing C++ Data Models to QML The above examples use QQmlContext::setContextProperty() to set -- cgit v1.2.3 From ddde056393b13db4bab7cf3fddf2a0aeb1fdb6e7 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Jul 2016 15:58:04 +0200 Subject: V4: Leave out Debug and Line istructions on -no-qml-debug Change-Id: Ida861af5636a1605783f7af4bf88cb46b44f9022 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4instr_moth_p.h | 15 +++++++++++++-- src/qml/compiler/qv4isel_moth.cpp | 33 +++++++++++++++++---------------- src/qml/compiler/qv4isel_moth_p.h | 2 ++ src/qml/jsruntime/qv4vme_moth.cpp | 4 ++++ 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 112003ebb8..20b871c4e8 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -58,10 +58,17 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER +#define MOTH_DEBUG_INSTR(F) +#else +#define MOTH_DEBUG_INSTR(F) \ + F(Line, line) \ + F(Debug, debug) +#endif + #define FOR_EACH_MOTH_INSTR(F) \ F(Ret, ret) \ - F(Line, line) \ - F(Debug, debug) \ + MOTH_DEBUG_INSTR(F) \ F(LoadRuntimeString, loadRuntimeString) \ F(LoadRegExp, loadRegExp) \ F(LoadClosure, loadClosure) \ @@ -254,6 +261,8 @@ union Instr MOTH_INSTR_HEADER Param result; }; + +#ifndef QT_NO_QML_DEBUGGING struct instr_line { MOTH_INSTR_HEADER qint32 lineNumber; @@ -262,6 +271,8 @@ union Instr MOTH_INSTR_HEADER qint32 lineNumber; }; +#endif // QT_NO_QML_DEBUGGING + struct instr_loadRuntimeString { MOTH_INSTR_HEADER int stringId; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index dc803f1ee2..41790c04a9 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -412,6 +412,7 @@ void InstructionSelection::run(int functionIndex) if (s->location.startLine != currentLine) { blockNeedsDebugInstruction = false; currentLine = s->location.startLine; +#ifndef QT_NO_QML_DEBUGGER if (irModule->debugMode) { Instruction::Debug debug; debug.lineNumber = currentLine; @@ -421,6 +422,7 @@ void InstructionSelection::run(int functionIndex) line.lineNumber = currentLine; addInstruction(line); } +#endif } } @@ -1092,6 +1094,17 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint } } +void InstructionSelection::addDebugInstruction() +{ +#ifndef QT_NO_QML_DEBUGGER + if (blockNeedsDebugInstruction) { + Instruction::Debug debug; + debug.lineNumber = -int(currentLine); + addInstruction(debug); + } +#endif +} + void InstructionSelection::visitJump(IR::Jump *s) { if (s->target == _nextBlock) @@ -1099,11 +1112,7 @@ void InstructionSelection::visitJump(IR::Jump *s) if (_removableJumps.contains(s)) return; - if (blockNeedsDebugInstruction) { - Instruction::Debug debug; - debug.lineNumber = -int(currentLine); - addInstruction(debug); - } + addDebugInstruction(); Instruction::Jump jump; jump.offset = 0; @@ -1114,11 +1123,7 @@ void InstructionSelection::visitJump(IR::Jump *s) void InstructionSelection::visitCJump(IR::CJump *s) { - if (blockNeedsDebugInstruction) { - Instruction::Debug debug; - debug.lineNumber = -int(currentLine); - addInstruction(debug); - } + addDebugInstruction(); Param condition; if (IR::Temp *t = s->cond->asTemp()) { @@ -1153,12 +1158,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { - if (blockNeedsDebugInstruction) { - // this is required so stepOut will always be guaranteed to stop in every stack frame - Instruction::Debug debug; - debug.lineNumber = -int(currentLine); - addInstruction(debug); - } + // this is required so stepOut will always be guaranteed to stop in every stack frame + addDebugInstruction(); Instruction::Ret ret; ret.result = getParam(s->expr); diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index ea323497ef..2d2bb91228 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -177,6 +177,8 @@ private: template inline ptrdiff_t addInstruction(const InstrData &data); + inline void addDebugInstruction(); + ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); void patchJumpAddresses(); QByteArray squeezeCode() const; diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index b83bae8a38..9b46871c85 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -142,6 +142,7 @@ Q_QML_EXPORT int qt_v4DebuggerHook(const char *json); } // extern "C" +#ifndef QT_NO_QML_DEBUGGER static int qt_v4BreakpointCount = 0; static bool qt_v4IsDebugging = true; static bool qt_v4IsStepping = false; @@ -285,6 +286,7 @@ static void qt_v4CheckForBreak(QV4::ExecutionContext *context, QV4::Value **scop } } +#endif // QT_NO_QML_DEBUGGER // End of debugger interface using namespace QV4; @@ -904,6 +906,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code return VALUE(instr.result).asReturnedValue(); MOTH_END_INSTR(Ret) +#ifndef QT_NO_QML_DEBUGGER MOTH_BEGIN_INSTR(Debug) engine->current->lineNumber = instr.lineNumber; QV4::Debugging::Debugger *debugger = context->engine()->debugger; @@ -918,6 +921,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code if (qt_v4IsDebugging) qt_v4CheckForBreak(context, scopes, scopeDepth); MOTH_END_INSTR(Line) +#endif // QT_NO_QML_DEBUGGER MOTH_BEGIN_INSTR(LoadThis) VALUE(instr.result) = context->thisObject(); -- cgit v1.2.3 From a7c7b26653c76e6c699a7f4e81f7ffec60dde0be Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Jul 2016 17:02:45 +0200 Subject: V4: Make ExecutionEngine's debugger and profiler private This will allow us to #define them away on -no-qml-debug, saving two pointers per engine. Change-Id: I400cffd32cd7f55ff0e68565734b6002b9f901d5 Reviewed-by: Simon Hausmann --- .../qmldbg_debugger/qqmlnativedebugservice.cpp | 2 +- .../qmltooling/qmldbg_debugger/qv4debugservice.cpp | 4 ++-- .../qmldbg_profiler/qv4profileradapter.cpp | 14 +++++------ src/qml/jsruntime/qv4engine.cpp | 28 +++++++++++----------- src/qml/jsruntime/qv4engine_p.h | 9 ++++--- src/qml/jsruntime/qv4functionobject.cpp | 2 +- src/qml/jsruntime/qv4profiling_p.h | 18 +++++++------- src/qml/jsruntime/qv4script.cpp | 2 +- src/qml/jsruntime/qv4vme_moth.cpp | 4 ++-- src/qml/qml/qqmltypeloader.cpp | 6 ++--- .../qml/debugger/qv4debugger/tst_qv4debugger.cpp | 4 ++-- 11 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp index d54bf98068..5b96163b48 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp @@ -761,7 +761,7 @@ void QQmlNativeDebugServiceImpl::stateAboutToBeChanged(QQmlDebugService::State s if (state == Enabled) { foreach (NativeDebugger *debugger, m_debuggers) { QV4::ExecutionEngine *engine = debugger->engine(); - if (!engine->debugger) + if (!engine->debugger()) engine->setDebugger(debugger); } } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index c87ca47c49..00c5c1ad77 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -706,7 +706,7 @@ void QV4DebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) if (engine){ const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); if (ee) { - QV4Debugger *debugger = qobject_cast(ee->debugger); + QV4Debugger *debugger = qobject_cast(ee->debugger()); if (debugger) debuggerAgent.removeDebugger(debugger); } @@ -720,7 +720,7 @@ void QV4DebugServiceImpl::stateAboutToBeChanged(State state) if (state == Enabled) { foreach (QV4Debugger *debugger, debuggerAgent.debuggers()) { QV4::ExecutionEngine *ee = debugger->engine(); - if (!ee->debugger) + if (!ee->debugger()) ee->setDebugger(debugger); } } diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp index c88cf559f7..eee1dd7eae 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp @@ -52,19 +52,19 @@ QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::Execut connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, this, &QV4ProfilerAdapter::forwardEnabledWhileWaiting, Qt::DirectConnection); connect(this, &QV4ProfilerAdapter::v4ProfilingEnabled, - engine->profiler, &QV4::Profiling::Profiler::startProfiling); + engine->profiler(), &QV4::Profiling::Profiler::startProfiling); connect(this, &QV4ProfilerAdapter::v4ProfilingEnabledWhileWaiting, - engine->profiler, &QV4::Profiling::Profiler::startProfiling, Qt::DirectConnection); + engine->profiler(), &QV4::Profiling::Profiler::startProfiling, Qt::DirectConnection); connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled, - engine->profiler, &QV4::Profiling::Profiler::stopProfiling); + engine->profiler(), &QV4::Profiling::Profiler::stopProfiling); connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting, - engine->profiler, &QV4::Profiling::Profiler::stopProfiling, + engine->profiler(), &QV4::Profiling::Profiler::stopProfiling, Qt::DirectConnection); connect(this, &QQmlAbstractProfilerAdapter::dataRequested, - engine->profiler, &QV4::Profiling::Profiler::reportData); + engine->profiler(), &QV4::Profiling::Profiler::reportData); connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown, - engine->profiler, &QV4::Profiling::Profiler::setTimer); - connect(engine->profiler, &QV4::Profiling::Profiler::dataReady, + engine->profiler(), &QV4::Profiling::Profiler::setTimer); + connect(engine->profiler(), &QV4::Profiling::Profiler::dataReady, this, &QV4ProfilerAdapter::receiveData); } diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index bff0df58cf..785094b96a 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -136,8 +136,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , currentContext(0) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , jsStack(new WTF::PageAllocation) - , debugger(0) - , profiler(0) , globalCode(0) , v8Engine(0) , argumentsAccessors(0) @@ -145,6 +143,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , m_engineId(engineSerial.fetchAndAddOrdered(1)) , regExpCache(0) , m_multiplyWrappedQObjects(0) + , m_debugger(0) + , m_profiler(0) { if (maxCallDepth == -1) { bool ok = false; @@ -442,10 +442,10 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) ExecutionEngine::~ExecutionEngine() { - delete debugger; - debugger = 0; - delete profiler; - profiler = 0; + delete m_debugger; + m_debugger = 0; + delete m_profiler; + m_profiler = 0; delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = 0; delete identifierTable; @@ -467,16 +467,16 @@ ExecutionEngine::~ExecutionEngine() delete [] argumentsAccessors; } -void ExecutionEngine::setDebugger(Debugging::Debugger *debugger_) +void ExecutionEngine::setDebugger(Debugging::Debugger *debugger) { - Q_ASSERT(!debugger); - debugger = debugger_; + Q_ASSERT(!m_debugger); + m_debugger = debugger; } -void ExecutionEngine::setProfiler(Profiling::Profiler *profiler_) +void ExecutionEngine::setProfiler(Profiling::Profiler *profiler) { - Q_ASSERT(!profiler); - profiler = profiler_; + Q_ASSERT(!m_profiler); + m_profiler = profiler; } void ExecutionEngine::initRootContext() @@ -910,8 +910,8 @@ ReturnedValue ExecutionEngine::throwError(const Value &value) else exceptionStackTrace = stackTrace(); - if (debugger) - debugger->aboutToThrow(); + if (QV4::Debugging::Debugger *debug = debugger()) + debug->aboutToThrow(); return Encode::undefined(); } diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 208598efd5..7c8c881255 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -137,9 +137,6 @@ public: IdentifierTable *identifierTable; - QV4::Debugging::Debugger *debugger; - QV4::Profiling::Profiler *profiler; - Object *globalObject; Function *globalCode; @@ -382,6 +379,9 @@ public: ExecutionEngine(EvalISelFactory *iselFactory = 0); ~ExecutionEngine(); + QV4::Debugging::Debugger *debugger() const { return m_debugger; } + QV4::Profiling::Profiler *profiler() const { return m_profiler; } + void setDebugger(Debugging::Debugger *debugger); void setProfiler(Profiling::Profiler *profiler); @@ -484,6 +484,9 @@ public: private: void failStackLimitCheck(Scope &scope); + + QV4::Debugging::Debugger *m_debugger; + QV4::Profiling::Profiler *m_profiler; }; // This is a trick to tell the code generators that functions taking a NoThrowContext won't diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index fce80c46eb..6b9c552350 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -283,7 +283,7 @@ void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callDa return; } - IR::Module module(scope.engine->debugger != 0); + IR::Module module(scope.engine->debugger() != 0); QQmlJS::RuntimeCodegen cg(scope.engine, f->strictMode()); cg.generateFromFunctionExpression(QString(), function, fe, &module); diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index fd14c0ae93..e06cb64a61 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -76,19 +76,19 @@ QT_END_NAMESPACE #else #define Q_V4_PROFILE_ALLOC(engine, size, type)\ - (engine->profiler &&\ - (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ - engine->profiler->trackAlloc(size, type) : false) + (engine->profiler() &&\ + (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ + engine->profiler()->trackAlloc(size, type) : false) #define Q_V4_PROFILE_DEALLOC(engine, size, type) \ - (engine->profiler &&\ - (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ - engine->profiler->trackDealloc(size, type) : false) + (engine->profiler() &&\ + (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ + engine->profiler()->trackDealloc(size, type) : false) #define Q_V4_PROFILE(engine, function)\ - (engine->profiler &&\ - (engine->profiler->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\ - Profiling::FunctionCallProfiler::profileCall(engine->profiler, engine, function) :\ + (engine->profiler() &&\ + (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\ + Profiling::FunctionCallProfiler::profileCall(engine->profiler(), engine, function) :\ function->code(engine, function->codeData)) QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 97f5cbb786..a2e379ec1a 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -148,7 +148,7 @@ void Script::parse() MemoryManager::GCBlocker gcBlocker(v4->memoryManager); - IR::Module module(v4->debugger != 0); + IR::Module module(v4->debugger() != 0); QQmlJS::Engine ee, *engine = ⅇ Lexer lexer(engine); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 9b46871c85..b6dc7716ba 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -909,7 +909,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code #ifndef QT_NO_QML_DEBUGGER MOTH_BEGIN_INSTR(Debug) engine->current->lineNumber = instr.lineNumber; - QV4::Debugging::Debugger *debugger = context->engine()->debugger; + QV4::Debugging::Debugger *debugger = context->engine()->debugger(); if (debugger && debugger->pauseAtNextOpportunity()) debugger->maybeBreakAtInstruction(); if (qt_v4IsDebugging) @@ -974,7 +974,7 @@ void **VME::instructionJumpTable() QV4::ReturnedValue VME::exec(ExecutionEngine *engine, const uchar *code) { VME vme; - QV4::Debugging::Debugger *debugger = engine->debugger; + QV4::Debugging::Debugger *debugger = engine->debugger(); if (debugger) debugger->enteringFunction(); QV4::ReturnedValue retVal = vme.run(engine, code); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 09e10005a0..a61387e411 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2314,7 +2314,7 @@ void QQmlTypeData::dataReceived(const Data &data) return; } QQmlEngine *qmlEngine = typeLoader()->engine(); - m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0)); + m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0)); m_document->jsModule.sourceTimeStamp = sourceTimeStamp; QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) { @@ -2338,7 +2338,7 @@ void QQmlTypeData::dataReceived(const Data &data) void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit) { QQmlEngine *qmlEngine = typeLoader()->engine(); - m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0)); + m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0)); unit->loadIR(m_document.data(), unit); continueLoadFromIR(); } @@ -2850,7 +2850,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) } - QmlIR::Document irUnit(v4->debugger != 0); + QmlIR::Document irUnit(v4->debugger() != 0); QString error; QString source = QString::fromUtf8(data.readAll(&error, &irUnit.jsModule.sourceTimeStamp)); diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 3f89913f3b..a23b7e37eb 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -326,7 +326,7 @@ private slots: private: QV4Debugger *debugger() const { - return static_cast(m_v4->debugger); + return static_cast(m_v4->debugger()); } void evaluateJavaScript(const QString &script, const QString &fileName, int lineNumber = 1) { @@ -444,7 +444,7 @@ void tst_qv4debugger::addBreakPointWhilePaused() static QV4::ReturnedValue someCall(QV4::CallContext *ctx) { - static_cast(ctx->d()->engine->debugger) + static_cast(ctx->d()->engine->debugger()) ->removeBreakPoint("removeBreakPointForNextInstruction", 2); return QV4::Encode::undefined(); } -- cgit v1.2.3 From 91deafdcc8e4b743e2d27a1793a2b1b2ba506cd7 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 28 Jul 2016 10:21:06 +0200 Subject: Remove some unused members on -no-qml-debug It's debatable if the uglification is worth the overhead of a few pointers. Change-Id: I63b55b2043b8752a94d4b862c3892915135a72b7 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4jsir_p.h | 8 ++++++++ src/qml/debugger/qqmlprofiler_p.h | 8 ++++---- src/qml/jsruntime/qv4engine.cpp | 6 ++++++ src/qml/jsruntime/qv4engine_p.h | 10 ++++++++++ src/qml/qml/qqmlengine.cpp | 7 ++++++- src/qml/qml/qqmlengine_p.h | 5 +++++ src/qml/qml/qqmlobjectcreator.cpp | 2 +- 7 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index de84accbb1..51b8797862 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -934,7 +934,11 @@ struct Q_QML_PRIVATE_EXPORT Module { QString fileName; qint64 sourceTimeStamp; bool isQmlModule; // implies rootFunction is always 0 +#ifdef QT_NO_QML_DEBUGGER + static const bool debugMode = false; +#else bool debugMode; +#endif Function *newFunction(const QString &name, Function *outer); @@ -942,8 +946,12 @@ struct Q_QML_PRIVATE_EXPORT Module { : rootFunction(0) , sourceTimeStamp(0) , isQmlModule(false) +#ifndef QT_NO_QML_DEBUGGER , debugMode(debugMode) {} +#else + { Q_UNUSED(debugMode); } +#endif ~Module(); void setFileName(const QString &name); diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 483252df6d..6643695d11 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -73,23 +73,23 @@ struct QQmlProfiler {}; struct QQmlBindingProfiler { - QQmlBindingProfiler(QQmlProfiler *, QQmlBinding *, QV4::FunctionObject *) {} + QQmlBindingProfiler(quintptr, QQmlBinding *, QV4::FunctionObject *) {} }; struct QQmlHandlingSignalProfiler { - QQmlHandlingSignalProfiler(QQmlProfiler *, QQmlBoundSignalExpression *) {} + QQmlHandlingSignalProfiler(quintptr, QQmlBoundSignalExpression *) {} }; struct QQmlCompilingProfiler { - QQmlCompilingProfiler(QQmlProfiler *, QQmlDataBlob *) {} + QQmlCompilingProfiler(quintptr, QQmlDataBlob *) {} }; struct QQmlVmeProfiler { QQmlVmeProfiler() {} - void init(QQmlProfiler *, int) {} + void init(quintptr, int) {} const QV4::CompiledData::Object *pop() { return nullptr; } void push(const QV4::CompiledData::Object *) {} diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 785094b96a..f5bae4b258 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -143,8 +143,10 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , m_engineId(engineSerial.fetchAndAddOrdered(1)) , regExpCache(0) , m_multiplyWrappedQObjects(0) +#ifndef QT_NO_QML_DEBUGGER , m_debugger(0) , m_profiler(0) +#endif { if (maxCallDepth == -1) { bool ok = false; @@ -442,10 +444,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) ExecutionEngine::~ExecutionEngine() { +#ifndef QT_NO_QML_DEBUGGER delete m_debugger; m_debugger = 0; delete m_profiler; m_profiler = 0; +#endif delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = 0; delete identifierTable; @@ -467,6 +471,7 @@ ExecutionEngine::~ExecutionEngine() delete [] argumentsAccessors; } +#ifndef QT_NO_QML_DEBUGGER void ExecutionEngine::setDebugger(Debugging::Debugger *debugger) { Q_ASSERT(!m_debugger); @@ -478,6 +483,7 @@ void ExecutionEngine::setProfiler(Profiling::Profiler *profiler) Q_ASSERT(!m_profiler); m_profiler = profiler; } +#endif // QT_NO_QML_DEBUGGER void ExecutionEngine::initRootContext() { diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 7c8c881255..f42f727295 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -379,11 +379,19 @@ public: ExecutionEngine(EvalISelFactory *iselFactory = 0); ~ExecutionEngine(); +#ifdef QT_NO_QML_DEBUGGER + QV4::Debugging::Debugger *debugger() const { return nullptr; } + QV4::Profiling::Profiler *profiler() const { return nullptr; } + + void setDebugger(Debugging::Debugger *) {} + void setProfiler(Profiling::Profiler *) {} +#else QV4::Debugging::Debugger *debugger() const { return m_debugger; } QV4::Profiling::Profiler *profiler() const { return m_profiler; } void setDebugger(Debugging::Debugger *debugger); void setProfiler(Profiling::Profiler *profiler); +#endif // QT_NO_QML_DEBUGGER ExecutionContext *pushGlobalContext(); void pushContext(Heap::ExecutionContext *context); @@ -485,8 +493,10 @@ public: private: void failStackLimitCheck(Scope &scope); +#ifndef QT_NO_QML_DEBUGGER QV4::Debugging::Debugger *m_debugger; QV4::Profiling::Profiler *m_profiler; +#endif }; // This is a trick to tell the code generators that functions taking a NoThrowContext won't diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 6e77bc551c..e4e7530af7 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -602,7 +602,10 @@ the same object as is returned from the Qt.include() call. QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) : propertyCapture(0), rootContext(0), - profiler(0), outputWarningsToMsgLog(true), +#ifndef QT_NO_QML_DEBUGGER + profiler(0), +#endif + outputWarningsToMsgLog(true), cleanup(0), erroredBindings(0), inProgressCreations(0), workerScriptEngine(0), activeObjectCreator(0), @@ -645,7 +648,9 @@ QQmlEnginePrivate::~QQmlEnginePrivate() QMetaType::unregisterType(iter.value()->metaTypeId); QMetaType::unregisterType(iter.value()->listMetaTypeId); } +#ifndef QT_NO_QML_DEBUGGER delete profiler; +#endif } void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 4269392743..949060f395 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -134,7 +134,12 @@ public: QRecyclePool jsExpressionGuardPool; QQmlContext *rootContext; + +#ifdef QT_NO_QML_DEBUGGER + static const quintptr profiler = 0; +#else QQmlProfiler *profiler; +#endif bool outputWarningsToMsgLog; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 4e98e04ce4..a38d18acfb 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -89,7 +89,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::Compil sharedState->creationContext = creationContext; sharedState->rootContext = 0; - if (QQmlProfiler *profiler = QQmlEnginePrivate::get(engine)->profiler) { + if (auto profiler = QQmlEnginePrivate::get(engine)->profiler) { Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler, sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount)); } else { -- cgit v1.2.3 From 84d6defe81bdd67dc986f3179927ff4c72d75d66 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 28 Jul 2016 18:22:43 +0200 Subject: Minor cleanup: Using point.id makes more sense here Change-Id: I90ae1958e92f62538ab63f40f00d7e3ec7206bbb Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index c0fdc91699..f108ee8093 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -647,7 +647,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e touchMouseDevice = device; // FIXME: this is a bit backwards, should just have the pointer event passed into the function - auto pointerEventPoint = device->pointerEvent()->pointById(touchMouseId); + auto pointerEventPoint = device->pointerEvent()->pointById(p.id()); pointerEventPoint->setGrabber(item); qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item; QScopedPointer mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); -- cgit v1.2.3 From 05d7fb5bd261746f4fc5ed5c23fd0b1bb8f3c087 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 28 Jul 2016 18:26:54 +0200 Subject: Prefer QCOMPARE over QVERIFY Change-Id: Id57e576653cd66d1b36685737ba477c8b3ff6258 Reviewed-by: Frederik Gladhorn --- tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 0ef17267b7..adf0d282c5 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -1904,9 +1904,9 @@ void tst_QQuickMouseArea::ignoreBySource() // MouseArea should grab the press because it's interested in non-synthesized mouse events QPoint p = QPoint(80, 80); QTest::mousePress(&window, Qt::LeftButton, 0, p); - QVERIFY(window.mouseGrabberItem() == mouseArea); + QCOMPARE(window.mouseGrabberItem(), mouseArea); // That was a real mouse event - QVERIFY(root->property("lastEventSource").toInt() == Qt::MouseEventNotSynthesized); + QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventNotSynthesized)); // Flickable content should not move p -= QPoint(startDragDistance() + 1, startDragDistance() + 1); @@ -1919,12 +1919,14 @@ void tst_QQuickMouseArea::ignoreBySource() QCOMPARE(flickable->contentY(), 0.); QTest::mouseRelease(&window, Qt::LeftButton, 0, p); + QCOMPARE(window.mouseGrabberItem(), nullptr); // Now try touch events and confirm that MouseArea ignores them, while Flickable does its thing p = QPoint(80, 80); QTest::touchEvent(&window, device).press(0, p, &window); QQuickTouchUtils::flush(&window); - QVERIFY(window.mouseGrabberItem() != mouseArea); + QCOMPARE(window.mouseGrabberItem(), flickable); + // That was a fake mouse event QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventSynthesizedByQt)); p -= QPoint(startDragDistance() + 1, startDragDistance() + 1); -- cgit v1.2.3 From 80dbc30e97c4c6def74a026ce0b50390990cc612 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 26 Jul 2016 23:52:31 +0200 Subject: Touch and mouse handling: don't pre-grab The touchmouse test needs adjusting since we don't see ungrab events any more (which makes sense, nobody actively grabbed anything there). The comment about storing the touchMouseId early enough is invalid - any item that wants to steal the grab, has to call grab anyway. The reason to only update the grab when there is no grabber set yet, is that in the filterChildMouse stuff some other item might steal the event, setting the grabber there - that must not be overridden when we come back to the original event handling. Change-Id: I4f191b16f4d76113d0c88414b5a80eb1baf99be1 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 34 ++++++++------------------ tests/auto/quick/touchmouse/tst_touchmouse.cpp | 7 +++--- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index f108ee8093..53f86fb3f1 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -640,12 +640,6 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e if (!item->contains(pos)) break; - // Store the id already here and restore it to -1 if the event does not get - // accepted. Cannot defer setting the new value because otherwise if the event - // handler spins the event loop all subsequent moves and releases get lost. - touchMouseId = p.id(); - touchMouseDevice = device; - // FIXME: this is a bit backwards, should just have the pointer event passed into the function auto pointerEventPoint = device->pointerEvent()->pointById(p.id()); pointerEventPoint->setGrabber(item); @@ -653,22 +647,14 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e QScopedPointer mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); // Send a single press and see if that's accepted - if (!q->mouseGrabberItem()) - item->grabMouse(); - item->grabTouchPoints(QVector() << touchMouseId); - QCoreApplication::sendEvent(item, mousePress.data()); event->setAccepted(mousePress->isAccepted()); - if (!mousePress->isAccepted()) { - touchMouseId = -1; - touchMouseDevice = nullptr; - if (pointerEventPoint->grabber() == item) { - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "disassociated"; - pointerEventPoint->setGrabber(nullptr); - } - - if (q->mouseGrabberItem() == item) - item->ungrabMouse(); + if (mousePress->isAccepted()) { + touchMouseDevice = device; + touchMouseId = p.id(); + if (!q->mouseGrabberItem()) + item->grabMouse(); + item->grabTouchPoints(QVector() << touchMouseId); } if (mousePress->isAccepted() && checkIfDoubleClicked(event->timestamp())) { @@ -1652,13 +1638,13 @@ bool QQuickWindowPrivate::deliverInitialMousePressEvent(QMouseEvent *event) if (item->contains(localPos)) { QScopedPointer me(cloneMouseEvent(event, &localPos)); me->accept(); - item->grabMouse(); q->sendEvent(item, me.data()); event->setAccepted(me->isAccepted()); - if (me->isAccepted()) + if (me->isAccepted()) { + if (!q->mouseGrabberItem()) + item->grabMouse(); return true; - if (q->mouseGrabberItem()) - q->mouseGrabberItem()->ungrabMouse(); + } } } } diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 24682bb7a3..4170a31a54 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -285,17 +285,16 @@ void tst_TouchMouse::simpleTouchEvent() p1 = QPoint(20, 20); QTest::touchEvent(window, device).press(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); - QCOMPARE(eventItem1->eventList.at(2).type, QEvent::UngrabMouse); p1 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 2); QTest::touchEvent(window, device).release(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 2); eventItem1->eventList.clear(); // wait to avoid getting a double click event -- cgit v1.2.3 From e9108b487b1be5fd429512b046f8febb5d23a618 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 28 Jul 2016 20:15:38 +0200 Subject: Unite repeated if statements Change-Id: Ifa61b86746858af0e1acb3b40687de17e510b7d1 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 53f86fb3f1..7037bb54f9 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -655,22 +655,21 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e if (!q->mouseGrabberItem()) item->grabMouse(); item->grabTouchPoints(QVector() << touchMouseId); - } - if (mousePress->isAccepted() && checkIfDoubleClicked(event->timestamp())) { - QScopedPointer mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); - QCoreApplication::sendEvent(item, mouseDoubleClick.data()); - event->setAccepted(mouseDoubleClick->isAccepted()); - if (mouseDoubleClick->isAccepted()) { - touchMouseIdCandidates.clear(); - return true; - } else { - touchMouseId = -1; - touchMouseDevice = nullptr; + if (checkIfDoubleClicked(event->timestamp())) { + QScopedPointer mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); + QCoreApplication::sendEvent(item, mouseDoubleClick.data()); + event->setAccepted(mouseDoubleClick->isAccepted()); + if (mouseDoubleClick->isAccepted()) { + touchMouseIdCandidates.clear(); + return true; + } else { + touchMouseId = -1; + touchMouseDevice = nullptr; + } } - } - // The event was accepted, we are done. - if (mousePress->isAccepted()) { + + // The event was accepted, we are done. touchMouseIdCandidates.clear(); return true; } -- cgit v1.2.3 From 34a812aa55194c726fa61d5d89d9f4f044e0720d Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 28 Jul 2016 20:17:20 +0200 Subject: Remove duplicate code Change-Id: I51e3502ac091636d7f9df7ebfb9a6e883e187d78 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 7037bb54f9..59dd9a2523 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -660,10 +660,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e QScopedPointer mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); QCoreApplication::sendEvent(item, mouseDoubleClick.data()); event->setAccepted(mouseDoubleClick->isAccepted()); - if (mouseDoubleClick->isAccepted()) { - touchMouseIdCandidates.clear(); - return true; - } else { + if (!mouseDoubleClick->isAccepted()) { touchMouseId = -1; touchMouseDevice = nullptr; } -- cgit v1.2.3 From 51d9db2a8adb2c08e1d52350eff6a8c0fc3cb857 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 26 Jul 2016 23:38:53 +0200 Subject: Touch cleanup Get rid of all the lists and sets that were passed around before. Change-Id: I75371742a598ba658c3d0b0aea30e3f8a60f9203 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 91 ++++++++++------------------------------ src/quick/items/qquickwindow_p.h | 4 +- 2 files changed, 25 insertions(+), 70 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 59dd9a2523..3e12d94529 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2182,37 +2182,14 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); - // List of all items that received an event before - // When we have TouchBegin this is and will stay empty - QHash > updatedPoints; - - // Figure out who accepted a touch point last and put it in updatedPoints - // Add additional item to newPoints - int pointCount = event->pointCount(); - QList newPoints; - for (int i = 0; i < pointCount; ++i) { - const QQuickEventPoint *point = event->point(i); - if (point->state() == Qt::TouchPointPressed) { - newPoints << point; - } else { - // TouchPointStationary is relevant only to items which - // are also receiving touch points with some other state. - // But we have not yet decided which points go to which item, - // so for now we must include all non-new points in updatedPoints. - if (QQuickItem *grabber = point->grabber()) - updatedPoints[grabber].append(point); - } - } - // Deliver the event, but only if there is at least one new point // or some item accepted a point and should receive an update - if (newPoints.count() > 0 || updatedPoints.count() > 0) { - QSet hasFiltered; - deliverPoints(contentItem, event, newPoints, &updatedPoints, &hasFiltered); - } + QSet hasFiltered; + deliverPoints(contentItem, event, &hasFiltered); // Remove released points from itemForTouchPointId bool allReleased = true; + int pointCount = event->pointCount(); for (int i = 0; i < pointCount; ++i) { QQuickEventPoint *point = event->point(i); if (point->state() == Qt::TouchPointReleased) { @@ -2236,20 +2213,10 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) } // This function recurses and sends the events to the individual items -bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEvent *event, const QList &newPoints, - QHash > *updatedPoints, - QSet *hasFiltered) +bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEvent *event, QSet *hasFiltered) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - for (const QQuickEventPoint * point : qAsConst(newPoints)) { - QPointF p = item->mapFromScene(point->scenePos()); - if (!item->contains(p)) - return false; - } - } - // Check if our children want the event (or parts of it) // This is the only point where touch event delivery recurses! QList children = itemPrivate->paintOrderChildItems(); @@ -2257,7 +2224,7 @@ bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEven QQuickItem *child = children.at(ii); if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled) continue; - if (deliverPoints(child, event, newPoints, updatedPoints, hasFiltered)) + if (deliverPoints(child, event, hasFiltered)) return true; } @@ -2268,25 +2235,8 @@ bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEven // which was already accepted by that item when it was first pressed. // (A point which was already accepted is effectively "grabbed" by the item.) - // set of IDs of "interesting" new points - QSet matchingNewPoints; - // now add the new points which are inside this item's bounds - if (newPoints.count() > 0) { - for (int i = 0; i < newPoints.count(); i++) { - const QQuickEventPoint * point = newPoints[i]; - if (point->isAccepted()) - continue; - QPointF p = item->mapFromScene(point->scenePos()); - if (item->contains(p)) - matchingNewPoints.insert(point->pointId()); - } - } - // This item might be interested in the event. - deliverMatchingPointsToItem(item, event, matchingNewPoints, hasFiltered); - - // record the fact that this item has been visited already - updatedPoints->remove(item); + deliverMatchingPointsToItem(item, event, hasFiltered); // recursion is done only if ALL touch points have been delivered return event->allPointsAccepted(); @@ -2296,9 +2246,7 @@ bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEven // only the points that are relevant for this item. Thus the need for // matchingPoints to already be that set of interesting points. // They are all pre-transformed, too. -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, - const QSet &matchingNewPoints, - QSet *hasFiltered) +bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *hasFiltered) { QScopedPointer touchEvent(event->touchEventForItem(item)); if (!touchEvent) @@ -2313,8 +2261,9 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; - foreach (int id, matchingNewPoints) - event->pointById(id)->setAccepted(); + for (auto point: qAsConst(touchEvent->touchPoints())) { + event->pointById(point.id())->setAccepted(); + } return true; } @@ -2334,18 +2283,24 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ if (touchEventAccepted) { // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints. - foreach (int id, matchingNewPoints) { - event->pointById(id)->setAccepted(); - event->pointById(id)->setGrabber(item); + for (auto point: qAsConst(touchEvent->touchPoints())) { + if (point.state() == Qt::TouchPointPressed) { + auto pointerEventPoint = event->pointById(point.id()); + pointerEventPoint->setAccepted(); + pointerEventPoint->setGrabber(item); + } } } else { // But if the event was not accepted then we know this item // will not be interested in further updates for those touchpoint IDs either. - foreach (int id, matchingNewPoints) - if (event->pointById(id)->grabber() == item) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "disassociated"; - event->pointById(id)->setGrabber(nullptr); + for (auto point: qAsConst(touchEvent->touchPoints())) { + if (point.state() == Qt::TouchPointPressed) { + if (event->pointById(point.id())->grabber() == item) { + qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated"; + event->pointById(point.id())->setGrabber(nullptr); + } } + } } return touchEventAccepted; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index eff23b1b9a..5b447efdc1 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -167,8 +167,8 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPoints(QQuickItem *, QQuickPointerTouchEvent *, const QList &, QHash > *, QSet *); - bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, const QSet &matchingNewPoints, QSet *filtered); + bool deliverPoints(QQuickItem *, QQuickPointerTouchEvent *, QSet *); + bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); -- cgit v1.2.3 From aeb0b305dfe36033dd49a699d30d6d1d0d140bae Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 28 Jul 2016 23:49:17 +0200 Subject: Remove unused parameter The function was only called in one place, no need to have an extra parameter if it's always going to have the same value. Change-Id: I29ed6ca1b7cc3722d11fb3f42ffc640e48c18af8 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 4 ++-- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 3e12d94529..9f6680a04c 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2307,7 +2307,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ } // create touch event containing only points inside the target item -QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds) +QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent) { const QList &touchPoints = originalEvent.touchPoints(); QList pointsInBounds; @@ -2318,7 +2318,7 @@ QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QT // Touch presses are relevant to the target item only if they occur inside its bounds. // Touch updates and releases are relevant if they occur inside, or if we want to // finish the sequence because the press occurred inside. - if (tp.state() == Qt::TouchPointPressed || alwaysCheckBounds) { + if (tp.state() == Qt::TouchPointPressed) { QPointF p = target->mapFromScene(tp.scenePos()); if (target->contains(p)) pointsInBounds.append(tp); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 5b447efdc1..5978a7309f 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -169,7 +169,7 @@ public: bool deliverTouchCancelEvent(QTouchEvent *); bool deliverPoints(QQuickItem *, QQuickPointerTouchEvent *, QSet *); bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *filtered); - static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); + static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent); static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); -- cgit v1.2.3 From 5d095ab7cbf306436ebe10b52392c85abdacac26 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 28 Jul 2016 23:50:52 +0200 Subject: Remove outdated comment Change-Id: I10a4c2c7a642de0cb0ec822d029a4464c86f8e00 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 9f6680a04c..ac6692365f 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2182,8 +2182,6 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); - // Deliver the event, but only if there is at least one new point - // or some item accepted a point and should receive an update QSet hasFiltered; deliverPoints(contentItem, event, &hasFiltered); -- cgit v1.2.3 From 6001b22424314772ccece363f82e768fd5639ae2 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 28 Jul 2016 23:52:02 +0200 Subject: Remove return value that is always ignored Change-Id: I26ffdebf594422588febc4a031a88fa5e8e3f970 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 7 ++----- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ac6692365f..1e08a0117f 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1647,7 +1647,7 @@ bool QQuickWindowPrivate::deliverInitialMousePressEvent(QMouseEvent *event) return false; } -bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) +void QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) { Q_Q(QQuickWindow); @@ -1661,7 +1661,7 @@ bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) event->accept(); else event->ignore(); - return event->isAccepted(); + return; } if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { @@ -1670,10 +1670,7 @@ bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) me->accept(); q->sendEvent(mouseGrabberItem, me.data()); event->setAccepted(me->isAccepted()); - return me->isAccepted(); } - - return false; } bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 5978a7309f..6391d76687 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -146,7 +146,7 @@ public: static void transformTouchPoints(QList &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); bool deliverInitialMousePressEvent(QMouseEvent *); - bool deliverMouseEvent(QMouseEvent *); + void deliverMouseEvent(QMouseEvent *); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet *); #ifndef QT_NO_WHEELEVENT bool deliverWheelEvent(QQuickItem *, QWheelEvent *); -- cgit v1.2.3 From c796b43c429a7a3f04784e32b1e1a07d9da57109 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 29 Jul 2016 00:04:58 +0200 Subject: Simplify QQuickWindowPrivate::deliverMouseEvent Change-Id: Ief331638fdb12fa87b820c15b44cc305600bcac5 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 1e08a0117f..16d99e961a 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1653,23 +1653,22 @@ void QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) lastMousePosition = event->windowPos(); - if (!q->mouseGrabberItem() && - (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) && - (event->buttons() & event->button()) == event->buttons()) { + QQuickItem *mouseGrabberItem = q->mouseGrabberItem(); - if (deliverInitialMousePressEvent(event)) - event->accept(); - else - event->ignore(); - return; - } - - if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { + if (mouseGrabberItem) { + // send update QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos()); QScopedPointer me(cloneMouseEvent(event, &localPos)); me->accept(); q->sendEvent(mouseGrabberItem, me.data()); event->setAccepted(me->isAccepted()); + } else { + // send initial press + if ((event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) && + (event->buttons() & event->button()) == event->buttons()) { + bool delivered = deliverInitialMousePressEvent(event); + event->setAccepted(delivered); + } } } -- cgit v1.2.3 From 451efdbe41d3381c3d8c42117662c2eea5436ff0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 29 Jul 2016 00:11:42 +0200 Subject: Move mouse release handling into deliverMouseEvent Change-Id: I4071a5fa217230883b6b99b2f37cdcd459aa14c5 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 16d99e961a..72b181fcdd 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1662,8 +1662,13 @@ void QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) me->accept(); q->sendEvent(mouseGrabberItem, me.data()); event->setAccepted(me->isAccepted()); + + // release event, make sure to ungrab if there still is a grabber + if (event->type() == QEvent::MouseButtonRelease && !event->buttons() && q->mouseGrabberItem()) + q->mouseGrabberItem()->ungrabMouse(); } else { // send initial press + event->setAccepted(false); if ((event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) && (event->buttons() & event->button()) == event->buttons()) { bool delivered = deliverInitialMousePressEvent(event); @@ -2022,14 +2027,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonRelease: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(), event->buttons()); - if (!q->mouseGrabberItem()) { - event->ignore(); - return; - } - deliverPointerEvent(pointerEventInstance(event)); - if (q->mouseGrabberItem() && !event->buttons()) - q->mouseGrabberItem()->ungrabMouse(); break; case QEvent::MouseButtonDblClick: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, -- cgit v1.2.3 From fb711c1b9c93313cc19719d11b6611fc79467903 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 29 Jul 2016 00:24:22 +0200 Subject: Simplify keeping track of lastMousePosition Change-Id: I19436b4c6cd0dc8a427d763fd2fc0b230a492df8 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 72b181fcdd..8895a43faa 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2045,9 +2045,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) #endif if (!q->mouseGrabberItem()) { - if (lastMousePosition.isNull()) - lastMousePosition = event->windowPos(); - QPointF last = lastMousePosition; + QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition; lastMousePosition = event->windowPos(); bool accepted = event->isAccepted(); -- cgit v1.2.3 From fe92db0a0eeed502ef851930b3404b38c4a03f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Martins?= Date: Fri, 29 Jul 2016 13:32:22 +0100 Subject: Revert "macOS: Use sRGB when doing native font rendering into FBO" This reverts commit 1e18a4f985f6ec4a0191a2e0cc087b13d29b1719. It breaks a QtCanvas3D unit-test and I can't look at it now. Will have another take at this soon. Change-Id: I22acd55443783934596d25cc4c8774bd34609f6b Reviewed-by: Liang Qi --- src/quick/scenegraph/qsgdefaultlayer.cpp | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/src/quick/scenegraph/qsgdefaultlayer.cpp b/src/quick/scenegraph/qsgdefaultlayer.cpp index 99735564ef..fa69f911dd 100644 --- a/src/quick/scenegraph/qsgdefaultlayer.cpp +++ b/src/quick/scenegraph/qsgdefaultlayer.cpp @@ -42,29 +42,6 @@ DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY) #endif DEFINE_BOOL_CONFIG_OPTION(qmlFboFlushBeforeDetach, QML_FBO_FLUSH_BEFORE_DETACH) - - -static QOpenGLFramebufferObject *createFramebuffer(const QSize &size, - QOpenGLFramebufferObjectFormat format) -{ -#ifdef Q_OS_MACOS - QOpenGLContext *context = QOpenGLContext::currentContext(); - if (context->hasExtension("GL_ARB_framebuffer_sRGB") - && context->hasExtension("GL_EXT_texture_sRGB") - && context->hasExtension("GL_EXT_texture_sRGB_decode")) - format.setInternalTextureFormat(GL_SRGB8_ALPHA8_EXT); -#endif - QOpenGLFramebufferObject *fbo = new QOpenGLFramebufferObject(size, format); -#ifdef Q_OS_MACOS - if (format.internalTextureFormat() == GL_SRGB8_ALPHA8_EXT) { - QOpenGLFunctions *funcs = context->functions(); - funcs->glBindTexture(GL_TEXTURE_2D, fbo->texture()); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT); - } -#endif - return fbo; -} - namespace { class BindableFbo : public QSGBindable @@ -347,7 +324,7 @@ void QSGDefaultLayer::grab() format.setInternalTextureFormat(m_format); format.setSamples(m_context->openglContext()->format().samples()); - m_secondaryFbo = createFramebuffer(m_size, format); + m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo); } else { QOpenGLFramebufferObjectFormat format; @@ -356,14 +333,14 @@ void QSGDefaultLayer::grab() if (m_recursive) { deleteFboLater = true; delete m_secondaryFbo; - m_secondaryFbo = createFramebuffer(m_size, format); + m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); funcs->glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture()); updateBindOptions(true); m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo); } else { delete m_fbo; delete m_secondaryFbo; - m_fbo = createFramebuffer(m_size, format); + m_fbo = new QOpenGLFramebufferObject(m_size, format); m_secondaryFbo = 0; funcs->glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); updateBindOptions(true); @@ -377,7 +354,7 @@ void QSGDefaultLayer::grab() Q_ASSERT(m_fbo); Q_ASSERT(!m_multisampling); - m_secondaryFbo = createFramebuffer(m_size, m_fbo->format()); + m_secondaryFbo = new QOpenGLFramebufferObject(m_size, m_fbo->format()); funcs->glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture()); updateBindOptions(true); } -- cgit v1.2.3 From bafb73b22a601eeae490b8225b33b39bf2d337e2 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 29 Jul 2016 16:16:36 +0200 Subject: Accept points inside QQuickPointerEvent Before the points for move events were not accepted, which is inconsistent. We know that we can stop delivering as soon as all points are accepted. Change-Id: I48d286be715eca16771e9ec0b107398fafb94194 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 8895a43faa..e3c80a8638 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2274,11 +2274,10 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints. for (auto point: qAsConst(touchEvent->touchPoints())) { - if (point.state() == Qt::TouchPointPressed) { - auto pointerEventPoint = event->pointById(point.id()); - pointerEventPoint->setAccepted(); + auto pointerEventPoint = event->pointById(point.id()); + pointerEventPoint->setAccepted(); + if (point.state() == Qt::TouchPointPressed) pointerEventPoint->setGrabber(item); - } } } else { // But if the event was not accepted then we know this item -- cgit v1.2.3 From 7ddb47f3aea660774353276c328efa817cbf16a8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 28 Jul 2016 17:40:26 +0200 Subject: Simplify control flow in the type loader Using a golang-style defer lambda to run "cleanup" code that should always be run allows us to do early returns when encountering errors. Change-Id: I92ff3e05a99f15f04e3d0f8b9ad258e549c491fc Reviewed-by: Ulf Hermann --- src/qml/qml/qqmltypeloader.cpp | 76 +++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index a61387e411..2578f44355 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -64,6 +64,8 @@ #include #include +#include + #if defined (Q_OS_UNIX) #include #include @@ -114,6 +116,20 @@ namespace { LockHolder(LockType *l) : lock(*l) { lock.lock(); } ~LockHolder() { lock.unlock(); } }; + + struct DeferredCall + { + std::function callback; + ~DeferredCall() { callback(); } + }; + + template + DeferredCall defer(Callback &&cb) + { + DeferredCall c; + c.callback = std::move(cb); + return c; + } } #ifndef QT_NO_NETWORK @@ -2145,8 +2161,18 @@ void QQmlTypeData::rebuildTypeAndPropertyCaches() void QQmlTypeData::done() { + auto cleanup = defer([this]{ + m_document.reset(); + m_typeReferences.clear(); + if (isError()) + m_compiledData = nullptr; + }); + + if (isError()) + return; + // Check all script dependencies for errors - for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { + for (int ii = 0; ii < m_scripts.count(); ++ii) { const ScriptReference &script = m_scripts.at(ii); Q_ASSERT(script.script->isCompleteOrError()); if (script.script->isError()) { @@ -2158,12 +2184,13 @@ void QQmlTypeData::done() error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString())); errors.prepend(error); setError(errors); + return; } } // Check all type dependencies for errors for (QHash::ConstIterator it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); - !isError() && it != end; ++it) { + it != end; ++it) { const TypeReference &type = *it; Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); if (type.typeData && type.typeData->isError()) { @@ -2177,11 +2204,12 @@ void QQmlTypeData::done() error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); errors.prepend(error); setError(errors); + return; } } // Check all composite singleton type dependencies for errors - for (int ii = 0; !isError() && ii < m_compositeSingletons.count(); ++ii) { + for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) { const TypeReference &type = m_compositeSingletons.at(ii); Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); if (type.typeData && type.typeData->isError()) { @@ -2195,19 +2223,21 @@ void QQmlTypeData::done() error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); errors.prepend(error); setError(errors); + return; } } - if (!isError()) { - if (!m_document.isNull()) { - // Compile component - compile(); - } else { - rebuildTypeAndPropertyCaches(); - } + if (!m_document.isNull()) { + // Compile component + compile(); + } else { + rebuildTypeAndPropertyCaches(); } - if (!isError()) { + if (isError()) + return; + + { QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); { // Sanity check property bindings @@ -2215,23 +2245,26 @@ void QQmlTypeData::done() QVector errors = validator.validate(); if (!errors.isEmpty()) { setError(errors); + return; } } m_compiledData->finalize(engine); } - if (!isError()) { + { QQmlType *type = QQmlMetaType::qmlType(finalUrl(), true); if (m_compiledData && m_compiledData->data->flags & QV4::CompiledData::Unit::IsSingleton) { if (!type) { QQmlError error; error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent.")); setError(error); + return; } else if (!type->isCompositeSingleton()) { QQmlError error; error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type->qmlTypeName())); setError(error); + return; } } else { // If the type is CompositeSingleton but there was no pragma Singleton in the @@ -2239,11 +2272,12 @@ void QQmlTypeData::done() if (type && type->isCompositeSingleton()) { QString typeName = type->qmlTypeName(); setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); + return; } } } - if (!isError()) { + { // Collect imported scripts m_compiledData->dependentScripts.reserve(m_scripts.count()); for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { @@ -2263,12 +2297,7 @@ void QQmlTypeData::done() scriptData->addref(); m_compiledData->dependentScripts << scriptData; } - } else { - m_compiledData = nullptr; } - - m_document.reset(); - m_typeReferences.clear(); } void QQmlTypeData::completed() @@ -2900,8 +2929,11 @@ void QQmlScriptBlob::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit * void QQmlScriptBlob::done() { + if (isError()) + return; + // Check all script dependencies for errors - for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { + for (int ii = 0; ii < m_scripts.count(); ++ii) { const ScriptReference &script = m_scripts.at(ii); Q_ASSERT(script.script->isCompleteOrError()); if (script.script->isError()) { @@ -2913,17 +2945,15 @@ void QQmlScriptBlob::done() error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString())); errors.prepend(error); setError(errors); + return; } } - if (isError()) - return; - m_scriptData->importCache = new QQmlTypeNameCache(); QSet ns; - for (int scriptIndex = 0; !isError() && scriptIndex < m_scripts.count(); ++scriptIndex) { + for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { const ScriptReference &script = m_scripts.at(scriptIndex); m_scriptData->scripts.append(script.script); -- cgit v1.2.3 From 93ed9cca8fdd21f9ecb26a3484b8b1507ab2b7b5 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 29 Jul 2016 17:14:09 +0200 Subject: Add QQuickPointerEvent::isPressEvent Change-Id: Ied358da23baabfad7e1f75e48a459f1bd5425102 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 12 ++++++++++++ src/quick/items/qquickevents_p_p.h | 3 +++ 2 files changed, 15 insertions(+) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 393d8345ec..46b6bbe87e 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -626,6 +626,13 @@ void QQuickPointerMouseEvent::clearGrabbers() const { m_mousePoint->setGrabber(nullptr); } +bool QQuickPointerMouseEvent::isPressEvent() const +{ + auto me = static_cast(m_event); + return ((me->type() == QEvent::MouseButtonPress || me->type() == QEvent::MouseButtonDblClick) && + (me->buttons() & me->button()) == me->buttons()); +} + bool QQuickPointerTouchEvent::allPointsAccepted() const { for (int i = 0; i < m_pointCount; ++i) { if (!m_touchPoints.at(i)->isAccepted()) @@ -652,6 +659,11 @@ void QQuickPointerTouchEvent::clearGrabbers() const { point->setGrabber(nullptr); } +bool QQuickPointerTouchEvent::isPressEvent() const +{ + return static_cast(m_event)->touchPointStates() & Qt::TouchPointPressed; +} + QVector QQuickPointerEvent::unacceptedPointScenePositions() const { QVector points; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 2375ae797b..b944a3d06d 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -352,6 +352,7 @@ public: // property accessors public: // helpers for C++ only (during event delivery) virtual QQuickPointerEvent *reset(QEvent *ev) = 0; + virtual bool isPressEvent() const = 0; virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; } virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; } @@ -389,6 +390,7 @@ public: } QQuickPointerEvent *reset(QEvent *) override; + bool isPressEvent() const override; QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; } const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; } int pointCount() const override { return 1; } @@ -412,6 +414,7 @@ public: {} QQuickPointerEvent *reset(QEvent *) override; + bool isPressEvent() const override; QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; } const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; } int pointCount() const override { return m_pointCount; } -- cgit v1.2.3 From 397588a9f68886608d2d257f70178ffc1b79e6ae Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 29 Jul 2016 15:09:56 +0200 Subject: Touch event delivery: split into press and update delivery This gets rid of iterating through all possible receivers when touch events get delivered, the event gets sent directly to the right item. Change-Id: I341bbdc095744a99c6c4011f07d5f5a239b7fe46 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 21 +++++++++++++++++---- src/quick/items/qquickwindow_p.h | 3 ++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index e3c80a8638..378b21c8cc 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2175,7 +2175,10 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); QSet hasFiltered; - deliverPoints(contentItem, event, &hasFiltered); + if (event->isPressEvent()) + deliverNewTouchPoints(contentItem, event, &hasFiltered); + if (!event->allPointsAccepted()) + deliverUpdatedTouchPoints(event, &hasFiltered); // Remove released points from itemForTouchPointId bool allReleased = true; @@ -2202,8 +2205,18 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) } } +// Deliver touch points to existing grabbers +bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered) +{ + for (auto grabber: event->grabbers()) { + deliverMatchingPointsToItem(grabber, event, hasFiltered); + } + + return false; +} + // This function recurses and sends the events to the individual items -bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEvent *event, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverNewTouchPoints(QQuickItem *item, QQuickPointerTouchEvent *event, QSet *hasFiltered) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -2214,7 +2227,7 @@ bool QQuickWindowPrivate::deliverPoints(QQuickItem *item, QQuickPointerTouchEven QQuickItem *child = children.at(ii); if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled) continue; - if (deliverPoints(child, event, hasFiltered)) + if (deliverNewTouchPoints(child, event, hasFiltered)) return true; } @@ -2272,7 +2285,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ if (touchEventAccepted) { // If the touch was accepted (regardless by whom or in what form), - // update acceptedNewPoints. + // update accepted new points. for (auto point: qAsConst(touchEvent->touchPoints())) { auto pointerEventPoint = event->pointById(point.id()); pointerEventPoint->setAccepted(); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 6391d76687..e5492cf031 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -167,7 +167,8 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPoints(QQuickItem *, QQuickPointerTouchEvent *, QSet *); + bool deliverNewTouchPoints(QQuickItem *, QQuickPointerTouchEvent *, QSet *); + bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered); bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent); static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); -- cgit v1.2.3 From b13668d5304c0c0cef116fb3126d772083a71563 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 29 Jul 2016 16:05:38 +0200 Subject: Make QQuickEventPoint::grabber a QPointer internally Accessing the grabbers was always dangerous. The present code only works because it iterates over all current QQuickItems and doesn't even try to deal with things being deleted inbetween. Change-Id: Id85791dcbd87ec8c5027f9c1376cb39e5779cabe Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 9 +++++++++ src/quick/items/qquickevents_p_p.h | 9 +++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 46b6bbe87e..966a78f56b 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -501,6 +501,15 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) return nullptr; } +QQuickItem *QQuickEventPoint::grabber() const +{ + return m_grabber.data(); +} + +void QQuickEventPoint::setGrabber(QQuickItem *grabber) +{ + m_grabber = QPointer(grabber); +} QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index b944a3d06d..571c257a26 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -55,13 +55,14 @@ #include #include +#include #include #include #include +#include QT_BEGIN_NAMESPACE -class QQuickItem; class QQuickPointerDevice; class QQuickPointerEvent; class QQuickPointerMouseEvent; @@ -281,13 +282,13 @@ public: qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } bool isAccepted() const { return m_accept; } void setAccepted(bool accepted = true) { m_accept = accepted; } - QQuickItem *grabber() const { return m_grabber; } - void setGrabber(QQuickItem *grabber) { m_grabber = grabber; } + QQuickItem *grabber() const; + void setGrabber(QQuickItem *grabber); private: QPointF m_scenePos; quint64 m_pointId; - QQuickItem *m_grabber; + QPointer m_grabber; ulong m_timestamp; ulong m_pressTimestamp; Qt::TouchPointState m_state; -- cgit v1.2.3 From 8e0c446b28c661944a8c014145134d80e2282a51 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 29 Jul 2016 13:24:38 +0200 Subject: Pass PointerEvent into deliverMouseEvent Change-Id: Ib9e1748cea24851ecc369da5d658654341d291ac Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 13 ++++++------- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 378b21c8cc..c1167ad8a5 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1647,14 +1647,14 @@ bool QQuickWindowPrivate::deliverInitialMousePressEvent(QMouseEvent *event) return false; } -void QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) +void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { Q_Q(QQuickWindow); + auto event = pointerEvent->asMouseEvent(); - lastMousePosition = event->windowPos(); + lastMousePosition = pointerEvent->point(0)->scenePos(); QQuickItem *mouseGrabberItem = q->mouseGrabberItem(); - if (mouseGrabberItem) { // send update QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos()); @@ -1669,8 +1669,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) } else { // send initial press event->setAccepted(false); - if ((event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) && - (event->buttons() & event->button()) == event->buttons()) { + if (pointerEvent->isPressEvent()) { bool delivered = deliverInitialMousePressEvent(event); event->setAccepted(delivered); } @@ -2129,8 +2128,8 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) // the usecase a bit evil, but we at least don't want to lose events. ++pointerEventRecursionGuard; - if (QQuickPointerMouseEvent *mouse = event->asPointerMouseEvent()) { - deliverMouseEvent(mouse->asMouseEvent()); + if (event->asPointerMouseEvent()) { + deliverMouseEvent(event->asPointerMouseEvent()); } else if (event->asPointerTouchEvent()) { deliverTouchEvent(event->asPointerTouchEvent()); } else { diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index e5492cf031..61d57692a7 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -146,7 +146,7 @@ public: static void transformTouchPoints(QList &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); bool deliverInitialMousePressEvent(QMouseEvent *); - void deliverMouseEvent(QMouseEvent *); + void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet *); #ifndef QT_NO_WHEELEVENT bool deliverWheelEvent(QQuickItem *, QWheelEvent *); -- cgit v1.2.3 From 30377d13b82799ad1d4449026b473843ae219855 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sun, 31 Jul 2016 21:27:58 +0200 Subject: Fix TouchMouse test: accept button That the EventItem would get an event is a bug in the current event handling, there is no reason for it to receive anything when it doesn't accept mouse/touch. Change-Id: I4079e6cb396cc1913f795015dcc7839ca79edb70 Reviewed-by: Frederik Gladhorn --- tests/auto/quick/touchmouse/tst_touchmouse.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 4170a31a54..313dee409a 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -68,7 +68,9 @@ class EventItem : public QQuickItem public: EventItem(QQuickItem *parent = 0) : QQuickItem(parent), acceptMouse(false), acceptTouch(false), filterTouch(false) - {} + { + setAcceptedMouseButtons(Qt::LeftButton); + } void touchEvent(QTouchEvent *event) { @@ -211,15 +213,17 @@ void tst_TouchMouse::simpleTouchEvent() p1 = QPoint(20, 20); QTest::touchEvent(window, device).press(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 1); + // Get a touch and then mouse event offered + QCOMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); p1 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 1); + // Not accepted, no updates + QCOMPARE(eventItem1->eventList.size(), 2); QTest::touchEvent(window, device).release(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 1); + QCOMPARE(eventItem1->eventList.size(), 2); eventItem1->eventList.clear(); // Accept touch -- cgit v1.2.3 From c479032e6ee6168c1d7c8354c7274ba1df019544 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sun, 31 Jul 2016 23:16:46 +0200 Subject: Add minor extra check in tst_qquickwindow Change-Id: If0343c36c747b419f949cff47a0f6a93bf73250d Reviewed-by: Frederik Gladhorn --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 025057d113..905c8961b2 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -869,6 +869,8 @@ void tst_qquickwindow::touchEvent_velocity() QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); QGuiApplication::processEvents(); QQuickTouchUtils::flush(window); + QCOMPARE(item->touchEventCount, 1); + points[0].state = Qt::TouchPointMoved; points[0].area.adjust(5, 5, 5, 5); QVector2D velocity(1.5, 2.5); -- cgit v1.2.3 From 435e3e95c2285f88b5cfcdd4709c0c614582098a Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sun, 31 Jul 2016 23:20:27 +0200 Subject: Micro optimize tst_qquickwindow::touchEvent_basic Change-Id: Ifd35eebaa8ba0aadcb15218a494c3e04167182ca Reviewed-by: Frederik Gladhorn --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 905c8961b2..6878abb178 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -523,9 +523,8 @@ void tst_qquickwindow::touchEvent_basic() // press single point QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window); - QTest::qWait(50); - - QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); + QQuickTouchUtils::flush(window); + QTRY_COMPARE(topItem->lastEvent.touchPoints.count(), 1); QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); -- cgit v1.2.3 From b4f05d7010e108564a36359755b114a26298a827 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sun, 31 Jul 2016 23:31:56 +0200 Subject: Let TestTouchItem::reset reset the event count Change-Id: I37ddf4d45fd3457decb35a597876008ea9d09d11 Reviewed-by: Frederik Gladhorn --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 6878abb178..51fe7eab0c 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -157,6 +157,7 @@ public: lastVelocity = lastVelocityFromMouseMove = QVector2D(); lastMousePos = QPointF(); lastMouseCapabilityFlags = 0; + touchEventCount = 0; } static void clearMousePressCounter() -- cgit v1.2.3 From 897eb7f1290428dcc257e999b099d0ef14349e35 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 28 Jul 2016 17:40:11 +0200 Subject: Add a function to QQmlPropertyCache to calculate the meta-object sizes This will be used later for calculating checksums of the meta-object data. Change-Id: Iba925eae298cbfc7b89196f4dd6fb2854ce75e2e Reviewed-by: Ulf Hermann --- src/qml/qml/qqmlpropertycache.cpp | 167 +++++++++++++++++++++ src/qml/qml/qqmlpropertycache_p.h | 2 + .../qqmlpropertycache/tst_qqmlpropertycache.cpp | 161 +++++++++++++++----- 3 files changed, 293 insertions(+), 37 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 502557fa9f..01be38ada1 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -1246,6 +1246,173 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) } } +namespace { +template +int visitMethods(const QMetaObject &mo, int methodOffset, int methodCount, + StringVisitor visitString, TypeInfoVisitor visitTypeInfo) +{ + const int intsPerMethod = 5; + + int fieldsForParameterData = 0; + + bool hasRevisionedMethods = false; + + for (int i = 0; i < methodCount; ++i) { + const int handle = methodOffset + i * intsPerMethod; + + const uint flags = mo.d.data[handle + 4]; + if (flags & MethodRevisioned) + hasRevisionedMethods = true; + + visitString(mo.d.data[handle + 0]); // name + visitString(mo.d.data[handle + 3]); // tag + + const int argc = mo.d.data[handle + 1]; + const int paramIndex = mo.d.data[handle + 2]; + + fieldsForParameterData += argc * 2; // type and name + fieldsForParameterData += 1; // + return type + + // return type + args + for (int i = 0; i < 1 + argc; ++i) { + // type name (maybe) + visitTypeInfo(mo.d.data[paramIndex + i]); + + // parameter name + if (i > 0) + visitString(mo.d.data[paramIndex + argc + i]); + } + } + + int fieldsForRevisions = 0; + if (hasRevisionedMethods) + fieldsForRevisions = methodCount; + + return methodCount * intsPerMethod + fieldsForRevisions + fieldsForParameterData; +} + +template +int visitProperties(const QMetaObject &mo, StringVisitor visitString, TypeInfoVisitor visitTypeInfo) +{ + const QMetaObjectPrivate *const priv = reinterpret_cast(mo.d.data); + const int intsPerProperty = 3; + + bool hasRevisionedProperties = false; + bool hasNotifySignals = false; + + for (int i = 0; i < priv->propertyCount; ++i) { + const int handle = priv->propertyData + i * intsPerProperty; + + const auto flags = mo.d.data[handle + 2]; + if (flags & Revisioned) { + hasRevisionedProperties = true; + } + if (flags & Notify) + hasNotifySignals = true; + + visitString(mo.d.data[handle]); // name + visitTypeInfo(mo.d.data[handle + 1]); + } + + int fieldsForPropertyRevisions = 0; + if (hasRevisionedProperties) + fieldsForPropertyRevisions = priv->propertyCount; + + int fieldsForNotifySignals = 0; + if (hasNotifySignals) + fieldsForNotifySignals = priv->propertyCount; + + return priv->propertyCount * intsPerProperty + fieldsForPropertyRevisions + + fieldsForNotifySignals; +} + +template +int visitClassInfo(const QMetaObject &mo, StringVisitor visitString) +{ + const QMetaObjectPrivate *const priv = reinterpret_cast(mo.d.data); + const int intsPerClassInfo = 2; + + for (int i = 0; i < priv->classInfoCount; ++i) { + const int handle = priv->classInfoData + i * intsPerClassInfo; + + visitString(mo.d.data[handle]); // key + visitString(mo.d.data[handle + 1]); // value + } + + return priv->classInfoCount * intsPerClassInfo; +} + +template +int visitEnumerations(const QMetaObject &mo, StringVisitor visitString) +{ + const QMetaObjectPrivate *const priv = reinterpret_cast(mo.d.data); + const int intsPerEnumerator = 4; + + int fieldCount = priv->enumeratorCount * intsPerEnumerator; + + for (int i = 0; i < priv->enumeratorCount; ++i) { + const uint *enumeratorData = mo.d.data + priv->enumeratorData + i * intsPerEnumerator; + + const uint keyCount = enumeratorData[2]; + fieldCount += keyCount * 2; + + visitString(enumeratorData[0]); // name + + const uint keyOffset = enumeratorData[3]; + + for (uint j = 0; j < keyCount; ++j) { + visitString(mo.d.data[keyOffset + 2 * j]); + } + } + + return fieldCount; +} + +template +int countMetaObjectFields(const QMetaObject &mo, StringVisitor stringVisitor) +{ + const QMetaObjectPrivate *const priv = reinterpret_cast(mo.d.data); + + const auto typeInfoVisitor = [&stringVisitor](uint typeInfo) { + if (typeInfo & IsUnresolvedType) + stringVisitor(typeInfo & TypeNameIndexMask); + }; + + int fieldCount = MetaObjectPrivateFieldCount; + + fieldCount += visitMethods(mo, priv->methodData, priv->methodCount, stringVisitor, + typeInfoVisitor); + fieldCount += visitMethods(mo, priv->constructorData, priv->constructorCount, stringVisitor, + typeInfoVisitor); + + fieldCount += visitProperties(mo, stringVisitor, typeInfoVisitor); + fieldCount += visitClassInfo(mo, stringVisitor); + fieldCount += visitEnumerations(mo, stringVisitor); + + return fieldCount; +} + +} // anonymous namespace + +bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, + int *stringCount) +{ + const QMetaObjectPrivate *priv = reinterpret_cast(mo.d.data); + if (priv->revision != 7) { + return false; + } + + uint highestStringIndex = 0; + const auto stringIndexVisitor = [&highestStringIndex](uint index) { + highestStringIndex = qMax(highestStringIndex, index); + }; + + *fieldCount = countMetaObjectFields(mo, stringIndexVisitor); + *stringCount = highestStringIndex + 1; + + return true; +} + /*! \internal \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()). This is different from QMetaMethod::methodIndex(). diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index ad6db9756f..fd29cc8e6a 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -366,6 +366,8 @@ public: void toMetaObjectBuilder(QMetaObjectBuilder &); + static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount); + protected: virtual void destroy(); virtual void clear(); diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index 5f15afff85..acc68befb5 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "../../shared/util.h" class tst_qqmlpropertycache : public QObject @@ -45,6 +46,8 @@ private slots: void methodsDerived(); void signalHandlers(); void signalHandlersDerived(); + void metaObjectSize_data(); + void metaObjectSize(); private: QQmlEngine engine; @@ -105,16 +108,16 @@ void tst_qqmlpropertycache::properties() QQmlRefPointer cache(new QQmlPropertyCache(v4, metaObject)); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "propertyA")); + QVERIFY((data = cacheProperty(cache, "propertyA"))); QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA")); - QVERIFY(data = cacheProperty(cache, "propertyB")); + QVERIFY((data = cacheProperty(cache, "propertyB"))); QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB")); - QVERIFY(data = cacheProperty(cache, "propertyC")); + QVERIFY((data = cacheProperty(cache, "propertyC"))); QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC")); - QVERIFY(data = cacheProperty(cache, "propertyD")); + QVERIFY((data = cacheProperty(cache, "propertyD"))); QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD")); } @@ -129,16 +132,16 @@ void tst_qqmlpropertycache::propertiesDerived() QQmlRefPointer cache(parentCache->copyAndAppend(object.metaObject())); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "propertyA")); + QVERIFY((data = cacheProperty(cache, "propertyA"))); QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA")); - QVERIFY(data = cacheProperty(cache, "propertyB")); + QVERIFY((data = cacheProperty(cache, "propertyB"))); QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB")); - QVERIFY(data = cacheProperty(cache, "propertyC")); + QVERIFY((data = cacheProperty(cache, "propertyC"))); QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC")); - QVERIFY(data = cacheProperty(cache, "propertyD")); + QVERIFY((data = cacheProperty(cache, "propertyD"))); QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD")); } @@ -152,28 +155,28 @@ void tst_qqmlpropertycache::methods() QQmlRefPointer cache(new QQmlPropertyCache(v4, metaObject)); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "slotA")); + QVERIFY((data = cacheProperty(cache, "slotA"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()")); - QVERIFY(data = cacheProperty(cache, "slotB")); + QVERIFY((data = cacheProperty(cache, "slotB"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()")); - QVERIFY(data = cacheProperty(cache, "signalA")); + QVERIFY((data = cacheProperty(cache, "signalA"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); - QVERIFY(data = cacheProperty(cache, "signalB")); + QVERIFY((data = cacheProperty(cache, "signalB"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); - QVERIFY(data = cacheProperty(cache, "propertyAChanged")); + QVERIFY((data = cacheProperty(cache, "propertyAChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyBChanged")); + QVERIFY((data = cacheProperty(cache, "propertyBChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyCChanged")); + QVERIFY((data = cacheProperty(cache, "propertyCChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyDChanged")); + QVERIFY((data = cacheProperty(cache, "propertyDChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); } @@ -188,28 +191,28 @@ void tst_qqmlpropertycache::methodsDerived() QQmlRefPointer cache(parentCache->copyAndAppend(object.metaObject())); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "slotA")); + QVERIFY((data = cacheProperty(cache, "slotA"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()")); - QVERIFY(data = cacheProperty(cache, "slotB")); + QVERIFY((data = cacheProperty(cache, "slotB"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()")); - QVERIFY(data = cacheProperty(cache, "signalA")); + QVERIFY((data = cacheProperty(cache, "signalA"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); - QVERIFY(data = cacheProperty(cache, "signalB")); + QVERIFY((data = cacheProperty(cache, "signalB"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); - QVERIFY(data = cacheProperty(cache, "propertyAChanged")); + QVERIFY((data = cacheProperty(cache, "propertyAChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyBChanged")); + QVERIFY((data = cacheProperty(cache, "propertyBChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyCChanged")); + QVERIFY((data = cacheProperty(cache, "propertyCChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); - QVERIFY(data = cacheProperty(cache, "propertyDChanged")); + QVERIFY((data = cacheProperty(cache, "propertyDChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); } @@ -223,22 +226,22 @@ void tst_qqmlpropertycache::signalHandlers() QQmlRefPointer cache(new QQmlPropertyCache(v4, metaObject)); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "onSignalA")); + QVERIFY((data = cacheProperty(cache, "onSignalA"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); - QVERIFY(data = cacheProperty(cache, "onSignalB")); + QVERIFY((data = cacheProperty(cache, "onSignalB"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); - QVERIFY(data = cacheProperty(cache, "onPropertyAChanged")); + QVERIFY((data = cacheProperty(cache, "onPropertyAChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyBChanged")); + QVERIFY((data = cacheProperty(cache, "onPropertyBChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyCChanged")); + QVERIFY((data = cacheProperty(cache, "onPropertyCChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyDChanged")); + QVERIFY((data = cacheProperty(cache, "onPropertyDChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); } @@ -253,25 +256,109 @@ void tst_qqmlpropertycache::signalHandlersDerived() QQmlRefPointer cache(parentCache->copyAndAppend(object.metaObject())); QQmlPropertyData *data; - QVERIFY(data = cacheProperty(cache, "onSignalA")); + QVERIFY((data = cacheProperty(cache, "onSignalA"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); - QVERIFY(data = cacheProperty(cache, "onSignalB")); + QVERIFY((data = cacheProperty(cache, "onSignalB"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); - QVERIFY(data = cacheProperty(cache, "onPropertyAChanged")); + QVERIFY((data = cacheProperty(cache, "onPropertyAChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyBChanged")); + QVERIFY((data = cacheProperty(cache, "onPropertyBChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyCChanged")); + QVERIFY((data = cacheProperty(cache, "onPropertyCChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); - QVERIFY(data = cacheProperty(cache, "onPropertyDChanged")); + QVERIFY((data = cacheProperty(cache, "onPropertyDChanged"))); QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); } -QTEST_MAIN(tst_qqmlpropertycache) +class TestClass : public QObject +{ + Q_OBJECT + Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged) + int m_prop; + +public: + enum MyEnum { + First, Second + }; + Q_ENUM(MyEnum) + + Q_CLASSINFO("Foo", "Bar") + + TestClass() {} + + int prop() const + { + return m_prop; + } + +public slots: + void setProp(int prop) + { + if (m_prop == prop) + return; + + m_prop = prop; + emit propChanged(prop); + } +signals: + void propChanged(int prop); +}; + +class TestClassWithParameters : public QObject +{ + Q_OBJECT + +public: + Q_INVOKABLE void slotWithArguments(int firstArg) { + Q_UNUSED(firstArg); + } +}; + +class TestClassWithClassInfo : public QObject +{ + Q_OBJECT + Q_CLASSINFO("Key", "Value") +}; #include "tst_qqmlpropertycache.moc" + +#define ARRAY_SIZE(arr) \ + int(sizeof(arr) / sizeof(arr[0])) + +#define TEST_CLASS(Class) \ + QTest::newRow(#Class) << &Class::staticMetaObject << ARRAY_SIZE(qt_meta_data_##Class) << ARRAY_SIZE(qt_meta_stringdata_##Class.data) + +Q_DECLARE_METATYPE(const QMetaObject*); + +void tst_qqmlpropertycache::metaObjectSize_data() +{ + QTest::addColumn("metaObject"); + QTest::addColumn("expectedFieldCount"); + QTest::addColumn("expectedStringCount"); + + TEST_CLASS(TestClass); + TEST_CLASS(TestClassWithParameters); + TEST_CLASS(TestClassWithClassInfo); +} + +void tst_qqmlpropertycache::metaObjectSize() +{ + QFETCH(const QMetaObject *, metaObject); + QFETCH(int, expectedFieldCount); + QFETCH(int, expectedStringCount); + + int size = 0; + int stringDataSize = 0; + bool valid = QQmlPropertyCache::determineMetaObjectSizes(*metaObject, &size, &stringDataSize); + QVERIFY(valid); + + QCOMPARE(size, expectedFieldCount - 1); // Remove trailing zero field until fixed in moc. + QCOMPARE(stringDataSize, expectedStringCount); +} + +QTEST_MAIN(tst_qqmlpropertycache) -- cgit v1.2.3 From 143f22babb43d50dd5ee99f73f04016c84d6171a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 28 Jul 2016 17:40:21 +0200 Subject: Add a checksum to QQmlPropertyCache By running an md5 hash over the meta-object data and string tables this will allow us to detect changes to meta-objects and invalidate QML disk caches. Change-Id: I15b92de4cdf0cb525281b86e1c7b8ba0b11347a0 Reviewed-by: Ulf Hermann --- src/qml/qml/qqmlpropertycache.cpp | 42 ++++++++++++++++++++++ src/qml/qml/qqmlpropertycache_p.h | 5 +++ .../qqmlpropertycache/tst_qqmlpropertycache.cpp | 38 ++++++++++++++++++++ 3 files changed, 85 insertions(+) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 01be38ada1..322e519706 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -1413,6 +1413,48 @@ bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fie return true; } +bool QQmlPropertyCache::addToHash(QCryptographicHash &hash, const QMetaObject &mo) +{ + int fieldCount = 0; + int stringCount = 0; + if (!determineMetaObjectSizes(mo, &fieldCount, &stringCount)) { + return false; + } + + hash.addData(reinterpret_cast(mo.d.data), fieldCount * sizeof(uint)); + for (int i = 0; i < stringCount; ++i) { + const QByteArrayDataPtr data = { const_cast(&mo.d.stringdata[i]) }; + hash.addData(QByteArray(data)); + } + + return true; +} + +QByteArray QQmlPropertyCache::checksum(bool *ok) +{ + if (!_checksum.isEmpty()) { + *ok = true; + return _checksum; + } + + QCryptographicHash hash(QCryptographicHash::Md5); + + if (_parent) { + hash.addData(_parent->checksum(ok)); + if (!*ok) + return QByteArray(); + } + + if (!addToHash(hash, *createMetaObject())) { + *ok = false; + return QByteArray(); + } + + _checksum = hash.result(); + *ok = !_checksum.isEmpty(); + return _checksum; +} + /*! \internal \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()). This is different from QMetaMethod::methodIndex(). diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index fd29cc8e6a..750537e707 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -65,6 +65,7 @@ QT_BEGIN_NAMESPACE +class QCryptographicHash; class QMetaProperty; class QQmlEngine; class QJSEngine; @@ -367,6 +368,9 @@ public: void toMetaObjectBuilder(QMetaObjectBuilder &); static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount); + static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo); + + QByteArray checksum(bool *ok); protected: virtual void destroy(); @@ -437,6 +441,7 @@ private: QByteArray _dynamicStringData; QString _defaultPropertyName; QQmlPropertyCacheMethodArguments *argumentsCache; + QByteArray _checksum; }; // QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache. diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index acc68befb5..2916d8455c 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "../../shared/util.h" class tst_qqmlpropertycache : public QObject @@ -48,6 +49,7 @@ private slots: void signalHandlersDerived(); void metaObjectSize_data(); void metaObjectSize(); + void metaObjectChecksum(); private: QQmlEngine engine; @@ -361,4 +363,40 @@ void tst_qqmlpropertycache::metaObjectSize() QCOMPARE(stringDataSize, expectedStringCount); } +void tst_qqmlpropertycache::metaObjectChecksum() +{ + QMetaObjectBuilder builder; + builder.setClassName("Test"); + builder.addClassInfo("foo", "bar"); + + QCryptographicHash hash(QCryptographicHash::Md5); + + QScopedPointer mo(builder.toMetaObject()); + QVERIFY(!mo.isNull()); + + QVERIFY(QQmlPropertyCache::addToHash(hash, *mo.data())); + QByteArray initialHash = hash.result(); + QVERIFY(!initialHash.isEmpty()); + hash.reset(); + + { + QVERIFY(QQmlPropertyCache::addToHash(hash, *mo.data())); + QByteArray nextHash = hash.result(); + QVERIFY(!nextHash.isEmpty()); + hash.reset(); + QCOMPARE(initialHash, nextHash); + } + + builder.addProperty("testProperty", "int", -1); + + mo.reset(builder.toMetaObject()); + { + QVERIFY(QQmlPropertyCache::addToHash(hash, *mo.data())); + QByteArray nextHash = hash.result(); + QVERIFY(!nextHash.isEmpty()); + hash.reset(); + QVERIFY(initialHash != nextHash); + } +} + QTEST_MAIN(tst_qqmlpropertycache) -- cgit v1.2.3 From fa53d4e35433cc59e99b24f16f18c713683c1a80 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 28 Jul 2016 17:40:29 +0200 Subject: Cleanup: Move ResolvedTypeReference(Map) one scope up The class is a candidate for further functionality, it doesn't have to be nested in CompilationUnit. Change-Id: I100553160f5ae34f66b9f8ff5df9f636da2ffb67 Reviewed-by: Ulf Hermann --- src/qml/compiler/qqmlpropertyvalidator_p.h | 2 +- src/qml/compiler/qqmltypecompiler.cpp | 6 ++-- src/qml/compiler/qqmltypecompiler_p.h | 12 +++---- src/qml/compiler/qv4compileddata.cpp | 6 ++-- src/qml/compiler/qv4compileddata_p.h | 56 ++++++++++++++++-------------- src/qml/qml/qqmlobjectcreator.cpp | 4 +-- src/qml/qml/qqmlobjectcreator_p.h | 2 +- src/qml/qml/qqmltypeloader.cpp | 15 ++++---- src/qml/qml/qqmltypeloader_p.h | 5 ++- 9 files changed, 59 insertions(+), 49 deletions(-) diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h index 1cbb370068..d0bd314461 100644 --- a/src/qml/compiler/qqmlpropertyvalidator_p.h +++ b/src/qml/compiler/qqmlpropertyvalidator_p.h @@ -76,7 +76,7 @@ private: QQmlEnginePrivate *enginePrivate; const QQmlImports &imports; const QV4::CompiledData::Unit *qmlUnit; - const QHash &resolvedTypes; + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes; const QQmlPropertyCacheVector &propertyCaches; QVector * const bindingPropertyDataPerObject; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index bb4d603f68..a0b219e28e 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -56,7 +56,9 @@ QT_BEGIN_NAMESPACE -QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *parsedQML, const QQmlRefPointer &importCache, const QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap &resolvedTypeCache) +QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, + QmlIR::Document *parsedQML, const QQmlRefPointer &importCache, + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) : resolvedTypes(resolvedTypeCache) , engine(engine) , typeData(typeData) @@ -837,7 +839,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; if (!resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) { - auto typeRef = new QV4::CompiledData::CompilationUnit::ResolvedTypeReference; + auto typeRef = new QV4::CompiledData::ResolvedTypeReference; typeRef->type = componentType; typeRef->majorVersion = componentType->majorVersion(); typeRef->minorVersion = componentType->minorVersion(); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 9f1a09b35c..6ad6ad8557 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -89,7 +89,7 @@ struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer &importCache, const QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap &resolvedTypeCache); + QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); // --- interface used by QQmlPropertyCacheCreator typedef QmlIR::Object CompiledObject; @@ -98,7 +98,7 @@ public: QString stringAt(int idx) const; QmlIR::PoolList::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } QmlIR::PoolList::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } - QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap resolvedTypes; + QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypes; // --- QV4::CompiledData::CompilationUnit *compile(); @@ -182,7 +182,7 @@ private: const QVector &qmlObjects; const QQmlImports *imports; const QHash &customParsers; - const QHash &resolvedTypes; + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes; const QSet &illegalNames; const QQmlPropertyCacheVector * const propertyCaches; }; @@ -213,7 +213,7 @@ private: const QVector &qmlObjects; const QQmlPropertyCacheVector * const propertyCaches; const QQmlImports *imports; - QHash *resolvedTypes; + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes; }; class QQmlCustomParserScriptIndexer: public QQmlCompilePass @@ -288,7 +288,7 @@ protected: QHash _idToObjectIndex; QVector _objectsWithAliases; - QHash *resolvedTypes; + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes; QQmlPropertyCacheVector propertyCaches; }; @@ -321,7 +321,7 @@ private: bool compileComponent(int componentRoot); bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); - const QHash &resolvedTypes; + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes; const QHash &customParsers; const QVector &qmlObjects; const QQmlPropertyCacheVector * const propertyCaches; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 965924262d..2aab1743cc 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -535,7 +535,7 @@ QString Binding::valueAsScriptString(const Unit *unit) const /*! Returns the property cache, if one alread exists. The cache is not referenced. */ -QQmlPropertyCache *CompilationUnit::ResolvedTypeReference::propertyCache() const +QQmlPropertyCache *ResolvedTypeReference::propertyCache() const { if (type) return typePropertyCache; @@ -546,7 +546,7 @@ QQmlPropertyCache *CompilationUnit::ResolvedTypeReference::propertyCache() const /*! Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. */ -QQmlPropertyCache *CompilationUnit::ResolvedTypeReference::createPropertyCache(QQmlEngine *engine) +QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine) { if (typePropertyCache) { return typePropertyCache; @@ -568,7 +568,7 @@ bool qtTypeInherits(const QMetaObject *mo) { return false; } -void CompilationUnit::ResolvedTypeReference::doDynamicTypeCheck() +void ResolvedTypeReference::doDynamicTypeCheck() { const QMetaObject *mo = 0; if (typePropertyCache) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 4153259760..82e303f27d 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -762,6 +762,35 @@ struct TypeReferenceMap : QHash } }; +#ifndef V4_BOOTSTRAP +struct ResolvedTypeReference +{ + ResolvedTypeReference() + : type(0) + , majorVersion(0) + , minorVersion(0) + , isFullyDynamicType(false) + {} + + QQmlType *type; + QQmlRefPointer typePropertyCache; + QQmlRefPointer compilationUnit; + + int majorVersion; + int minorVersion; + // Types such as QQmlPropertyMap can add properties dynamically at run-time and + // therefore cannot have a property cache installed when instantiated. + bool isFullyDynamicType; + + QQmlPropertyCache *propertyCache() const; + QQmlPropertyCache *createPropertyCache(QQmlEngine *); + + void doDynamicTypeCheck(); +}; +// map from name index +typedef QHash ResolvedTypeReferenceMap; +#endif + // index is per-object binding index typedef QVector BindingPropertyData; @@ -823,33 +852,6 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount int totalObjectCount; // Number of objects explicitly instantiated QVector dependentScripts; - - struct ResolvedTypeReference - { - ResolvedTypeReference() - : type(0) - , majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} - - QQmlType *type; - QQmlRefPointer typePropertyCache; - QQmlRefPointer compilationUnit; - - int majorVersion; - int minorVersion; - // Types such as QQmlPropertyMap can add properties dynamically at run-time and - // therefore cannot have a property cache installed when instantiated. - bool isFullyDynamicType; - - QQmlPropertyCache *propertyCache() const; - QQmlPropertyCache *createPropertyCache(QQmlEngine *); - - void doDynamicTypeCheck(); - }; - // map from name index - typedef QHash ResolvedTypeReferenceMap; ResolvedTypeReferenceMap resolvedTypes; int metaTypeId; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index a38d18acfb..f2b3276778 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -675,7 +675,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); - QV4::CompiledData::CompilationUnit::ResolvedTypeReference *tr = resolvedTypes.value(binding->propertyNameIndex); + QV4::CompiledData::ResolvedTypeReference *tr = resolvedTypes.value(binding->propertyNameIndex); Q_ASSERT(tr); QQmlType *attachedType = tr->type; if (!attachedType) { @@ -1008,7 +1008,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo instance = component; ddata = QQmlData::get(instance, /*create*/true); } else { - QV4::CompiledData::CompilationUnit::ResolvedTypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); + QV4::CompiledData::ResolvedTypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); installPropertyCache = !typeRef->isFullyDynamicType; QQmlType *type = typeRef->type; diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 4aa3f634ed..e3312f9df5 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -138,7 +138,7 @@ private: const QV4::CompiledData::Unit *qmlUnit; QQmlGuardedContextData parentContext; QQmlContextData *context; - const QHash &resolvedTypes; + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes; const QQmlPropertyCacheVector *propertyCaches; QExplicitlySharedDataPointer sharedState; bool topLevelCreator; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 2578f44355..c54af43a6f 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2029,7 +2029,7 @@ QQmlTypeData::~QQmlTypeData() if (QQmlTypeData *tdata = m_compositeSingletons.at(ii).typeData) tdata->release(); } - for (QHash::ConstIterator it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); + for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; ++it) { if (QQmlTypeData *tdata = it->typeData) tdata->release(); @@ -2189,8 +2189,8 @@ void QQmlTypeData::done() } // Check all type dependencies for errors - for (QHash::ConstIterator it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); - it != end; ++it) { + for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; + ++it) { const TypeReference &type = *it; Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); if (type.typeData && type.typeData->isError()) { @@ -2469,7 +2469,7 @@ void QQmlTypeData::compile() Q_ASSERT(m_compiledData.isNull()); QQmlRefPointer importCache; - QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap resolvedTypeCache; + QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache; QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache); if (error.isSet()) { setError(error); @@ -2609,7 +2609,10 @@ void QQmlTypeData::resolveTypes() } } -QQmlCompileError QQmlTypeData::buildTypeResolutionCaches(QQmlRefPointer *importCache, QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap *resolvedTypeCache) const +QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( + QQmlRefPointer *importCache, + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache + ) const { importCache->adopt(new QQmlTypeNameCache); @@ -2625,7 +2628,7 @@ QQmlCompileError QQmlTypeData::buildTypeResolutionCaches(QQmlRefPointerengine()); for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) { - QScopedPointer ref(new QV4::CompiledData::CompilationUnit::ResolvedTypeReference); + QScopedPointer ref(new QV4::CompiledData::ResolvedTypeReference); QQmlType *qmlType = resolvedType->type; if (resolvedType->typeData) { if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) { diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 4c57306ed7..ab6b046fcf 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -444,7 +444,10 @@ private: bool tryLoadFromDiskCache(); void continueLoadFromIR(); void resolveTypes(); - QQmlCompileError buildTypeResolutionCaches(QQmlRefPointer *importCache, QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap *resolvedTypeCache) const; + QQmlCompileError buildTypeResolutionCaches( + QQmlRefPointer *importCache, + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache + ) const; void compile(); void rebuildTypeAndPropertyCaches(); bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref); -- cgit v1.2.3 From bf2c880b2b147804e9553d17a23ac4dd6ea4b3f4 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 12 Jul 2016 16:16:28 +0200 Subject: Add mergePointerTargets to create pointer delivery list This code joins lists of "target item" which are joins of where several touch points should be delivered. Change-Id: I15ab4b7f70b8930d15368bf4cba0893ab339fa2a Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 22 ++++++++ src/quick/items/qquickwindow_p.h | 2 + tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 60 ++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index c1167ad8a5..339e770910 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2169,6 +2169,28 @@ QVector QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons return targets; } +// return the joined lists +// list1 has priority, common items come last +QVector QQuickWindowPrivate::mergePointerTargets(const QVector &list1, const QVector &list2) const +{ + QVector targets = list1; + // start at the end of list2 + // if item not in list, append it + // if item found, move to next one, inserting before the last found one + int insertPosition = targets.length(); + for (int i = list2.length() - 1; i >= 0; --i) { + int newInsertPosition = targets.lastIndexOf(list2.at(i), insertPosition); + if (newInsertPosition >= 0) { + Q_ASSERT(newInsertPosition <= insertPosition); + insertPosition = newInsertPosition; + } + // check for duplicates, only insert if the item isn't there already + if (insertPosition == targets.size() || list2.at(i) != targets.at(insertPosition)) + targets.insert(insertPosition, list2.at(i)); + } + return targets; +} + void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 61d57692a7..51804653f7 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -175,6 +175,8 @@ public: bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); QVector pointerTargets(QQuickItem *, const QPointF &) const; + QVector mergePointerTargets(const QVector &list1, const QVector &list2) const; + // hover delivery bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 51fe7eab0c..d4df09838b 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -303,6 +303,9 @@ private slots: void touchEvent_reentrant(); void touchEvent_velocity(); + void mergeTouchPointLists_data(); + void mergeTouchPointLists(); + void mouseFromTouch_basic(); void clearWindow(); @@ -903,6 +906,63 @@ void tst_qquickwindow::touchEvent_velocity() delete item; } +void tst_qquickwindow::mergeTouchPointLists_data() +{ + QTest::addColumn>("list1"); + QTest::addColumn>("list2"); + QTest::addColumn>("expected"); + QTest::addColumn("showItem"); + + // FIXME: do not leak all these items + auto item1 = new QQuickItem(); + auto item2 = new QQuickItem(); + auto item3 = new QQuickItem(); + auto item4 = new QQuickItem(); + auto item5 = new QQuickItem(); + + QTest::newRow("empty") << QVector() << QVector() << QVector(); + QTest::newRow("single list left") + << (QVector() << item1 << item2 << item3) + << QVector() + << (QVector() << item1 << item2 << item3); + QTest::newRow("single list right") + << QVector() + << (QVector() << item1 << item2 << item3) + << (QVector() << item1 << item2 << item3); + QTest::newRow("two lists identical") + << (QVector() << item1 << item2 << item3) + << (QVector() << item1 << item2 << item3) + << (QVector() << item1 << item2 << item3); + QTest::newRow("two lists 1") + << (QVector() << item1 << item2 << item5) + << (QVector() << item3 << item4 << item5) + << (QVector() << item1 << item2 << item3 << item4 << item5); + QTest::newRow("two lists 2") + << (QVector() << item1 << item2 << item5) + << (QVector() << item3 << item4 << item5) + << (QVector() << item1 << item2 << item3 << item4 << item5); + QTest::newRow("two lists 3") + << (QVector() << item1 << item2 << item3) + << (QVector() << item1 << item4 << item5) + << (QVector() << item1 << item2 << item3 << item4 << item5); + QTest::newRow("two lists 3") + << (QVector() << item1 << item3 << item4) + << (QVector() << item2 << item3 << item5) + << (QVector() << item1 << item2 << item3 << item4 << item5); +} + +void tst_qquickwindow::mergeTouchPointLists() +{ + QFETCH(QVector, list1); + QFETCH(QVector, list2); + QFETCH(QVector, expected); + + QQuickWindow win; + auto windowPrivate = QQuickWindowPrivate::get(&win); + auto targetList = windowPrivate->mergePointerTargets(list1, list2); + QCOMPARE(targetList, expected); +} + void tst_qquickwindow::mouseFromTouch_basic() { // Turn off accepting touch events with acceptTouchEvents. This -- cgit v1.2.3 From eae73be9060b4017b3eee64ab43a5b8ecb62ab2c Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 29 Jul 2016 17:58:12 +0200 Subject: Deliver touch points in defined order [ChangeLog][QtQuick] Touch events are now delivered in a well defined order: New touch points are delivered first to items under the finger which was first touching the screen. Then to items that are under the second finger and finally to common ancestors. This means that items that are "on top" will get a chance to grab touch points before any items in the background. Change-Id: Icf9a163c0183437cdb79040b8513fd746c3a6a44 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 42 ++++++++++++++-------------------------- src/quick/items/qquickwindow_p.h | 4 ++-- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 339e770910..cc422361f8 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1626,7 +1626,7 @@ bool QQuickWindowPrivate::deliverInitialMousePressEvent(QMouseEvent *event) { Q_Q(QQuickWindow); - QVector targets = pointerTargets(contentItem, event->windowPos()); + QVector targets = pointerTargets(contentItem, event->windowPos(), true); for (QQuickItem *item: qAsConst(targets)) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (itemPrivate->acceptedMouseButtons() & event->button()) { @@ -2141,7 +2141,7 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) // check if item or any of its child items contain the point // FIXME: should this be iterative instead of recursive? -QVector QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos) const +QVector QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons) const { QVector targets; auto itemPrivate = QQuickItemPrivate::get(item); @@ -2159,10 +2159,10 @@ QVector QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons auto childPrivate = QQuickItemPrivate::get(child); if (!child->isVisible() || !child->isEnabled() || childPrivate->culled) continue; - targets << pointerTargets(child, scenePos); + targets << pointerTargets(child, scenePos, false); } - if (item->contains(itemPos) && itemPrivate->acceptedMouseButtons()) { + if (item->contains(itemPos) && (!checkMouseButtons || itemPrivate->acceptedMouseButtons())) { // add this item last - children take precedence targets << item; } @@ -2197,7 +2197,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) QSet hasFiltered; if (event->isPressEvent()) - deliverNewTouchPoints(contentItem, event, &hasFiltered); + deliverNewTouchPoints(event, &hasFiltered); if (!event->allPointsAccepted()) deliverUpdatedTouchPoints(event, &hasFiltered); @@ -2236,33 +2236,19 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve return false; } -// This function recurses and sends the events to the individual items -bool QQuickWindowPrivate::deliverNewTouchPoints(QQuickItem *item, QQuickPointerTouchEvent *event, QSet *hasFiltered) +// Deliver newly pressed touch points +bool QQuickWindowPrivate::deliverNewTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered) { - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - - // Check if our children want the event (or parts of it) - // This is the only point where touch event delivery recurses! - QList children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QQuickItem *child = children.at(ii); - if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled) - continue; - if (deliverNewTouchPoints(child, event, hasFiltered)) - return true; + const QVector points = event->unacceptedPressedPointScenePositions(); + QVector targetItems; + for (QPointF point: points) { + QVector targetItemsForPoint = pointerTargets(contentItem, point, false); + targetItems = mergePointerTargets(targetItems, targetItemsForPoint); } - // None of the children accepted the event, so check the given item itself. - // First, construct matchingPoints as a list of TouchPoints which the - // given item might be interested in. Any newly-pressed point which is - // inside the item's bounds will be interesting, and also any updated point - // which was already accepted by that item when it was first pressed. - // (A point which was already accepted is effectively "grabbed" by the item.) - - // This item might be interested in the event. - deliverMatchingPointsToItem(item, event, hasFiltered); + for (QQuickItem *item: targetItems) + deliverMatchingPointsToItem(item, event, hasFiltered); - // recursion is done only if ALL touch points have been delivered return event->allPointsAccepted(); } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 51804653f7..cd63f5a8f6 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -167,14 +167,14 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverNewTouchPoints(QQuickItem *, QQuickPointerTouchEvent *, QSet *); + bool deliverNewTouchPoints(QQuickPointerTouchEvent *, QSet *); bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered); bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent); static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); - QVector pointerTargets(QQuickItem *, const QPointF &) const; + QVector pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; QVector mergePointerTargets(const QVector &list1, const QVector &list2) const; // hover delivery -- cgit v1.2.3 From 4409160ceb7671123c20f4539cf02c75c935d9e1 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 1 Aug 2016 14:13:20 +0200 Subject: Test touch point delivery order Change-Id: I9b93eda0378e10bbcc274998d58f5f5ec62bb1b1 Reviewed-by: Shawn Rutledge --- .../touchmouse/data/touchpointdeliveryorder.qml | 39 ++++++++ tests/auto/quick/touchmouse/tst_touchmouse.cpp | 105 +++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml diff --git a/tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml b/tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml new file mode 100644 index 0000000000..9f67c226a0 --- /dev/null +++ b/tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml @@ -0,0 +1,39 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Rectangle { + id: root + + width: 600 + height: 400 + + EventItem { + objectName: "background" + width: 600 + height: 400 + Rectangle { anchors.fill: parent; color: "lightsteelblue" } + + EventItem { + objectName: "left" + width: 300 + height: 300 + Rectangle { anchors.fill: parent; color: "yellow" } + } + + EventItem { + objectName: "right" + x: 300 + width: 300 + height: 300 + Rectangle { anchors.fill: parent; color: "green" } + } + + EventItem { + objectName: "middle" + x: 150 + width: 300 + height: 300 + Rectangle { anchors.fill: parent; color: "blue" } + } + } +} diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 313dee409a..1ec24e35d5 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -65,6 +65,10 @@ struct Event class EventItem : public QQuickItem { Q_OBJECT + +Q_SIGNALS: + void onTouchEvent(QQuickItem *receiver); + public: EventItem(QQuickItem *parent = 0) : QQuickItem(parent), acceptMouse(false), acceptTouch(false), filterTouch(false) @@ -76,6 +80,7 @@ public: { eventList.append(Event(event->type(), event->touchPoints())); event->setAccepted(acceptTouch); + emit onTouchEvent(this); } void mousePressEvent(QMouseEvent *event) { @@ -156,6 +161,7 @@ private slots: void tapOnDismissiveTopMouseAreaClicksBottomOne(); void touchGrabCausesMouseUngrab(); + void touchPointDeliveryOrder(); void hoverEnabled(); @@ -1225,6 +1231,105 @@ void tst_TouchMouse::touchGrabCausesMouseUngrab() delete window; } +void tst_TouchMouse::touchPointDeliveryOrder() +{ + // Touch points should be first delivered to the item under the primary finger + QScopedPointer window(createView()); + + window->setSource(testFileUrl("touchpointdeliveryorder.qml")); + /* + The items are positioned from left to right: + | background | + | left | + | | right | + | middle | + 0 150 300 450 600 + */ + QPoint pLeft = QPoint(100, 100); + QPoint pRight = QPoint(500, 100); + QPoint pLeftMiddle = QPoint(200, 100); + QPoint pRightMiddle = QPoint(350, 100); + + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QVector events; + EventItem *background = window->rootObject()->findChild("background"); + EventItem *left = window->rootObject()->findChild("left"); + EventItem *middle = window->rootObject()->findChild("middle"); + EventItem *right = window->rootObject()->findChild("right"); + QVERIFY(background); + QVERIFY(left); + QVERIFY(middle); + QVERIFY(right); + connect(background, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); }); + connect(left, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); }); + connect(middle, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); }); + connect(right, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); }); + + QTest::touchEvent(window.data(), device).press(0, pLeft, window.data()); + QQuickTouchUtils::flush(window.data()); + + // Touch on left, then background + QCOMPARE(events.size(), 2); + QCOMPARE(events.at(0), left); + QCOMPARE(events.at(1), background); + events.clear(); + + // New press events are deliverd first, the stationary point was not accepted, thus it doesn't get delivered + QTest::touchEvent(window.data(), device).stationary(0).press(1, pRightMiddle, window.data()); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(events.size(), 3); + QCOMPARE(events.at(0), middle); + QCOMPARE(events.at(1), right); + QCOMPARE(events.at(2), background); + events.clear(); + + QTest::touchEvent(window.data(), device).release(0, pLeft, window.data()).release(1, pRightMiddle, window.data()); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(events.size(), 0); // no accepted events + + // Two presses, the first point should come first + QTest::touchEvent(window.data(), device).press(0, pLeft, window.data()).press(1, pRight, window.data()); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(events.size(), 3); + QCOMPARE(events.at(0), left); + QCOMPARE(events.at(1), right); + QCOMPARE(events.at(2), background); + QTest::touchEvent(window.data(), device).release(0, pLeft, window.data()).release(1, pRight, window.data()); + events.clear(); + + // Again, pressing right first + QTest::touchEvent(window.data(), device).press(0, pRight, window.data()).press(1, pLeft, window.data()); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(events.size(), 3); + QCOMPARE(events.at(0), right); + QCOMPARE(events.at(1), left); + QCOMPARE(events.at(2), background); + QTest::touchEvent(window.data(), device).release(0, pRight, window.data()).release(1, pLeft, window.data()); + events.clear(); + + // Two presses, both hitting the middle item on top, then branching left and right, then bottom + // Each target should be offered the events exactly once, middle first, left must come before right (id 0) + QTest::touchEvent(window.data(), device).press(0, pLeftMiddle, window.data()).press(1, pRightMiddle, window.data()); + QCOMPARE(events.size(), 4); + QCOMPARE(events.at(0), middle); + QCOMPARE(events.at(1), left); + QCOMPARE(events.at(2), right); + QCOMPARE(events.at(3), background); + QTest::touchEvent(window.data(), device).release(0, pLeftMiddle, window.data()).release(1, pRightMiddle, window.data()); + events.clear(); + + QTest::touchEvent(window.data(), device).press(0, pRightMiddle, window.data()).press(1, pLeftMiddle, window.data()); + qDebug() << events; + QCOMPARE(events.size(), 4); + QCOMPARE(events.at(0), middle); + QCOMPARE(events.at(1), right); + QCOMPARE(events.at(2), left); + QCOMPARE(events.at(3), background); + QTest::touchEvent(window.data(), device).release(0, pRightMiddle, window.data()).release(1, pLeftMiddle, window.data()); +} + void tst_TouchMouse::hoverEnabled() { // QTouchDevice *device = new QTouchDevice; -- cgit v1.2.3 From f059e47a8450051f8ee9145d618f4bfc72d4793f Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 1 Aug 2016 15:55:02 +0200 Subject: Extend tst_qquickwindow::mergeTouchPointLists Change-Id: I199c57197fafed84ba0065765411d5239804d17f Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index d4df09838b..e57e27533b 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -945,10 +945,14 @@ void tst_qquickwindow::mergeTouchPointLists_data() << (QVector() << item1 << item2 << item3) << (QVector() << item1 << item4 << item5) << (QVector() << item1 << item2 << item3 << item4 << item5); - QTest::newRow("two lists 3") + QTest::newRow("two lists 4") << (QVector() << item1 << item3 << item4) << (QVector() << item2 << item3 << item5) << (QVector() << item1 << item2 << item3 << item4 << item5); + QTest::newRow("two lists 5") + << (QVector() << item1 << item2 << item4) + << (QVector() << item1 << item3 << item4) + << (QVector() << item1 << item2 << item3 << item4); } void tst_qquickwindow::mergeTouchPointLists() -- cgit v1.2.3 From 3dd1578386fc9fa5fdea666bff47ff60174b270b Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 1 Aug 2016 17:09:50 +0200 Subject: Rename touchEventAccepted to match its purpose The bool is used to check if the touch or mouse event is accepted, so just call it eventAccepted. Change-Id: If22c9f759411edb9357a68273bcaa44364acc4e1 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index cc422361f8..d07778b8bc 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2263,7 +2263,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ return false; qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item; - bool touchEventAccepted = false; + bool eventAccepted = false; // First check whether the parent wants to be a filter, // and if the parent accepts the event we are done. @@ -2280,17 +2280,17 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ // Deliver the touch event to the given item qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; QCoreApplication::sendEvent(item, touchEvent.data()); - touchEventAccepted = touchEvent->isAccepted(); + eventAccepted = touchEvent->isAccepted(); // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it. QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { + if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event if (translateTouchToMouse(item, touchEvent.data())) - touchEventAccepted = true; + eventAccepted = true; } - if (touchEventAccepted) { + if (eventAccepted) { // If the touch was accepted (regardless by whom or in what form), // update accepted new points. for (auto point: qAsConst(touchEvent->touchPoints())) { @@ -2312,7 +2312,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ } } - return touchEventAccepted; + return eventAccepted; } // create touch event containing only points inside the target item -- cgit v1.2.3 From 70b05000e1ffd3942bd631520f2996209d0f8770 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 1 Aug 2016 17:12:33 +0200 Subject: Rename translateTouchToMouse -> deliverTouchAsMouse This function actually sends the event, not just "translates". Change-Id: Ia9b4d136f8f6c214f52beacf89cdf686e4617570 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 4 ++-- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index d07778b8bc..0b3026c167 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -622,7 +622,7 @@ bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp) return doubleClicked; } -bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event) +bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *event) { Q_Q(QQuickWindow); auto device = QQuickPointerDevice::touchDevice(event->device()); @@ -2286,7 +2286,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - if (translateTouchToMouse(item, touchEvent.data())) + if (deliverTouchAsMouse(item, touchEvent.data())) eventAccepted = true; } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index cd63f5a8f6..5747a26c9d 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -138,7 +138,7 @@ public: // Mouse positions are saved in widget coordinates QPointF lastMousePosition; - bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event); + bool deliverTouchAsMouse(QQuickItem *item, QTouchEvent *event); void translateTouchEvent(QTouchEvent *touchEvent); void setMouseGrabber(QQuickItem *grabber); void grabTouchPoints(QQuickItem *grabber, const QVector &ids); -- cgit v1.2.3 From 388b2c5b46365a12aea5aaab091f24314349a102 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 1 Aug 2016 17:30:05 +0200 Subject: Keep pointer to touch point that is accessed twice Change-Id: I460680e522ae8dd14b488ebba5e9608f9ce6ffe4 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 966a78f56b..39dcf0ac1c 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -583,8 +583,9 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) { } for (int i = 0; i < newPointCount; ++i) { - m_touchPoints.at(i)->reset(tps.at(i), ev->timestamp()); - m_touchPoints.at(i)->setGrabber(grabbers.at(i)); + auto point = m_touchPoints.at(i); + point->reset(tps.at(i), ev->timestamp()); + point->setGrabber(grabbers.at(i)); } m_pointCount = newPointCount; return this; -- cgit v1.2.3 From 54ee6994e66936c1d532f7718c858c9b18e3c91d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 28 Jul 2016 17:40:32 +0200 Subject: Add a checksum to the generated QML compilation units Change-Id: Icd5b1d805059981cbbb4c0eb2a5c842d59223839 Reviewed-by: Ulf Hermann --- src/qml/compiler/qqmlirbuilder.cpp | 16 +++++++++++++++- src/qml/compiler/qqmlirbuilder_p.h | 2 +- src/qml/compiler/qqmltypecompiler.cpp | 2 +- src/qml/compiler/qv4compileddata.cpp | 13 +++++++++++++ src/qml/compiler/qv4compileddata_p.h | 10 +++++++++- src/qml/compiler/qv4compiler.cpp | 1 + src/qml/qml/qqmltypeloader.cpp | 3 ++- src/qml/qml/qqmltypeloader_p.h | 4 +++- 8 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index f736e04b88..31250d0d57 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1359,7 +1359,7 @@ bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *prope return QQmlJS::AST::cast(expr); } -QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) +QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes) { QQmlRefPointer compilationUnit = output.javaScriptCompilationUnit; QV4::CompiledData::Unit *jsUnit = compilationUnit->createUnitData(&output); @@ -1402,6 +1402,20 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) qmlUnit->offsetToStringTable = totalSize - output.jsGenerator.stringTable.sizeOfTableAndData(); qmlUnit->stringTableSize = output.jsGenerator.stringTable.stringCount(); +#ifndef V4_BOOTSTRAP + if (!dependentTypes.isEmpty()) { + QCryptographicHash hash(QCryptographicHash::Md5); + if (dependentTypes.addToHash(&hash, engine)) { + QByteArray checksum = hash.result(); + Q_ASSERT(checksum.size() == sizeof(qmlUnit->dependencyMD5Checksum)); + memcpy(qmlUnit->dependencyMD5Checksum, checksum.constData(), sizeof(qmlUnit->dependencyMD5Checksum)); + } + } +#else + Q_UNUSED(dependentTypes); + Q_UNUSED(engine); +#endif + // write imports char *importPtr = data + qmlUnit->offsetToImports; foreach (const QV4::CompiledData::Import *imp, output.imports) { diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 821a6bba6d..eedc262e7a 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -549,7 +549,7 @@ public: struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator { - QV4::CompiledData::Unit *generate(Document &output); + QV4::CompiledData::Unit *generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes); private: typedef bool (Binding::*BindingFilter)() const; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index a0b219e28e..68de9aee44 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -156,7 +156,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() // Generate QML compiled type data structures QmlIR::QmlUnitGenerator qmlGenerator; - QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document); + QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document, QQmlEnginePrivate::get(engine), resolvedTypes); Q_ASSERT(document->javaScriptCompilationUnit); // The js unit owns the data and will free the qml unit. diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 2aab1743cc..83ae9c56df 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -579,6 +579,19 @@ void ResolvedTypeReference::doDynamicTypeCheck() mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); isFullyDynamicType = qtTypeInherits(mo); } + +bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const +{ + for (auto it = constBegin(), end = constEnd(); it != end; ++it) { + QQmlPropertyCache *pc = it.value()->createPropertyCache(engine); + bool ok = false; + hash->addData(pc->checksum(&ok)); + if (!ok) + return false; + } + return true; +} + #endif } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 82e303f27d..519708f089 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -611,6 +611,7 @@ struct Unit LEUInt32 architectureIndex; // string index to QSysInfo::buildAbi() LEUInt32 codeGeneratorIndex; + char dependencyMD5Checksum[16]; enum : unsigned int { IsJavascript = 0x1, @@ -788,7 +789,14 @@ struct ResolvedTypeReference void doDynamicTypeCheck(); }; // map from name index -typedef QHash ResolvedTypeReferenceMap; +// While this could be a hash, a map is chosen here to provide a stable +// order, which is used to calculating a check-sum on dependent meta-objects. +struct ResolvedTypeReferenceMap: public QMap +{ + bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const; +}; +#else +struct ResolvedTypeReferenceMap {}; #endif // index is per-object binding index diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 768a4ffcd3..8503317430 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -361,6 +361,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.qtVersion = QT_VERSION; unit.architectureIndex = registerString(QSysInfo::buildAbi()); unit.codeGeneratorIndex = registerString(codeGeneratorName); + memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); quint32 nextOffset = sizeof(CompiledData::Unit); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index c54af43a6f..9f94aa41ec 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2910,7 +2910,8 @@ void QQmlScriptBlob::dataReceived(const Data &data) irUnit.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary; QmlIR::QmlUnitGenerator qmlGenerator; - QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit); + QV4::CompiledData::ResolvedTypeReferenceMap emptyDependencies; + QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit, m_typeLoader->engine(), emptyDependencies); Q_ASSERT(!unit->data); // The js unit owns the data and will free the qml unit. unit->data = unitData; diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index ab6b046fcf..5f754df1fc 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -463,7 +463,9 @@ private: QList m_compositeSingletons; // map from name index to resolved type - QHash m_resolvedTypes; + // While this could be a hash, a map is chosen here to provide a stable + // order, which is used to calculating a check-sum on dependent meta-objects. + QMap m_resolvedTypes; bool m_typesResolved:1; QQmlRefPointer m_compiledData; -- cgit v1.2.3 From e611c00d8c73f0aae77920646db88c09f0f880fa Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 29 Jul 2016 14:09:00 +0200 Subject: Stop copying mouse events when delivering them Reduce allocations of events, just refill the local pos. Change-Id: I2948faf0e302bff315e482f2c1432fe0def19bc5 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 6 ++-- src/quick/items/qquickevents_p_p.h | 2 +- src/quick/items/qquickwindow.cpp | 35 +++++++++++----------- src/quick/items/qquickwindow_p.h | 2 +- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 12 ++++++-- 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 39dcf0ac1c..cfc35efbb4 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -619,9 +619,11 @@ bool QQuickPointerMouseEvent::allPointsAccepted() const { return m_mousePoint->isAccepted(); } -QMouseEvent *QQuickPointerMouseEvent::asMouseEvent() const +QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const { - return static_cast(m_event); + auto event = static_cast(m_event); + event->setLocalPos(localPos); + return event; } QVector QQuickPointerMouseEvent::grabbers() const diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 571c257a26..072a90d864 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -401,7 +401,7 @@ public: QVector grabbers() const override; void clearGrabbers() const override; - QMouseEvent *asMouseEvent() const; + QMouseEvent *asMouseEvent(const QPointF& localPos) const; private: QQuickEventPoint *m_mousePoint; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 0b3026c167..e08147d7b0 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1622,56 +1622,57 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t return me; } -bool QQuickWindowPrivate::deliverInitialMousePressEvent(QMouseEvent *event) +void QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickPointerMouseEvent *event) { Q_Q(QQuickWindow); + QPointF scenePos = event->point(0)->scenePos(); - QVector targets = pointerTargets(contentItem, event->windowPos(), true); + QVector targets = pointerTargets(contentItem, scenePos, true); for (QQuickItem *item: qAsConst(targets)) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (itemPrivate->acceptedMouseButtons() & event->button()) { - QPointF localPos = item->mapFromScene(event->windowPos()); + QPointF localPos = item->mapFromScene(scenePos); if (item->contains(localPos)) { - QScopedPointer me(cloneMouseEvent(event, &localPos)); + QMouseEvent *me = event->asMouseEvent(localPos); me->accept(); - q->sendEvent(item, me.data()); - event->setAccepted(me->isAccepted()); + q->sendEvent(item, me); if (me->isAccepted()) { if (!q->mouseGrabberItem()) item->grabMouse(); - return true; + return; } } } } - return false; + // no item accepted the event, make sure we don't accept the original mouse event + event->asMouseEvent(QPointF())->setAccepted(false); } void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { Q_Q(QQuickWindow); - auto event = pointerEvent->asMouseEvent(); lastMousePosition = pointerEvent->point(0)->scenePos(); QQuickItem *mouseGrabberItem = q->mouseGrabberItem(); if (mouseGrabberItem) { // send update - QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos()); - QScopedPointer me(cloneMouseEvent(event, &localPos)); + QPointF localPos = mouseGrabberItem->mapFromScene(lastMousePosition); + auto me = pointerEvent->asMouseEvent(localPos); me->accept(); - q->sendEvent(mouseGrabberItem, me.data()); - event->setAccepted(me->isAccepted()); + q->sendEvent(mouseGrabberItem, me); + pointerEvent->point(0)->setAccepted(me->isAccepted()); // release event, make sure to ungrab if there still is a grabber - if (event->type() == QEvent::MouseButtonRelease && !event->buttons() && q->mouseGrabberItem()) + if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem()) q->mouseGrabberItem()->ungrabMouse(); } else { // send initial press - event->setAccepted(false); if (pointerEvent->isPressEvent()) { - bool delivered = deliverInitialMousePressEvent(event); - event->setAccepted(delivered); + deliverInitialMousePressEvent(pointerEvent); + } else { + // make sure not to accept unhandled events + pointerEvent->asMouseEvent(QPointF())->setAccepted(false); } } } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 5747a26c9d..a97ed36b09 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -145,7 +145,7 @@ public: void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); static void transformTouchPoints(QList &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); - bool deliverInitialMousePressEvent(QMouseEvent *); + void deliverInitialMousePressEvent(QQuickPointerMouseEvent *); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet *); #ifndef QT_NO_WHEELEVENT diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index e57e27533b..a9953b944c 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -2400,19 +2400,27 @@ void tst_qquickwindow::testHoverChildMouseEventFilter() void tst_qquickwindow::pointerEventTypeAndPointCount() { - QMouseEvent me(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QPointF localPosition(33, 66); + QPointF scenePosition(133, 166); + QPointF screenPosition(333, 366); + QMouseEvent me(QEvent::MouseButtonPress, localPosition, scenePosition, screenPosition, + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QTouchEvent te(QEvent::TouchBegin, touchDevice, Qt::NoModifier, Qt::TouchPointPressed, QList() << QTouchEvent::TouchPoint(1)); + QQuickPointerMouseEvent pme; pme.reset(&me); QVERIFY(pme.isValid()); - QCOMPARE(pme.asMouseEvent(), &me); + QCOMPARE(pme.asMouseEvent(localPosition), &me); QVERIFY(pme.asPointerMouseEvent()); QVERIFY(!pme.asPointerTouchEvent()); QVERIFY(!pme.asPointerTabletEvent()); // QVERIFY(!pe->asTabletEvent()); // TODO QCOMPARE(pme.pointCount(), 1); + QCOMPARE(pme.point(0)->scenePos(), scenePosition); + QCOMPARE(pme.asMouseEvent(localPosition)->localPos(), localPosition); + QCOMPARE(pme.asMouseEvent(localPosition)->screenPos(), screenPosition); QQuickPointerTouchEvent pte; pte.reset(&te); -- cgit v1.2.3 From 0b73cc36f398aad9001439987c6c969afae6492e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 1 Aug 2016 15:06:04 +0200 Subject: QQuickWindowPrivate::deliverMouseEvent: use the event's grabber A tiny step toward delivery unification: the grabber in the given event (without requiring it to be a mouse event) should be the same as the current mouse grabber. Change-Id: Ice5530d01d999be78b5d10b509f669f247d4bae9 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index e08147d7b0..ad174d66ff 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1651,17 +1651,16 @@ void QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickPointerMouseEvent void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { Q_Q(QQuickWindow); - - lastMousePosition = pointerEvent->point(0)->scenePos(); - - QQuickItem *mouseGrabberItem = q->mouseGrabberItem(); - if (mouseGrabberItem) { + auto point = pointerEvent->point(0); + lastMousePosition = point->scenePos(); + QQuickItem *grabber = point->grabber(); + if (grabber) { // send update - QPointF localPos = mouseGrabberItem->mapFromScene(lastMousePosition); + QPointF localPos = grabber->mapFromScene(lastMousePosition); auto me = pointerEvent->asMouseEvent(localPos); me->accept(); - q->sendEvent(mouseGrabberItem, me); - pointerEvent->point(0)->setAccepted(me->isAccepted()); + q->sendEvent(grabber, me); + point->setAccepted(me->isAccepted()); // release event, make sure to ungrab if there still is a grabber if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem()) -- cgit v1.2.3 From bc84dd0b85c5067a75c7a7b6c30527c08a4844bb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 13 Jul 2016 11:27:28 +0200 Subject: Make generated code loading constants relocatable On 32-bit architectures we were encoding the absolute address of generated constants in memory into the load instruction. In order to make the code reloctable, this patch changes the JIT over to use the constant table in the compilation unit. This means two additional loads per constant. On architectures that support instruction pointer relative addressing, we can try to fix this in the future (arm32 for example). Change-Id: I8ed7aa7c67711696d6c46c72b3b642d610ff2cbc Reviewed-by: Erik Verbruggen --- src/qml/compiler/qv4compiler.cpp | 4 +++ src/qml/jit/qv4assembler.cpp | 16 ++++++++++-- src/qml/jit/qv4assembler_p.h | 25 +++---------------- src/qml/jit/qv4binop.cpp | 8 +++--- src/qml/jit/qv4isel_masm.cpp | 43 --------------------------------- src/qml/jit/qv4isel_masm_p.h | 1 - src/qml/jsruntime/qv4context.cpp | 4 +++ src/qml/jsruntime/qv4context_p.h | 8 +++--- src/qml/jsruntime/qv4functionobject.cpp | 2 ++ src/qml/jsruntime/qv4script.cpp | 1 + src/qml/jsruntime/qv4script_p.h | 4 +++ 11 files changed, 42 insertions(+), 74 deletions(-) diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 8503317430..924f2e15e2 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -43,6 +43,7 @@ #include #include #include +#include QV4::Compiler::StringTableGenerator::StringTableGenerator() { @@ -378,6 +379,9 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp nextOffset += unit.regexpTableSize * sizeof(CompiledData::RegExp); unit.constantTableSize = constants.size(); + + // Ensure we load constants from well-aligned addresses into for example SSE registers. + nextOffset = static_cast(WTF::roundUpToMultipleOf(16, nextOffset)); unit.offsetToConstantTable = nextOffset; nextOffset += unit.constantTableSize * sizeof(ReturnedValue); diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 08e4f0a8c0..625f5b5e5a 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -148,8 +148,7 @@ bool CompilationUnit::memoryMapCode(QString *errorString) const Assembler::VoidType Assembler::Void; Assembler::Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) - : _constTable(this) - , _function(function) + : _function(function) , _nextBlock(0) , _executableAllocator(executableAllocator) , _isel(isel) @@ -279,6 +278,19 @@ Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &s return Pointer(reg, id * sizeof(QV4::String*)); } +Assembler::Address Assembler::loadConstant(IR::Const *c, RegisterID baseReg) +{ + return loadConstant(convertToValue(c), baseReg); +} + +Assembler::Address Assembler::loadConstant(const Primitive &v, RegisterID baseReg) +{ + loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), baseReg); + loadPtr(Address(baseReg, qOffsetOf(QV4::Heap::ExecutionContext, constantTable)), baseReg); + const int index = _isel->jsUnitGenerator()->registerConstant(v.asReturnedValue()); + return Address(baseReg, index * sizeof(QV4::Value)); +} + void Assembler::loadStringRef(RegisterID reg, const QString &string) { const int id = _isel->registerString(string); diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 669b0b2ff4..d76a21c74c 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -89,7 +89,6 @@ struct CompilationUnit : public QV4::CompiledData::CompilationUnit // Coderef + execution engine QVector codeRefs; - QList > constantValues; }; struct LookupCall { @@ -297,22 +296,6 @@ public: int savedRegCount; }; - class ConstantTable - { - public: - ConstantTable(Assembler *as): _as(as) {} - - int add(const QV4::Primitive &v); - Address loadValueAddress(IR::Const *c, RegisterID baseReg); - Address loadValueAddress(const QV4::Primitive &v, RegisterID baseReg); - void finalize(JSC::LinkBuffer &linkBuffer, InstructionSelection *isel); - - private: - Assembler *_as; - QVector _values; - QVector _toPatch; - }; - struct VoidType { VoidType() {} }; static const VoidType Void; @@ -382,6 +365,8 @@ public: Pointer loadTempAddress(IR::Temp *t); Pointer loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al); Pointer loadStringAddress(RegisterID reg, const QString &string); + Address loadConstant(IR::Const *c, RegisterID baseReg); + Address loadConstant(const Primitive &v, RegisterID baseReg); void loadStringRef(RegisterID reg, const QString &string); Pointer stackSlotPointer(IR::Temp *t) const { @@ -1029,7 +1014,7 @@ public: move(TrustedImm64(i), ReturnValueRegister); move64ToDouble(ReturnValueRegister, target); #else - JSC::MacroAssembler::loadDouble(constantTable().loadValueAddress(c, ScratchRegister), target); + JSC::MacroAssembler::loadDouble(loadConstant(c, ScratchRegister), target); #endif return target; } @@ -1093,7 +1078,7 @@ public: // it's not in signed int range, so load it as a double, and truncate it down loadDouble(addr, FPGpr0); - Address inversionAddress = constantTable().loadValueAddress(QV4::Primitive::fromDouble(double(INT_MAX) + 1), scratchReg); + Address inversionAddress = loadConstant(QV4::Primitive::fromDouble(double(INT_MAX) + 1), scratchReg); subDouble(inversionAddress, FPGpr0); Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg); canNeverHappen.link(this); @@ -1111,14 +1096,12 @@ public: void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave); const StackLayout &stackLayout() const { return *_stackLayout.data(); } - ConstantTable &constantTable() { return _constTable; } Label exceptionReturnLabel; IR::BasicBlock * catchBlock; QVector exceptionPropagationJumps; private: QScopedPointer _stackLayout; - ConstantTable _constTable; IR::Function *_function; QHash _addrs; QHash > _patches; diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index c09fc6fdca..45cc9259c3 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -162,7 +162,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) #if CPU(X86) if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address] as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister); + Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister); as->addDouble(addr, targetReg); break; } @@ -184,7 +184,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) #if CPU(X86) if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address] as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister); + Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister); as->mulDouble(addr, targetReg); break; } @@ -203,7 +203,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) #if CPU(X86) if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address] as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister); + Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister); as->subDouble(addr, targetReg); break; } @@ -231,7 +231,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) #if CPU(X86) if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address] as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister); + Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister); as->divDouble(addr, targetReg); break; } diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 4066ab213c..da28df817d 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -177,7 +177,6 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) linkBuffer.patch(label, linkBuffer.locationOf(target)); } } - _constTable.finalize(linkBuffer, _isel); *codeSize = linkBuffer.offsetOf(endOfCode); @@ -370,16 +369,6 @@ void InstructionSelection::run(int functionIndex) qSwap(_removableJumps, removableJumps); } -const void *InstructionSelection::addConstantTable(QVector *values) -{ - compilationUnit->constantValues.append(*values); - values->clear(); - - QVector &finalValues = compilationUnit->constantValues.last(); - finalValues.squeeze(); - return finalValues.constData(); -} - QQmlRefPointer InstructionSelection::backendCompileStep() { QQmlRefPointer result; @@ -1719,38 +1708,6 @@ bool operator==(const Primitive &v1, const Primitive &v2) } // QV4 namespace QT_END_NAMESPACE -int Assembler::ConstantTable::add(const Primitive &v) -{ - int idx = _values.indexOf(v); - if (idx == -1) { - idx = _values.size(); - _values.append(v); - } - return idx; -} - -Assembler::Address Assembler::ConstantTable::loadValueAddress(IR::Const *c, RegisterID baseReg) -{ - return loadValueAddress(convertToValue(c), baseReg); -} - -Assembler::Address Assembler::ConstantTable::loadValueAddress(const Primitive &v, RegisterID baseReg) -{ - _toPatch.append(_as->moveWithPatch(TrustedImmPtr(0), baseReg)); - Address addr(baseReg); - addr.offset = add(v) * sizeof(QV4::Primitive); - Q_ASSERT(addr.offset >= 0); - return addr; -} - -void Assembler::ConstantTable::finalize(JSC::LinkBuffer &linkBuffer, InstructionSelection *isel) -{ - const void *tablePtr = isel->addConstantTable(&_values); - - foreach (DataLabelPtr label, _toPatch) - linkBuffer.patch(label, const_cast(tablePtr)); -} - bool InstructionSelection::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) { diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 4b35a72e01..93453f71be 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -81,7 +81,6 @@ public: virtual void run(int functionIndex); - const void *addConstantTable(QVector *values); protected: virtual QQmlRefPointer backendCompileStep(); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 97b3e26a26..60717c9491 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -73,6 +73,7 @@ Heap::CallContext *ExecutionContext::newCallContext(const FunctionObject *functi c->compilationUnit = function->function()->compilationUnit; c->lookups = c->compilationUnit->runtimeLookups; + c->constantTable = c->compilationUnit->data->constants(); c->locals = (Value *)((quintptr(c + 1) + 7) & ~7); const CompiledData::Function *compiledFunction = function->function()->compiledFunction; @@ -172,6 +173,7 @@ Heap::WithContext::WithContext(ExecutionContext *outerContext, Object *with) outer = outerContext; callData = outer->callData; lookups = outer->lookups; + constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; withObject = with; @@ -184,6 +186,7 @@ Heap::CatchContext::CatchContext(ExecutionContext *outerContext, String *excepti strictMode = outer->strictMode; callData = outer->callData; lookups = outer->lookups; + constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; this->exceptionVarName = exceptionVarName; @@ -197,6 +200,7 @@ Heap::QmlContext::QmlContext(QV4::ExecutionContext *outerContext, QV4::QmlContex strictMode = false; callData = outer->callData; lookups = outer->lookups; + constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; this->qml = qml->d(); diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 2e6773a927..368605ca4a 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -108,6 +108,7 @@ struct ExecutionContext : Base { ExecutionEngine *engine; Pointer outer; Lookup *lookups; + const QV4::Value *constantTable; CompiledData::CompilationUnit *compilationUnit; ContextType type : 8; @@ -118,9 +119,10 @@ struct ExecutionContext : Base { inline ExecutionContext::ExecutionContext(ExecutionEngine *engine, ContextType t) : engine(engine) - , outer(0) - , lookups(0) - , compilationUnit(0) + , outer(nullptr) + , lookups(nullptr) + , constantTable(nullptr) + , compilationUnit(nullptr) , type(t) , strictMode(false) , lineNumber(-1) diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 6b9c552350..805087e389 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -535,6 +535,7 @@ void SimpleScriptFunction::construct(const Managed *that, Scope &scope, CallData ctx.function = f->d(); ctx.compilationUnit = f->function()->compilationUnit; ctx.lookups = ctx.compilationUnit->runtimeLookups; + ctx.constantTable = ctx.compilationUnit->data->constants(); ctx.outer = f->scope(); ctx.locals = scope.alloc(f->varCount()); for (int i = callData->argc; i < (int)f->formalParameterCount(); ++i) @@ -572,6 +573,7 @@ void SimpleScriptFunction::call(const Managed *that, Scope &scope, CallData *cal ctx.function = f->d(); ctx.compilationUnit = f->function()->compilationUnit; ctx.lookups = ctx.compilationUnit->runtimeLookups; + ctx.constantTable = ctx.compilationUnit->data->constants(); ctx.outer = f->scope(); ctx.locals = scope.alloc(f->varCount()); for (int i = callData->argc; i < (int)f->formalParameterCount(); ++i) diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index a2e379ec1a..46adaf7e79 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -222,6 +222,7 @@ ReturnedValue Script::run() ContextStateSaver stateSaver(valueScope, scope); scope->d()->strictMode = vmFunction->isStrict(); scope->d()->lookups = vmFunction->compilationUnit->runtimeLookups; + scope->d()->constantTable = vmFunction->compilationUnit->data->constants(); scope->d()->compilationUnit = vmFunction->compilationUnit; return Q_V4_PROFILE(engine, vmFunction); diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index e81bc3049c..2e87a7692b 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -71,6 +71,7 @@ struct ContextStateSaver { Value *savedContext; bool strictMode; Lookup *lookups; + const QV4::Value *constantTable; CompiledData::CompilationUnit *compilationUnit; int lineNumber; @@ -78,6 +79,7 @@ struct ContextStateSaver { : savedContext(scope.alloc(1)) , strictMode(context->d()->strictMode) , lookups(context->d()->lookups) + , constantTable(context->d()->constantTable) , compilationUnit(context->d()->compilationUnit) , lineNumber(context->d()->lineNumber) { @@ -87,6 +89,7 @@ struct ContextStateSaver { : savedContext(scope.alloc(1)) , strictMode(context->strictMode) , lookups(context->lookups) + , constantTable(context->constantTable) , compilationUnit(context->compilationUnit) , lineNumber(context->lineNumber) { @@ -98,6 +101,7 @@ struct ContextStateSaver { Heap::ExecutionContext *ctx = static_cast(savedContext->m()); ctx->strictMode = strictMode; ctx->lookups = lookups; + ctx->constantTable = constantTable; ctx->compilationUnit = compilationUnit; ctx->lineNumber = lineNumber; } -- cgit v1.2.3 From c5e75a9f647139336e254fa788aa39e9772a34d6 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 2 Aug 2016 14:59:39 +0200 Subject: Optimize QQuickWindowPrivate::deliverPressEvent slightly Calling the merge function at least one time less often saves some vector moving around. Change-Id: I4573fa4cb34398fa6297786558a21b780429d121 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ad174d66ff..560aab9e73 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2243,7 +2243,11 @@ bool QQuickWindowPrivate::deliverNewTouchPoints(QQuickPointerTouchEvent *event, QVector targetItems; for (QPointF point: points) { QVector targetItemsForPoint = pointerTargets(contentItem, point, false); - targetItems = mergePointerTargets(targetItems, targetItemsForPoint); + if (targetItems.count()) { + targetItems = mergePointerTargets(targetItems, targetItemsForPoint); + } else { + targetItems = targetItemsForPoint; + } } for (QQuickItem *item: targetItems) -- cgit v1.2.3 From 340563013ed7dcb0229085180b68cfea18317e0d Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 2 Aug 2016 14:08:01 +0200 Subject: Use public function for QQuickItem::acceptedMouseButtons There is no point in calling the privat if the public version does exist and returns the same. Change-Id: Ia079af4ba84a631b840e40e2a76f1fd911afcfbd Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 560aab9e73..74998b9161 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1629,8 +1629,7 @@ void QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickPointerMouseEvent QVector targets = pointerTargets(contentItem, scenePos, true); for (QQuickItem *item: qAsConst(targets)) { - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - if (itemPrivate->acceptedMouseButtons() & event->button()) { + if (item->acceptedMouseButtons() & event->button()) { QPointF localPos = item->mapFromScene(scenePos); if (item->contains(localPos)) { QMouseEvent *me = event->asMouseEvent(localPos); -- cgit v1.2.3 From 80c7ce5e0c990fb67f90563fb81b16e33699a522 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 1 Aug 2016 15:17:52 +0200 Subject: add QQuickPointerEvent::setAccepted/isAccepted to avoid mouse/touch casting This makes deliverMouseEvent() slightly more generic: the only place we now care about it being a mouse event is to be able to call sendEvent() with the original event. Change-Id: Ibd1660bb45b6f8b3a5b9926aeb3dc85ff2e41d0f Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents_p_p.h | 2 ++ src/quick/items/qquickwindow.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 072a90d864..b28c68c090 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -362,6 +362,8 @@ public: // helpers for C++ only (during event delivery) virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } bool isValid() const { return m_event != nullptr; } virtual bool allPointsAccepted() const = 0; + bool isAccepted() { return m_event->isAccepted(); } + void setAccepted(bool accepted) { m_event->setAccepted(accepted); } QVector unacceptedPointScenePositions() const; QVector unacceptedPressedPointScenePositions() const; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 74998b9161..b8a7382525 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1644,7 +1644,7 @@ void QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickPointerMouseEvent } } // no item accepted the event, make sure we don't accept the original mouse event - event->asMouseEvent(QPointF())->setAccepted(false); + event->setAccepted(false); } void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) @@ -1670,7 +1670,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven deliverInitialMousePressEvent(pointerEvent); } else { // make sure not to accept unhandled events - pointerEvent->asMouseEvent(QPointF())->setAccepted(false); + pointerEvent->setAccepted(false); } } } -- cgit v1.2.3 From af4150ce50ab3aa7b9f34a4ac7fc6dde317f66b0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 2 Aug 2016 13:11:00 +0200 Subject: Reduce duplicated code in MultiPointTouchArea Change-Id: I807a8bab270813ea62bd5028eae45910aeb65bbb Reviewed-by: Shawn Rutledge --- src/quick/items/qquickmultipointtoucharea.cpp | 31 ++++++++------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index ac5598767a..89bff6e057 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -448,20 +448,12 @@ void QQuickMultiPointTouchArea::touchEvent(QTouchEvent *event) } } updateTouchData(event); - if (event->type() == QEvent::TouchEnd) { - //TODO: move to window - _stealMouse = false; - setKeepMouseGrab(false); - setKeepTouchGrab(false); - ungrabTouchPoints(); - } + if (event->type() == QEvent::TouchEnd) + ungrab(); break; } case QEvent::TouchCancel: - _stealMouse = false; - setKeepMouseGrab(false); - setKeepTouchGrab(false); - ungrabTouchPoints(); + ungrab(); break; default: QQuickItem::touchEvent(event); @@ -784,13 +776,12 @@ void QQuickMultiPointTouchArea::mouseReleaseEvent(QMouseEvent *event) void QQuickMultiPointTouchArea::ungrab() { + _stealMouse = false; + setKeepMouseGrab(false); + setKeepTouchGrab(false); + ungrabTouchPoints(); + if (_touchPoints.count()) { - QQuickWindow *c = window(); - if (c && c->mouseGrabberItem() == this) { - _stealMouse = false; - setKeepMouseGrab(false); - } - setKeepTouchGrab(false); foreach (QObject *obj, _touchPoints) static_cast(obj)->setPressed(false); emit canceled(_touchPoints.values()); @@ -881,11 +872,7 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *i, QEvent *eve if (!shouldFilter(event)) return false; updateTouchData(event); - //TODO: verify this behavior - _stealMouse = false; - setKeepMouseGrab(false); - setKeepTouchGrab(false); - ungrabTouchPoints(); + ungrab(); } break; default: -- cgit v1.2.3 From cf186c441dd0d68dbfac416ee6d09d4f923a0a4c Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 1 Aug 2016 16:59:55 +0200 Subject: D3D12: Fix winrt build Change-Id: I91a90a9141bd17b01a9b87d8ad39c11f8b386a8c Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 2 ++ src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 34ab4d11b0..e32ecdc138 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -682,9 +682,11 @@ void QSGD3D12EnginePrivate::releaseResources() commandQueue = nullptr; copyCommandQueue = nullptr; +#ifndef Q_OS_WINRT dcompTarget = nullptr; dcompVisual = nullptr; dcompDevice = nullptr; +#endif swapChain = nullptr; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h index 2b1e3dc7b7..b3b244cd86 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h @@ -435,9 +435,11 @@ private: DeviceLossTester devLossTest; +#ifndef Q_OS_WINRT ComPtr dcompDevice; ComPtr dcompTarget; ComPtr dcompVisual; +#endif }; inline uint qHash(const QSGD3D12EnginePrivate::PersistentFrameData::PendingRelease &pr, uint seed = 0) -- cgit v1.2.3 From 44e86f701247a86da37e3a94b77853b57ab1b303 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 27 Jul 2016 11:59:17 +0200 Subject: Add optional flags and rect to QSGRenderNode Having rendernodes triggering fullscreen updates with the software backend is not ideal. Therefore, introduce the option of reporting that the rendernode is well-behaving, meaning it only writes inside the reported bounding rectangle. Similarly, the OpenGL batch renderer can keep using the depth buffer when the rendernode is known to behave like the renderer expects. Change-Id: I6acc434432c2504776f26e8917b5434b44be293d Reviewed-by: Gunnar Sletta --- .../quick/scenegraph/rendernode/d3d12renderer.cpp | 10 ++++ .../quick/scenegraph/rendernode/d3d12renderer.h | 2 + .../quick/scenegraph/rendernode/openglrenderer.cpp | 10 ++++ .../quick/scenegraph/rendernode/openglrenderer.h | 2 + .../scenegraph/rendernode/softwarerenderer.cpp | 12 +++- .../quick/scenegraph/rendernode/softwarerenderer.h | 2 + .../software/qsgsoftwarerenderablenode.cpp | 18 +++++- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 8 ++- src/quick/scenegraph/coreapi/qsgrendernode.cpp | 67 ++++++++++++++++++++++ src/quick/scenegraph/coreapi/qsgrendernode.h | 10 ++++ 10 files changed, 134 insertions(+), 7 deletions(-) diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp index f475ec838b..d35f82a76a 100644 --- a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp +++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp @@ -270,4 +270,14 @@ void D3D12RenderNode::render(const RenderState *state) // No need to reimplement changedStates() because no relevant commands are // added to the command list in render(). +QSGRenderNode::RenderingFlags D3D12RenderNode::flags() const +{ + return BoundedRectRendering | DepthAwareRendering; +} + +QRectF D3D12RenderNode::rect() const +{ + return QRect(0, 0, m_item->width(), m_item->height()); +} + #endif // HAS_D3D12 diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.h b/examples/quick/scenegraph/rendernode/d3d12renderer.h index a81db0f398..f13a1d451c 100644 --- a/examples/quick/scenegraph/rendernode/d3d12renderer.h +++ b/examples/quick/scenegraph/rendernode/d3d12renderer.h @@ -60,6 +60,8 @@ public: void render(const RenderState *state) override; void releaseResources() override; + RenderingFlags flags() const override; + QRectF rect() const override; private: void init(); diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp index 33c59198b8..3de864b7b9 100644 --- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp +++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp @@ -151,4 +151,14 @@ QSGRenderNode::StateFlags OpenGLRenderNode::changedStates() const return BlendState; } +QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const +{ + return BoundedRectRendering | DepthAwareRendering; +} + +QRectF OpenGLRenderNode::rect() const +{ + return QRect(0, 0, m_item->width(), m_item->height()); +} + #endif // QT_NO_OPENGL diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.h b/examples/quick/scenegraph/rendernode/openglrenderer.h index 28d528e617..1ee6350218 100644 --- a/examples/quick/scenegraph/rendernode/openglrenderer.h +++ b/examples/quick/scenegraph/rendernode/openglrenderer.h @@ -58,6 +58,8 @@ public: void render(const RenderState *state) override; void releaseResources() override; StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; private: void init(); diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp index 2f11c56f29..06e406874a 100644 --- a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp +++ b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp @@ -68,7 +68,7 @@ void SoftwareRenderNode::render(const RenderState *renderState) p->setOpacity(inheritedOpacity()); const QRegion *clipRegion = renderState->clipRegion(); if (clipRegion && !clipRegion->isEmpty()) - p->setClipRegion(*clipRegion); + p->setClipRegion(*clipRegion, Qt::IntersectClip); const QPointF p0(m_item->width() - 1, m_item->height() - 1); const QPointF p1(0, 0); @@ -89,3 +89,13 @@ QSGRenderNode::StateFlags SoftwareRenderNode::changedStates() const { return 0; } + +QSGRenderNode::RenderingFlags SoftwareRenderNode::flags() const +{ + return BoundedRectRendering; +} + +QRectF SoftwareRenderNode::rect() const +{ + return QRect(0, 0, m_item->width(), m_item->height()); +} diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.h b/examples/quick/scenegraph/rendernode/softwarerenderer.h index 60036f96a1..5b2a475ed8 100644 --- a/examples/quick/scenegraph/rendernode/softwarerenderer.h +++ b/examples/quick/scenegraph/rendernode/softwarerenderer.h @@ -54,6 +54,8 @@ public: void render(const RenderState *state) override; void releaseResources() override; StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; private: QQuickItem *m_item; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 032a06f946..d900688173 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -187,6 +187,12 @@ void QSGSoftwareRenderableNode::update() boundingRect = m_handle.spriteNode->rect().toRect(); break; case QSGSoftwareRenderableNode::RenderNode: + if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering) && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.renderNode->rect().toRect(); break; default: break; @@ -244,14 +250,20 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu rd->m_opacity = m_opacity; RenderNodeState rs; rs.cr = m_clipRegion; + + const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering) + ? m_boundingRect : + QRect(0, 0, painter->device()->width(), painter->device()->height()); + painter->save(); + painter->setClipRegion(br, Qt::ReplaceClip); m_handle.renderNode->render(&rs); painter->restore(); - const QRect fullRect = QRect(0, 0, painter->device()->width(), painter->device()->height()); - m_previousDirtyRegion = fullRect; + + m_previousDirtyRegion = QRegion(br); m_isDirty = false; m_dirtyRegion = QRegion(); - return fullRect; + return br; } } diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index bee2015007..49bbbf0ba8 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -1033,11 +1033,13 @@ void Renderer::nodeWasAdded(QSGNode *node, Node *shadowParent) m_rebuild |= FullRebuild; } else if (node->type() == QSGNode::RenderNodeType) { - RenderNodeElement *e = new RenderNodeElement(static_cast(node)); + QSGRenderNode *rn = static_cast(node); + RenderNodeElement *e = new RenderNodeElement(rn); snode->data = e; - Q_ASSERT(!m_renderNodeElements.contains(static_cast(node))); + Q_ASSERT(!m_renderNodeElements.contains(rn)); m_renderNodeElements.insert(e->renderNode, e); - m_useDepthBuffer = false; + if (!rn->flags().testFlag(QSGRenderNode::DepthAwareRendering)) + m_useDepthBuffer = false; m_rebuild |= FullRebuild; } diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 365abd09e2..5915d51f2b 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -179,6 +179,11 @@ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const default value according to the OpenGL specification. For other APIs, see the documentation for changedStates() for more information. + \note Depth writes are disabled when this function is called (for example, + glDepthMask(false) in case of OpenGL). Enabling depth writes can lead to + unexpected results, depending on the scenegraph backend in use, so nodes + should avoid this. + For APIs other than OpenGL, it will likely be necessary to query certain API-specific resources (for example, the graphics device or the command list/buffer to add the commands to). This is done via QSGRendererInterface. @@ -210,6 +215,68 @@ void QSGRenderNode::releaseResources() { } +/*! + \enum QSGRenderNode::RenderingFlag + + Possible values for the bitmask returned from flags(). + + \value BoundedRectRendering Indicates that the implementation of render() + does not render outside the area reported from rect() in item + coordinates. Such node implementations can lead to more efficient rendering, + depending on the scenegraph backend. For example, the software backend can + continue to use the more optimal partial update path when all render nodes + in the scene have this flag set. + + \value DepthAwareRendering Indicates that the implementations of render() + conforms to scenegraph expectations by only generating a Z value of 0 in + scene coordinates which is then transformed by the matrices retrieved from + RenderState::projectionMatrix() and matrix(), as described in the notes for + render(). Such node implementations can lead to more efficient rendering, + depending on the scenegraph backend. For example, the batching OpenGL + renderer can continue to use a more optimal path when all render nodes in + the scene have this flag set. + + \value OpaqueRendering Indicates that the implementation of render() writes + out opaque pixels for the entire area reported from rect(). By default the + renderers must assume that render() can also output semi or fully + transparent pixels. Setting this flag can improve performance in some + cases. + + \sa render(), rect() + */ + +/*! + \return flags describing the behavior of this render node. + + The default implementation returns 0. + + \sa RenderingFlag, rect() + */ +QSGRenderNode::RenderingFlags QSGRenderNode::flags() const +{ + return 0; +} + +/*! + \return the bounding rectangle in item coordinates for the area render() + touches. The value is only in use when flags() includes + BoundedRectRendering, ignored otherwise. + + Reporting the rectangle in combination with BoundedRectRendering is + particularly important with the \c software backend because otherwise + having a rendernode in the scene would trigger fullscreen updates, skipping + all partial update optimizations. + + For rendernodes covering the entire area of a corresponding QQuickItem the + return value will be (0, 0, item->width(), item->height()). + + \sa flags() +*/ +QRectF QSGRenderNode::rect() const +{ + return QRectF(); +} + /*! \return pointer to the current model-view matrix. */ diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.h b/src/quick/scenegraph/coreapi/qsgrendernode.h index 6eb425c03b..f6bc40d3ee 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.h +++ b/src/quick/scenegraph/coreapi/qsgrendernode.h @@ -61,6 +61,13 @@ public: }; Q_DECLARE_FLAGS(StateFlags, StateFlag) + enum RenderingFlag { + BoundedRectRendering = 0x01, + DepthAwareRendering = 0x02, + OpaqueRendering = 0x04 + }; + Q_DECLARE_FLAGS(RenderingFlags, RenderingFlag) + struct Q_QUICK_EXPORT RenderState { virtual ~RenderState(); virtual const QMatrix4x4 *projectionMatrix() const = 0; @@ -78,6 +85,8 @@ public: virtual StateFlags changedStates() const; virtual void render(const RenderState *state) = 0; virtual void releaseResources(); + virtual RenderingFlags flags() const; + virtual QRectF rect() const; const QMatrix4x4 *matrix() const; const QSGClipNode *clipList() const; @@ -89,6 +98,7 @@ private: }; Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::RenderingFlags) QT_END_NAMESPACE -- cgit v1.2.3 From d8198f2a16c97924700cdf96997d40dfac28917e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 1 Aug 2016 14:03:35 +0200 Subject: Improve debug output handling of disk caching code Use categorized logging to make the debugging output of disk caching more selective and also not interfere with the output expected by unit tests. Change-Id: I0f42060326dfface54d4e74da5777ffb894f5937 Reviewed-by: Ulf Hermann --- src/qml/qml/qqmltypeloader.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 9f94aa41ec..5bc9c8ac25 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -105,6 +106,9 @@ DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); DEFINE_BOOL_CONFIG_OPTION(diskCache, QML_DISK_CACHE); DEFINE_BOOL_CONFIG_OPTION(forceDiskCacheRefresh, QML_FORCE_DISK_CACHE_REFRESH); +Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) +Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache") + QT_BEGIN_NAMESPACE namespace { @@ -2075,7 +2079,7 @@ bool QQmlTypeData::tryLoadFromDiskCache() { QString error; if (!unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { - qDebug() << "Error loading" << url().toString() << "from disk cache:" << error; + qCDebug(DBG_DISK_CACHE) << "Error loading" << url().toString() << "from disk cache:" << error; return false; } } @@ -2485,7 +2489,7 @@ void QQmlTypeData::compile() if (diskCache() || forceDiskCacheRefresh()) { QString errorString; if (!m_compiledData->saveToDisk(&errorString)) { - qDebug() << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString; + qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString; } } } @@ -2877,7 +2881,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) initializeFromCompilationUnit(unit); return; } else { - qDebug() << "Error loading" << url().toString() << "from disk cache:" << error; + qCDebug(DBG_DISK_CACHE()) << "Error loading" << url().toString() << "from disk cache:" << error; } } @@ -2919,7 +2923,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) if (diskCache() || forceDiskCacheRefresh()) { QString errorString; if (!unit->saveToDisk(&errorString)) { - qDebug() << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString; + qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString; } } -- cgit v1.2.3 From 546679658ed0a39f758a9dcfad8f16a6552da7fa Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 29 Jul 2016 16:20:19 +0200 Subject: Don't save compilation units to disk that don't orignate from files It's asking for trouble and we have no way of verifying that the source changed. Change-Id: I2ae2e975ef52142b8a6821b23f99ab3517ae3d49 Reviewed-by: Ulf Hermann --- src/qml/compiler/qv4compileddata.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 83ae9c56df..121b5907db 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -291,6 +291,11 @@ bool CompilationUnit::saveToDisk(QString *errorString) { errorString->clear(); + if (data->sourceTimeStamp == 0) { + *errorString = QStringLiteral("Missing time stamp for source file"); + return false; + } + const QUrl unitUrl = url(); if (!unitUrl.isLocalFile()) { *errorString = QStringLiteral("File has to be a local file."); -- cgit v1.2.3 From 47ee54455beb1a063515041f85b4c216132491b3 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 2 Aug 2016 14:17:57 +0200 Subject: Start to unite press event delivery for touch and mouse Needs some cleanup, but seems to work now. Change-Id: I579009648d874c9293a0ebb3d7809536420b5574 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 76 +++++++++++++++++++++------------------- src/quick/items/qquickwindow_p.h | 5 ++- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index b8a7382525..3f5e85be5e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1622,31 +1622,6 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t return me; } -void QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickPointerMouseEvent *event) -{ - Q_Q(QQuickWindow); - QPointF scenePos = event->point(0)->scenePos(); - - QVector targets = pointerTargets(contentItem, scenePos, true); - for (QQuickItem *item: qAsConst(targets)) { - if (item->acceptedMouseButtons() & event->button()) { - QPointF localPos = item->mapFromScene(scenePos); - if (item->contains(localPos)) { - QMouseEvent *me = event->asMouseEvent(localPos); - me->accept(); - q->sendEvent(item, me); - if (me->isAccepted()) { - if (!q->mouseGrabberItem()) - item->grabMouse(); - return; - } - } - } - } - // no item accepted the event, make sure we don't accept the original mouse event - event->setAccepted(false); -} - void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { Q_Q(QQuickWindow); @@ -1666,12 +1641,15 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven q->mouseGrabberItem()->ungrabMouse(); } else { // send initial press + bool delivered = false; if (pointerEvent->isPressEvent()) { - deliverInitialMousePressEvent(pointerEvent); - } else { + QSet hasFiltered; + delivered = deliverPressEvent(pointerEvent, &hasFiltered); + } + + if (!delivered) // make sure not to accept unhandled events pointerEvent->setAccepted(false); - } } } @@ -2196,7 +2174,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) QSet hasFiltered; if (event->isPressEvent()) - deliverNewTouchPoints(event, &hasFiltered); + deliverPressEvent(event, &hasFiltered); if (!event->allPointsAccepted()) deliverUpdatedTouchPoints(event, &hasFiltered); @@ -2236,7 +2214,7 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve } // Deliver newly pressed touch points -bool QQuickWindowPrivate::deliverNewTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet *hasFiltered) { const QVector points = event->unacceptedPressedPointScenePositions(); QVector targetItems; @@ -2249,18 +2227,44 @@ bool QQuickWindowPrivate::deliverNewTouchPoints(QQuickPointerTouchEvent *event, } } - for (QQuickItem *item: targetItems) + for (QQuickItem *item: targetItems) { deliverMatchingPointsToItem(item, event, hasFiltered); + if (event->allPointsAccepted()) + break; + } return event->allPointsAccepted(); } -// touchEventForItem has no means to generate a touch event that contains -// only the points that are relevant for this item. Thus the need for -// matchingPoints to already be that set of interesting points. -// They are all pre-transformed, too. -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet *hasFiltered) { + Q_Q(QQuickWindow); + + // TODO: unite this mouse point delivery with the synthetic mouse event below + if (auto event = pointerEvent->asPointerMouseEvent()) { + if (item->acceptedMouseButtons() & event->button()) { + auto point = event->point(0); + if (point->isAccepted()) + return false; + QPointF localPos = item->mapFromScene(point->scenePos()); + Q_ASSERT(item->contains(localPos)); // transform is checked already + QMouseEvent *me = event->asMouseEvent(localPos); + me->accept(); + q->sendEvent(item, me); + if (me->isAccepted()) { + if (!q->mouseGrabberItem()) + item->grabMouse(); + point->setAccepted(true); + } + return me->isAccepted(); + } + return false; + } + + QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent(); + if (!event) + return false; + QScopedPointer touchEvent(event->touchEventForItem(item)); if (!touchEvent) return false; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index a97ed36b09..11cadb4739 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -145,7 +145,6 @@ public: void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); static void transformTouchPoints(QList &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); - void deliverInitialMousePressEvent(QQuickPointerMouseEvent *); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet *); #ifndef QT_NO_WHEELEVENT @@ -167,9 +166,9 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverNewTouchPoints(QQuickPointerTouchEvent *, QSet *); + bool deliverPressEvent(QQuickPointerEvent *, QSet *); bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered); - bool deliverMatchingPointsToItem(QQuickItem *item, const QQuickPointerTouchEvent *event, QSet *filtered); + bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent); static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); -- cgit v1.2.3 From dbf131fa70dc6269349f3b8d1bedd05203a0c3e4 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 14 Jul 2016 16:13:05 +0200 Subject: QML: Add specialized binding for QObject* properties In it we can cache the meta-type for the specific QObject subclass of the property type. This reduces the number of calls to QMetaType. Change-Id: Ie3774395c971bc33af58a8290453b19631939ea8 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlbinding.cpp | 96 +++++++++++++++++++++++++++++++++++++-------- src/qml/qml/qqmlbinding_p.h | 2 +- 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 10d16a8a12..ffe1b4287d 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -51,6 +51,8 @@ #include #include #include +#include +#include #include #include @@ -59,7 +61,7 @@ QT_BEGIN_NAMESPACE QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContext *ctxt) { - QQmlBinding *b = newBinding(property); + QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); b->setNotifyOnValueChanged(true); b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt)); b->setScopeObject(obj); @@ -71,7 +73,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt) { - QQmlBinding *b = newBinding(property); + QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); if (ctxt && !ctxt->isValid()) return b; @@ -108,7 +110,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContextData *ctxt) { - QQmlBinding *b = newBinding(property); + QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); b->setNotifyOnValueChanged(true); b->QQmlJavaScriptExpression::setContext(ctxt); @@ -123,7 +125,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString QQmlContextData *ctxt, const QString &url, quint16 lineNumber, quint16 columnNumber) { - QQmlBinding *b = newBinding(property); + QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); Q_UNUSED(columnNumber); b->setNotifyOnValueChanged(true); @@ -137,7 +139,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt) { - QQmlBinding *b = newBinding(property); + QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); b->setNotifyOnValueChanged(true); b->QQmlJavaScriptExpression::setContext(ctxt); @@ -204,18 +206,13 @@ protected: const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL { QQmlPropertyData pd = getPropertyData(); - - int idx = pd.coreIndex; - Q_ASSERT(idx != -1); - - int status = -1; - void *a[] = { &binding, 0, &status, &flags }; - QMetaObject::metacall(*m_target, QMetaObject::WriteProperty, idx, a); + pd.writeProperty(*m_target, &binding, flags); } }; -template -class GenericBinding: public QQmlBinding +// For any target that's not a binding, we have a common doUpdate. However, depending on the type +// of the target property, there is a specialized write method. +class QQmlNonbindingBinding: public QQmlBinding { protected: void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, @@ -252,9 +249,16 @@ protected: ep->dereferenceScarceResources(); } + virtual bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) = 0; +}; + +template +class GenericBinding: public QQmlNonbindingBinding +{ +protected: // Returns true if successful, false if an error description was set on expression Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, - QQmlPropertyData::WriteFlags flags) + QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL { QQmlPropertyData pd = getPropertyData(); int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded. @@ -572,8 +576,68 @@ Q_ALWAYS_INLINE int QQmlBinding::getPropertyCoreIndex() const } } -QQmlBinding *QQmlBinding::newBinding(const QQmlPropertyData *property) +class QObjectPointerBinding: public QQmlNonbindingBinding { + QQmlMetaObject targetMetaObject; + +public: + QObjectPointerBinding(QQmlEnginePrivate *engine, int propertyType) + : targetMetaObject(QQmlPropertyPrivate::rawMetaObjectForType(engine, propertyType)) + {} + +protected: + Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, + QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL + { + QQmlPropertyData pd = getPropertyData(); + if (Q_UNLIKELY(isUndefined || pd.isValueTypeVirtual())) + return slowWrite(pd, result, isUndefined, flags); + + // Check if the result is a QObject: + QObject *resultObject = nullptr; + QQmlMetaObject resultMo; + if (result.isNull()) { + // Special case: we can always write a nullptr. Don't bother checking anything else. + return pd.writeProperty(targetObject(), &resultObject, flags); + } else if (auto wrapper = result.as()) { + resultObject = wrapper->object(); + if (!resultObject) + return pd.writeProperty(targetObject(), &resultObject, flags); + if (QQmlData *ddata = QQmlData::get(resultObject, false)) + resultMo = ddata->propertyCache; + if (resultMo.isNull()) { + resultMo = resultObject->metaObject(); + } + } else if (auto variant = result.as()) { + QVariant value = variant->d()->data; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()); + resultMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, value.userType()); + if (resultMo.isNull()) + return slowWrite(pd, result, isUndefined, flags); + resultObject = *static_cast(value.constData()); + } else { + return slowWrite(pd, result, isUndefined, flags); + } + + // Compare & set: + if (QQmlMetaObject::canConvert(resultMo, targetMetaObject)) { + return pd.writeProperty(targetObject(), &resultObject, flags); + } else if (!resultObject && QQmlMetaObject::canConvert(targetMetaObject, resultMo)) { + // In the case of a null QObject, we assign the null if there is + // any change that the null variant type could be up or down cast to + // the property type. + return pd.writeProperty(targetObject(), &resultObject, flags); + } else { + return slowWrite(pd, result, isUndefined, flags); + } + } +}; + +QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property) +{ + if (property && property->isQObject()) + return new QObjectPointerBinding(engine, property->propType); + const int type = (property && property->isFullyResolved()) ? property->propType : QMetaType::UnknownType; if (type == qMetaTypeId()) { diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 0c797d7df2..67fbeb693e 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -119,7 +119,7 @@ private: inline bool enabledFlag() const; inline void setEnabledFlag(bool); - static QQmlBinding *newBinding(const QQmlPropertyData *property); + static QQmlBinding *newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property); }; bool QQmlBinding::updatingFlag() const -- cgit v1.2.3 From 576806252f302002f1cc5ac944b71f95602a2ed0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 2 Aug 2016 15:14:46 +0200 Subject: Remove const of device getter in QQuickPointerEvent Makes it possible to assign QQuickPointerDevice from the PointerEvent. Change-Id: Ica5182175eea210f5fe090807ed54467be11b031 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index b28c68c090..9f9b4083f6 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -345,7 +345,7 @@ public: virtual ~QQuickPointerEvent(); public: // property accessors - const QQuickPointerDevice *device() const { return m_device; } + QQuickPointerDevice *device() const { return m_device; } Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; } Qt::MouseButton button() const { return m_button; } Qt::MouseButtons buttons() const { return m_pressedButtons; } @@ -375,7 +375,7 @@ public: // helpers for C++ only (during event delivery) protected: - const QQuickPointerDevice *m_device; + QQuickPointerDevice *m_device; QInputEvent *m_event; // original event as received by QQuickWindow Qt::MouseButton m_button; Qt::MouseButtons m_pressedButtons; -- cgit v1.2.3 From 5b620ed68909c63f3a5a892577a07f66d35c2923 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 1 Aug 2016 16:38:56 +0200 Subject: Add QT_NO_QML_DEBUGGER to MODULE_DEFINES on -no-qml-debug If -no-qml-debug is set when building QtQml, we have to add QT_NO_QML_DEBUGGER when building application code, because this build option modifies the headers and linking an application built with QML debugging against a Qt built without is crashy. Change-Id: Ib150810f40deb98dcf398e3998401358a25155d9 Reviewed-by: Simon Hausmann Reviewed-by: Oswald Buddenhagen --- src/qml/debugger/debugger.pri | 1 + src/qml/jsruntime/jsruntime.pri | 6 +----- src/quick/util/util.pri | 6 +----- src/quickwidgets/quickwidgets.pro | 1 - 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri index 74dcb250a8..da1ab867d4 100644 --- a/src/qml/debugger/debugger.pri +++ b/src/qml/debugger/debugger.pri @@ -1,5 +1,6 @@ contains(QT_CONFIG, no-qml-debug) { DEFINES += QT_NO_QML_DEBUGGER + MODULE_DEFINES += QT_NO_QML_DEBUGGER } else { HEADERS += \ $$PWD/qqmldebugpluginmanager_p.h \ diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index cf434ee2ed..e72b06359e 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -43,11 +43,7 @@ SOURCES += \ $$PWD/qv4typedarray.cpp \ $$PWD/qv4dataview.cpp -!contains(QT_CONFIG, no-qml-debug) { - SOURCES += $$PWD/qv4profiling.cpp -} else { - DEFINES += QT_NO_QML_DEBUGGER -} +!contains(QT_CONFIG, no-qml-debug): SOURCES += $$PWD/qv4profiling.cpp HEADERS += \ $$PWD/qv4global_p.h \ diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index 2c31f6feed..66792536d7 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -31,11 +31,7 @@ SOURCES += \ $$PWD/qquickshortcut.cpp \ $$PWD/qquickvalidator.cpp -contains(QT_CONFIG, no-qml-debug) { - DEFINES += QT_NO_QML_DEBUGGER -} else { - SOURCES += $$PWD/qquickprofiler.cpp -} +!contains(QT_CONFIG, no-qml-debug): SOURCES += $$PWD/qquickprofiler.cpp HEADERS += \ $$PWD/qquickapplication_p.h\ diff --git a/src/quickwidgets/quickwidgets.pro b/src/quickwidgets/quickwidgets.pro index 0e45e63307..87409e31c5 100644 --- a/src/quickwidgets/quickwidgets.pro +++ b/src/quickwidgets/quickwidgets.pro @@ -3,7 +3,6 @@ TARGET = QtQuickWidgets QT = core-private gui-private qml-private quick-private widgets-private DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES -contains(QT_CONFIG, no-qml-debug): DEFINES += QT_NO_QML_DEBUGGER HEADERS += \ qquickwidget.h \ -- cgit v1.2.3 From c25c95ba22ef843d09b22f9524a04585dc26f2bd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Jul 2016 20:39:58 +0200 Subject: Fix endianness in constant handling When running on a big-endian system, we need to convert the constant values into big-endian once and then it's possible to access them directly. Change-Id: I655bad7b7734e3b95e79e5f688f0b4041d0c41c4 Reviewed-by: Erik Verbruggen --- src/qml/compiler/qv4compileddata.cpp | 13 +++++++++++++ src/qml/compiler/qv4compileddata_p.h | 7 +++++-- src/qml/compiler/qv4compiler.cpp | 6 ++++++ src/qml/jsruntime/qv4context.cpp | 2 +- src/qml/jsruntime/qv4functionobject.cpp | 4 ++-- src/qml/jsruntime/qv4script.cpp | 2 +- src/qml/jsruntime/qv4vme_moth.cpp | 2 +- 7 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 121b5907db..b060d72448 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -161,6 +161,16 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) } } +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + Value *bigEndianConstants = new Value[data->constantTableSize]; + const LEUInt64 *littleEndianConstants = data->constants(); + for (uint i = 0; i < data->constantTableSize; ++i) + bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]); + constants = bigEndianConstants; +#else + constants = reinterpret_cast(data->constants()); +#endif + linkBackendToEngine(engine); if (data->indexOfRootFunction != -1) @@ -203,6 +213,9 @@ void CompilationUnit::unlink() runtimeClasses = 0; qDeleteAll(runtimeFunctions); runtimeFunctions.clear(); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + delete [] constants; +#endif } void CompilationUnit::markObjects(QV4::ExecutionEngine *e) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 519708f089..1f253e02fd 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -691,8 +691,8 @@ struct Unit const RegExp *regexpAt(int index) const { return reinterpret_cast(reinterpret_cast(this) + offsetToRegexpTable + index * sizeof(RegExp)); } - const QV4::Value *constants() const { - return reinterpret_cast(reinterpret_cast(this) + offsetToConstantTable); + const LEUInt64 *constants() const { + return reinterpret_cast(reinterpret_cast(this) + offsetToConstantTable); } const JSClassMember *jsClassAt(int idx, int *nMembers) const { @@ -853,6 +853,9 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QHash> namedObjectsPerComponentCache; IdentifierHash namedObjectsPerComponent(int componentObjectIndex); + // pointers either to data->constants() or little-endian memory copy. + const Value* constants; + void finalize(QQmlEnginePrivate *engine); int totalBindingsCount; // Number of bindings used in this type diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 924f2e15e2..50ade2c6e5 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -248,8 +248,14 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO CompiledData::RegExp *regexpTable = reinterpret_cast(dataPtr + unit->offsetToRegexpTable); memcpy(regexpTable, regexps.constData(), regexps.size() * sizeof(*regexpTable)); +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN ReturnedValue *constantTable = reinterpret_cast(dataPtr + unit->offsetToConstantTable); memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue)); +#else + CompiledData::LEUInt64 *constantTable = reinterpret_cast(dataPtr + unit->offsetToConstantTable); + for (int i = 0; i < constants.count(); ++i) + constantTable[i] = constants.at(i); +#endif { memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size()); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 60717c9491..1abaca3dd1 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -73,7 +73,7 @@ Heap::CallContext *ExecutionContext::newCallContext(const FunctionObject *functi c->compilationUnit = function->function()->compilationUnit; c->lookups = c->compilationUnit->runtimeLookups; - c->constantTable = c->compilationUnit->data->constants(); + c->constantTable = c->compilationUnit->constants; c->locals = (Value *)((quintptr(c + 1) + 7) & ~7); const CompiledData::Function *compiledFunction = function->function()->compiledFunction; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 805087e389..276a069a77 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -535,7 +535,7 @@ void SimpleScriptFunction::construct(const Managed *that, Scope &scope, CallData ctx.function = f->d(); ctx.compilationUnit = f->function()->compilationUnit; ctx.lookups = ctx.compilationUnit->runtimeLookups; - ctx.constantTable = ctx.compilationUnit->data->constants(); + ctx.constantTable = ctx.compilationUnit->constants; ctx.outer = f->scope(); ctx.locals = scope.alloc(f->varCount()); for (int i = callData->argc; i < (int)f->formalParameterCount(); ++i) @@ -573,7 +573,7 @@ void SimpleScriptFunction::call(const Managed *that, Scope &scope, CallData *cal ctx.function = f->d(); ctx.compilationUnit = f->function()->compilationUnit; ctx.lookups = ctx.compilationUnit->runtimeLookups; - ctx.constantTable = ctx.compilationUnit->data->constants(); + ctx.constantTable = ctx.compilationUnit->constants; ctx.outer = f->scope(); ctx.locals = scope.alloc(f->varCount()); for (int i = callData->argc; i < (int)f->formalParameterCount(); ++i) diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 46adaf7e79..e3475e5cd6 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -222,7 +222,7 @@ ReturnedValue Script::run() ContextStateSaver stateSaver(valueScope, scope); scope->d()->strictMode = vmFunction->isStrict(); scope->d()->lookups = vmFunction->compilationUnit->runtimeLookups; - scope->d()->constantTable = vmFunction->compilationUnit->data->constants(); + scope->d()->constantTable = vmFunction->compilationUnit->constants; scope->d()->compilationUnit = vmFunction->compilationUnit; return Q_V4_PROFILE(engine, vmFunction); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index b6dc7716ba..420abd1458 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -402,7 +402,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code QV4::Value **scopes = static_cast(alloca(sizeof(QV4::Value *)*(2 + 2*scopeDepth))); { - scopes[0] = const_cast(context->d()->compilationUnit->data->constants()); + scopes[0] = const_cast(context->d()->compilationUnit->constants); // stack gets setup in push instruction scopes[1] = 0; QV4::Heap::ExecutionContext *scope = context->d(); -- cgit v1.2.3 From fa50403ab39a9f4549bf0cc56d07a8717eb177ae Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 2 Aug 2016 12:49:06 +0200 Subject: QML debugger: Fix test Use delete[] on a new[]-ed pointer. Change-Id: Ic5721851bdcec284cdd631b81d2cc96b7a1748fc Reviewed-by: Simon Hausmann --- .../debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index 4568d25dc1..40e19d375d 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -1223,16 +1223,15 @@ void tst_QQmlEngineDebugService::queryObjectTree() int main(int argc, char *argv[]) { int _argc = argc + 1; - char **_argv = new char*[_argc]; + QScopedArrayPointer_argv(new char*[_argc]); for (int i = 0; i < argc; ++i) _argv[i] = argv[i]; char arg[] = "-qmljsdebugger=port:3768,services:QmlDebugger"; _argv[_argc - 1] = arg; - QGuiApplication app(_argc, _argv); + QGuiApplication app(_argc, _argv.data()); tst_QQmlEngineDebugService tc; - return QTest::qExec(&tc, _argc, _argv); - delete _argv; + return QTest::qExec(&tc, _argc, _argv.data()); } #include "tst_qqmlenginedebugservice.moc" -- cgit v1.2.3 From 5c2877b14f225e12ad6e4227e85e60dd217c5dba Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 3 Aug 2016 13:51:10 +0200 Subject: deprecate QQuickWindow::sendEvent and minimize the internal usages It seems to be necessary only to handle parent-filtering cases. Meanwhile, filtering out certain events while sending others through via QCoreApplication::sendEvent() also seems questionable, so let's see if anything breaks if we allow all events through. And I can't think of a use for it in application code, so let's deprecate it and hope that we can remove it in Qt 6. [ChangeLog][QtQuick][QQuickWindow] QQuickWindow::sendEvent() is deprecated. Use QCoreApplication::sendEvent() directly instead. Change-Id: Ide8004bcb0003f14cf176d6d7e80a9af8f442813 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickitem.cpp | 2 +- src/quick/items/qquickwindow.cpp | 73 +++++++++++++--------------------------- 2 files changed, 25 insertions(+), 50 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 07a415eecd..695454af38 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1426,7 +1426,7 @@ void QQuickKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post) d->inIM = true; for (QQuickItem *targetItem : qAsConst(d->targets)) { if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) { - d->item->window()->sendEvent(targetItem, event); + QCoreApplication::sendEvent(targetItem, event); if (event->isAccepted()) { d->imeItem = targetItem; d->inIM = false; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 3f5e85be5e..df6e8646bb 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -756,8 +756,10 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) } if (oldGrabber) { - QEvent ev(QEvent::UngrabMouse); - q->sendEvent(oldGrabber, &ev); + QEvent e(QEvent::UngrabMouse); + QSet hasFiltered; + if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) + oldGrabber->mouseUngrabEvent(); } } @@ -950,17 +952,16 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q } // Now that all the state is changed, emit signals & events - // We must do this last, as this process may result in further changes to - // focus. + // We must do this last, as this process may result in further changes to focus. if (oldActiveFocusItem) { QFocusEvent event(QEvent::FocusOut, reason); - q->sendEvent(oldActiveFocusItem, &event); + QCoreApplication::sendEvent(oldActiveFocusItem, &event); } // Make sure that the FocusOut didn't result in another focus change. if (sendFocusIn && activeFocusItem == newActiveFocusItem) { QFocusEvent event(QEvent::FocusIn, reason); - q->sendEvent(newActiveFocusItem, &event); + QCoreApplication::sendEvent(newActiveFocusItem, &event); } if (activeFocusItem != currentActiveFocusItem) @@ -1047,13 +1048,13 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, // focus. if (oldActiveFocusItem) { QFocusEvent event(QEvent::FocusOut, reason); - q->sendEvent(oldActiveFocusItem, &event); + QCoreApplication::sendEvent(oldActiveFocusItem, &event); } // Make sure that the FocusOut didn't result in another focus change. if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) { QFocusEvent event(QEvent::FocusIn, reason); - q->sendEvent(newActiveFocusItem, &event); + QCoreApplication::sendEvent(newActiveFocusItem, &event); } if (activeFocusItem != currentActiveFocusItem) @@ -1570,7 +1571,7 @@ bool QQuickWindow::event(QEvent *e) #endif case QEvent::ShortcutOverride: if (d->activeFocusItem) - sendEvent(d->activeFocusItem, static_cast(e)); + QCoreApplication::sendEvent(d->activeFocusItem, e); return true; default: break; @@ -1657,7 +1658,6 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool accepted) { - Q_Q(QQuickWindow); const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform(); //create copy of event @@ -1669,7 +1669,7 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, return true; } - q->sendEvent(item, &hoverEvent); + QCoreApplication::sendEvent(item, &hoverEvent); return hoverEvent.isAccepted(); } @@ -1753,7 +1753,6 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce #ifndef QT_NO_WHEELEVENT bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event) { - Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { @@ -1777,7 +1776,7 @@ bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(), event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted()); wheel.accept(); - q->sendEvent(item, &wheel); + QCoreApplication::sendEvent(item, &wheel); if (wheel.isAccepted()) { event->accept(); return true; @@ -2386,7 +2385,6 @@ QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, #ifndef QT_NO_DRAGANDDROP void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event) { - Q_Q(QQuickWindow); grabber->resetTarget(); QQuickDragGrabber::iterator grabItem = grabber->begin(); if (grabItem != grabber->end()) { @@ -2402,7 +2400,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e e->mouseButtons(), e->keyboardModifiers()); QQuickDropEventEx::copyActions(&translatedEvent, *e); - q->sendEvent(**grabItem, &translatedEvent); + QCoreApplication::sendEvent(**grabItem, &translatedEvent); e->setAccepted(translatedEvent.isAccepted()); e->setDropAction(translatedEvent.dropAction()); grabber->setTarget(**grabItem); @@ -2411,7 +2409,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave. QDragLeaveEvent leaveEvent; for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) - q->sendEvent(**grabItem, &leaveEvent); + QCoreApplication::sendEvent(**grabItem, &leaveEvent); return; } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { QDragMoveEvent *moveEvent = static_cast(event); @@ -2427,18 +2425,18 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e moveEvent->mouseButtons(), moveEvent->keyboardModifiers()); QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent); - q->sendEvent(**grabItem, &translatedEvent); + QCoreApplication::sendEvent(**grabItem, &translatedEvent); ++grabItem; } else { QDragLeaveEvent leaveEvent; - q->sendEvent(**grabItem, &leaveEvent); + QCoreApplication::sendEvent(**grabItem, &leaveEvent); grabItem = grabber->release(grabItem); } } return; } else { QDragLeaveEvent leaveEvent; - q->sendEvent(**grabItem, &leaveEvent); + QCoreApplication::sendEvent(**grabItem, &leaveEvent); } } } @@ -2457,7 +2455,6 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event) { - Q_Q(QQuickWindow); bool accepted = false; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled) @@ -2492,7 +2489,7 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte event->keyboardModifiers(), event->type()); QQuickDropEventEx::copyActions(&translatedEvent, *event); - q->sendEvent(item, &translatedEvent); + QCoreApplication::sendEvent(item, &translatedEvent); if (event->type() == QEvent::DragEnter) { if (translatedEvent.isAccepted()) { grabber->grab(item); @@ -2777,8 +2774,13 @@ void QQuickWindowPrivate::contextCreationFailureMessage(const QSurfaceFormat &fo /*! Propagates an event \a e to a QQuickItem \a item on the window. + Use \l QCoreApplication::sendEvent() directly instead. + The return value is currently not used. + + \deprecated */ +// ### Qt6: remove bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) { Q_D(QQuickWindow); @@ -2800,9 +2802,6 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) QCoreApplication::sendEvent(item, e); } break; - case QEvent::ShortcutOverride: - QCoreApplication::sendEvent(item, e); - break; case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: @@ -2816,31 +2815,6 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) } } break; - case QEvent::UngrabMouse: { - QSet hasFiltered; - if (!d->sendFilteredMouseEvent(item->parentItem(), item, e, &hasFiltered)) { - e->accept(); - item->mouseUngrabEvent(); - } - } - break; -#ifndef QT_NO_WHEELEVENT - case QEvent::Wheel: -#endif -#ifndef QT_NO_DRAGANDDROP - case QEvent::DragEnter: - case QEvent::DragMove: - case QEvent::DragLeave: - case QEvent::Drop: -#endif - case QEvent::FocusIn: - case QEvent::FocusOut: - case QEvent::HoverEnter: - case QEvent::HoverLeave: - case QEvent::HoverMove: - case QEvent::TouchCancel: - QCoreApplication::sendEvent(item, e); - break; case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: { @@ -2851,6 +2825,7 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) } break; default: + QCoreApplication::sendEvent(item, e); break; } -- cgit v1.2.3 From c8978bd582eabf8deb139e2606317b0df0aa7ec2 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Tue, 2 Aug 2016 17:20:42 +0200 Subject: QQuickDefaultClipNode: Set clipRect before marking geometry dirty The ordering here is important because the software adaptation uses the QSGClipNode's clipRect property instead of the geometry to update the clipRect, and with the previous code the clip node would not be correct for the next frame. Change-Id: I43147aaabd65f22820f01afb326975bb62ea95cc Reviewed-by: Laszlo Agocs --- src/quick/items/qquickclipnode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickclipnode.cpp b/src/quick/items/qquickclipnode.cpp index 1114686a9a..7c7fee4a42 100644 --- a/src/quick/items/qquickclipnode.cpp +++ b/src/quick/items/qquickclipnode.cpp @@ -113,7 +113,7 @@ void QQuickDefaultClipNode::updateGeometry() } } - markDirty(DirtyGeometry); setClipRect(m_rect); + markDirty(DirtyGeometry); } -- cgit v1.2.3 From 5d8f6b49d24df83af40727260c64c6d0399da0b0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 20 Jul 2016 15:24:37 +0200 Subject: Reduce the amount of malloc calls when instantiating objects For regular Qt types we currently perform three mallocs, one for the public class, one for the private and one for the declarative data. The latter we can fold quite easily into the malloc of the public class. The plan for the private is a bigger separate one ;-) Change-Id: I69973652ba70a4a238b491dff3d47a9db9a226f1 Reviewed-by: Erik Verbruggen --- src/qml/qml/qqmlengine.cpp | 2 ++ src/qml/qml/qqmlobjectcreator.cpp | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index e4e7530af7..5cfb546718 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1713,6 +1713,8 @@ void QQmlData::destroyed(QObject *object) if (ownMemory) delete this; + else + this->~QQmlData(); } DEFINE_BOOL_CONFIG_OPTION(parentTest, QML_PARENT_TEST); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 3000f3e695..b354104b6e 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1039,12 +1039,20 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (type) { Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( compilationUnit, obj, type->qmlTypeName(), context->url())); - instance = type->create(); + + void *ddataMemory = 0; + type->create(&instance, &ddataMemory, sizeof(QQmlData)); if (!instance) { recordError(obj->location, tr("Unable to create object of type %1").arg(stringAt(obj->inheritedTypeNameIndex))); return 0; } + { + QQmlData *ddata = new (ddataMemory) QQmlData; + ddata->ownMemory = false; + QObjectPrivate::get(instance)->declarativeData = ddata; + } + const int parserStatusCast = type->parserStatusCast(); if (parserStatusCast != -1) parserStatus = reinterpret_cast(reinterpret_cast(instance) + parserStatusCast); -- cgit v1.2.3 From 0f4b84bc9db9566cf6220b3842852db4e05c7e5f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 3 Aug 2016 15:15:51 +0200 Subject: Fix build on QNX/Windows/MinGW Oddly the include order is different and we need to include the header for the class we use. Change-Id: If8a7485b8b728b1d3565023f37234912a0e0feea Reviewed-by: Liang Qi --- src/qml/compiler/qv4compileddata.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index b060d72448..5948992f67 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -58,6 +58,7 @@ #endif #include #include +#include #include -- cgit v1.2.3 From 8cd1736055f4483e2f12c04822501d2e7d4d8d63 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Wed, 3 Aug 2016 13:21:39 +0200 Subject: QQmlIRBuilder: Initialize variable gcc on Centos 7 struggles to understand that this variable is always used when initialized, so give it a helping hand. Change-Id: I11d2e9fdf23ae9173fa9205fbe0615158a2460ad Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 31250d0d57..2c164abd6f 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -819,7 +819,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) const QStringRef &name = node->name; bool typeFound = false; - QV4::CompiledData::Property::Type type; + QV4::CompiledData::Property::Type type = QV4::CompiledData::Property::Var; for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { const TypeNameToType *t = propTypeNameToTypes + ii; -- cgit v1.2.3 From ecf365c0b2ceeda97e42e4bc408964a9588cb6f2 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 2 Aug 2016 12:51:31 +0200 Subject: QSG: Make example namespace-aware Change-Id: Ic2c101d49f4140a85f2e9f79c8df78e38a4404f5 Reviewed-by: Simon Hausmann --- examples/quick/scenegraph/rendernode/openglrenderer.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.h b/examples/quick/scenegraph/rendernode/openglrenderer.h index 1ee6350218..92cc2bc72b 100644 --- a/examples/quick/scenegraph/rendernode/openglrenderer.h +++ b/examples/quick/scenegraph/rendernode/openglrenderer.h @@ -45,6 +45,8 @@ #ifndef QT_NO_OPENGL +QT_BEGIN_NAMESPACE + class QQuickItem; class QOpenGLShaderProgram; class QOpenGLBuffer; @@ -71,6 +73,8 @@ private: QOpenGLBuffer *m_vbo = nullptr; }; +QT_END_NAMESPACE + #endif // QT_NO_OPENGL #endif -- cgit v1.2.3 From 53cc9a9df9f94912df5afe64f235b0525e33603a Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 3 Aug 2016 13:41:32 +0200 Subject: Remove touch handling from QQuickWindow::sendEvent This is dead code which doesn't fulfill any purpose. It is actively harmful in that it doesn't do what it promises: it never sends the event but only does the filtering. Change-Id: If2eda1bcd1a219dbdb76e777d52146814cc436be Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index df6e8646bb..0a9807c128 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2815,15 +2815,6 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) } } break; - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: { - QSet hasFiltered; - QTouchEvent *ev = static_cast(e); - qCDebug(DBG_TOUCH) << " - sendEvent for " << ev << " to " << item->parentItem() << " and " << item; - d->sendFilteredTouchEvent(item->parentItem(), item, ev, &hasFiltered); - } - break; default: QCoreApplication::sendEvent(item, e); break; -- cgit v1.2.3 From 965f951988e437dddb3f2d048877d3e68777db96 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 2 Aug 2016 15:18:19 +0200 Subject: Pass pointer event into sendFilteredTouchEvent Change-Id: Id8709b45af135df5f16558c4b611409bc134ea63 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 4 +- src/quick/items/qquickevents_p_p.h | 2 +- src/quick/items/qquickwindow.cpp | 88 ++++---------------------------------- src/quick/items/qquickwindow_p.h | 5 +-- 4 files changed, 12 insertions(+), 87 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index cfc35efbb4..69df946d6f 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -791,7 +791,7 @@ const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int point Returns a nullptr if all points are stationary or there are no points inside the item. */ -QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item) const +QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const { QList touchPoints; Qt::TouchPointStates eventStates; @@ -806,7 +806,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item) const continue; bool isGrabber = p->grabber() == item; bool isPressInside = p->state() == Qt::TouchPointPressed && item->contains(item->mapFromScene(p->scenePos())); - if (!(isGrabber || isPressInside)) + if (!(isGrabber || isPressInside || isFiltering)) continue; const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId()); diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 9f9b4083f6..3bc28ff95d 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -429,7 +429,7 @@ public: void clearGrabbers() const override; QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; - QTouchEvent *touchEventForItem(QQuickItem *item) const; + QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const; QTouchEvent *asTouchEvent() const; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 0a9807c128..7d298bda61 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -813,19 +813,6 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to } } -void QQuickWindowPrivate::transformTouchPoints(QList &touchPoints, const QTransform &transform) -{ - QMatrix4x4 transformMatrix(transform); - for (int i=0; iparentItem(), item, event->asTouchEvent(), hasFiltered)) { + if (sendFilteredTouchEvent(item->parentItem(), item, event, hasFiltered)) { // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; @@ -2321,67 +2308,6 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo return eventAccepted; } -// create touch event containing only points inside the target item -QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent) -{ - const QList &touchPoints = originalEvent.touchPoints(); - QList pointsInBounds; - // if all points are stationary, the list of points should be empty to signal a no-op - if (originalEvent.touchPointStates() != Qt::TouchPointStationary) { - for (int i = 0; i < touchPoints.count(); ++i) { - const QTouchEvent::TouchPoint &tp = touchPoints.at(i); - // Touch presses are relevant to the target item only if they occur inside its bounds. - // Touch updates and releases are relevant if they occur inside, or if we want to - // finish the sequence because the press occurred inside. - if (tp.state() == Qt::TouchPointPressed) { - QPointF p = target->mapFromScene(tp.scenePos()); - if (target->contains(p)) - pointsInBounds.append(tp); - } else { - pointsInBounds.append(tp); - } - } - transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->windowToItemTransform()); - } - - QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds); - touchEvent->setTarget(target); - return touchEvent; -} - -// copy a touch event's basic properties but give it new touch points -// TODO remove this variant and use QQuickPointerEvent/QQuickEventPoint -QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList &newPoints) -{ - Qt::TouchPointStates eventStates; - for (const QTouchEvent::TouchPoint &p : qAsConst(newPoints)) - eventStates |= p.state(); - // if all points have the same state, set the event type accordingly - QEvent::Type eventType = event.type(); - switch (eventStates) { - case Qt::TouchPointPressed: - eventType = QEvent::TouchBegin; - break; - case Qt::TouchPointReleased: - eventType = QEvent::TouchEnd; - break; - default: - eventType = QEvent::TouchUpdate; - break; - } - - QTouchEvent *touchEvent = new QTouchEvent(eventType); - touchEvent->setWindow(event.window()); - touchEvent->setTarget(event.target()); - touchEvent->setDevice(event.device()); - touchEvent->setModifiers(event.modifiers()); - touchEvent->setTouchPoints(newPoints); - touchEvent->setTouchPointStates(eventStates); - touchEvent->setTimestamp(event.timestamp()); - touchEvent->accept(); - return touchEvent; -} - #ifndef QT_NO_DRAGANDDROP void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event) { @@ -2553,7 +2479,7 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF #endif // Child event filtering is legacy stuff, so here we use QTouchEvent -bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *hasFiltered) +bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet *hasFiltered) { Q_Q(QQuickWindow); @@ -2565,8 +2491,8 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { hasFiltered->insert(target); - QScopedPointer targetEvent(touchEventForItem(target, *event)); - if (!targetEvent->touchPoints().isEmpty()) { + QScopedPointer targetEvent(event->touchEventForItem(target, true)); + if (targetEvent) { if (target->childMouseEventFilter(item, targetEvent.data())) { qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target; QVector touchIds; @@ -2610,13 +2536,15 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId if ((touchMouseIdCandidates.contains(tp.id()) && touchMouseId == -1) || touchMouseId == tp.id()) { // targetEvent is already transformed wrt local position, velocity, etc. - QScopedPointer mouseEvent(touchToMouseEvent(t, tp, event, item, false)); + + // FIXME: remove asTouchEvent!!! + QScopedPointer mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false)); if (target->childMouseEventFilter(item, mouseEvent.data())) { qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target; if (t != QEvent::MouseButtonRelease) { qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; touchMouseId = tp.id(); - touchMouseDevice = QQuickPointerDevice::touchDevice(event->device()); + touchMouseDevice = event->device(); touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target); target->grabMouse(); } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 11cadb4739..9b380a66f8 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -143,7 +143,6 @@ public: void setMouseGrabber(QQuickItem *grabber); void grabTouchPoints(QQuickItem *grabber, const QVector &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); - static void transformTouchPoints(QList &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet *); @@ -169,9 +168,7 @@ public: bool deliverPressEvent(QQuickPointerEvent *, QSet *); bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered); bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet *filtered); - static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent); - static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList &newPoints); - bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet *filtered); + bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet *filtered); QVector pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; QVector mergePointerTargets(const QVector &list1, const QVector &list2) const; -- cgit v1.2.3 From 9baa491a068d6cc3a0276949b538ae8f415ae1ad Mon Sep 17 00:00:00 2001 From: David Faure Date: Wed, 3 Aug 2016 20:01:58 +0200 Subject: autotests: fix finding data files when not in CWD These tests now can find their data files even when running from a different directory than the source directory, and they no longer misuse QUrl::fromLocalFile for a relative URL (resolved with QUrl::resolved). Change-Id: If18afd2e29571cca2a4c820eda6b9f6713e08a92 Reviewed-by: Simon Hausmann --- .../tst_qquickframebufferobject.cpp | 4 +- .../qquickopenglinfo/tst_qquickopenglinfo.cpp | 2 +- tests/auto/quick/scenegraph/tst_scenegraph.cpp | 46 ++++++++++------------ 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp index ada372cfdd..a3be728f5e 100644 --- a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp +++ b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp @@ -185,7 +185,7 @@ void tst_QQuickFramebufferObject::testThatStuffWorks() qmlRegisterType("FBOItem", 1, 0, "FBOItem"); QQuickView view; - view.setSource(QUrl::fromLocalFile("data/testStuff.qml")); + view.setSource(testFileUrl("testStuff.qml")); FBOItem *item = view.rootObject()->findChild("fbo"); @@ -229,7 +229,7 @@ void tst_QQuickFramebufferObject::testInvalidate() qmlRegisterType("FBOItem", 1, 0, "FBOItem"); QQuickView view; - view.setSource(QUrl::fromLocalFile("data/testStuff.qml")); + view.setSource(testFileUrl("testStuff.qml")); FBOItem *item = view.rootObject()->findChild("fbo"); item->setTextureFollowsItemSize(false); diff --git a/tests/auto/quick/qquickopenglinfo/tst_qquickopenglinfo.cpp b/tests/auto/quick/qquickopenglinfo/tst_qquickopenglinfo.cpp index 29bcce802f..7446eaec0f 100644 --- a/tests/auto/quick/qquickopenglinfo/tst_qquickopenglinfo.cpp +++ b/tests/auto/quick/qquickopenglinfo/tst_qquickopenglinfo.cpp @@ -53,7 +53,7 @@ private slots: void tst_QQuickOpenGLInfo::testProperties() { QQuickView view; - view.setSource(QUrl::fromLocalFile("data/basic.qml")); + view.setSource(testFileUrl("basic.qml")); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp index 80672e234e..b2093076e8 100644 --- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp +++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp @@ -44,6 +44,7 @@ #include #include +#include "../../shared/util.h" class PerPixelRect : public QQuickItem { @@ -89,7 +90,7 @@ private: QColor m_color; }; -class tst_SceneGraph : public QObject +class tst_SceneGraph : public QQmlDataTest { Q_OBJECT @@ -109,6 +110,7 @@ private slots: private: bool m_brokenMipmapSupport; + QQuickView *createView(const QString &file, QWindow *parent = 0, int x = -1, int y = -1, int w = -1, int h = -1); }; template class ScopedList : public QList { @@ -120,6 +122,8 @@ void tst_SceneGraph::initTestCase() { qmlRegisterType("SceneGraphTest", 1, 0, "PerPixelRect"); + QQmlDataTest::initTestCase(); + QSGRenderLoop *loop = QSGRenderLoop::instance(); qDebug() << "RenderLoop: " << loop; @@ -157,26 +161,16 @@ void tst_SceneGraph::initTestCase() context.doneCurrent(); } -QQuickView *createView(const QString &file, QWindow *parent = 0, int x = -1, int y = -1, int w = -1, int h = -1) +QQuickView *tst_SceneGraph::createView(const QString &file, QWindow *parent, int x, int y, int w, int h) { QQuickView *view = new QQuickView(parent); - view->setSource(QUrl::fromLocalFile("data/" + file)); + view->setSource(testFileUrl(file)); if (x >= 0 && y >= 0) view->setPosition(x, y); if (w >= 0 && h >= 0) view->resize(w, h); view->show(); return view; } -QImage showAndGrab(const QString &file, int w, int h) -{ - QQuickView view; - view.setSource(QUrl::fromLocalFile(QStringLiteral("data/") + file)); - if (w >= 0 && h >= 0) - view.resize(w, h); - view.create(); - return view.grabWindow(); -} - // Assumes the images are opaque white... bool containsSomethingOtherThanWhite(const QImage &image) { @@ -410,17 +404,17 @@ void tst_SceneGraph::render_data() QTest::addColumn >("finalStage"); QList files; - files << "data/render_DrawSets.qml" - << "data/render_Overlap.qml" - << "data/render_MovingOverlap.qml" - << "data/render_BreakOpacityBatch.qml" - << "data/render_OutOfFloatRange.qml" - << "data/render_StackingOrder.qml" - << "data/render_ImageFiltering.qml" - << "data/render_bug37422.qml" - << "data/render_OpacityThroughBatchRoot.qml"; + files << "render_DrawSets.qml" + << "render_Overlap.qml" + << "render_MovingOverlap.qml" + << "render_BreakOpacityBatch.qml" + << "render_OutOfFloatRange.qml" + << "render_StackingOrder.qml" + << "render_ImageFiltering.qml" + << "render_bug37422.qml" + << "render_OpacityThroughBatchRoot.qml"; if (!m_brokenMipmapSupport) - files << "data/render_Mipmap.qml"; + files << "render_Mipmap.qml"; QRegExp sampleCount("#samples: *(\\d+)"); // X:int Y:int R:float G:float B:float Error:float @@ -428,7 +422,7 @@ void tst_SceneGraph::render_data() QRegExp finalSamples("#final: *(\\d+) *(\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+)"); foreach (QString fileName, files) { - QFile file(fileName); + QFile file(testFile(fileName)); if (!file.open(QFile::ReadOnly)) { qFatal("render_data: QFile::open failed! file=%s, error=%s", qPrintable(fileName), qPrintable(file.errorString())); @@ -471,7 +465,7 @@ void tst_SceneGraph::render() QQuickView view; view.rootContext()->setContextProperty("suite", &suite); - view.setSource(QUrl::fromLocalFile(file)); + view.setSource(testFileUrl(file)); view.setResizeMode(QQuickView::SizeViewToRootObject); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); @@ -519,7 +513,7 @@ void tst_SceneGraph::hideWithOtherContext() { QQuickView view; - view.setSource(QUrl::fromLocalFile("data/simple.qml")); + view.setSource(testFileUrl("simple.qml")); view.setResizeMode(QQuickView::SizeViewToRootObject); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); -- cgit v1.2.3 From 580f86b8a0e43656805180bc36defb7cf1f76826 Mon Sep 17 00:00:00 2001 From: David Faure Date: Mon, 1 Aug 2016 23:44:34 +0200 Subject: QQmlComponent: fix handling of relative paths Calling QUrl::fromLocalFile() on a relative path leads to a non-relative URL, as per the definition of QUrl::isRelative(). Change-Id: Ibaa9ecac56c6a14f6e41c5cf5250d7bbafed9837 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlcomponent.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 9d56ea50de..9ea38fe528 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -55,6 +55,7 @@ #include #include +#include #include #include #include @@ -543,7 +544,8 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, { Q_D(QQmlComponent); d->engine = engine; - d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName))); + const QUrl url = QDir::isAbsolutePath(fileName) ? QUrl::fromLocalFile(fileName) : d->engine->baseUrl().resolved(QUrl(fileName)); + d->loadUrl(url); } /*! @@ -559,7 +561,8 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, { Q_D(QQmlComponent); d->engine = engine; - d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)), mode); + const QUrl url = QDir::isAbsolutePath(fileName) ? QUrl::fromLocalFile(fileName) : d->engine->baseUrl().resolved(QUrl(fileName)); + d->loadUrl(url, mode); } /*! -- cgit v1.2.3 From 9cf6aea69becee1bc7cb0d41d6dca685f3c7b8c0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 3 Aug 2016 19:25:46 +0200 Subject: Remove outdated comment Change-Id: I538915e2adb6758583c736be9147c27fd5130244 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 7d298bda61..9c42d77cde 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2478,7 +2478,6 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF } #endif -// Child event filtering is legacy stuff, so here we use QTouchEvent bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet *hasFiltered) { Q_Q(QQuickWindow); -- cgit v1.2.3 From bc8f81ba345c455dd9f6a1fdbb9b2007bb21f6b8 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 3 Aug 2016 18:47:13 +0200 Subject: Remove touchMouseIdCandidates The set was there to protect from assigning to touchMouseId twice. That seems rather redundant: either something can become the touchMouseId, because touchMouseId is -1, or well, it can't. I suspect this set was needed before, when pre-grabbing was still there. Change-Id: I56d2c86cd13a3f6ec129d27ff8d5f7edf35df7cb Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 12 +----------- src/quick/items/qquickwindow_p.h | 2 -- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 9c42d77cde..022c930c28 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -666,8 +666,6 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *eve } } - // The event was accepted, we are done. - touchMouseIdCandidates.clear(); return true; } // The event was not accepted but touchMouseId was set. @@ -2177,7 +2175,6 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) touchMouseId = -1; touchMouseDevice = nullptr; } - touchMouseIdCandidates.remove(id); } else { allReleased = false; } @@ -2515,12 +2512,6 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem switch (tp.state()) { case Qt::TouchPointPressed: t = QEvent::MouseButtonPress; - if (touchMouseId == -1) { - // We don't want to later filter touches as a mouse event if they were pressed - // while a touchMouseId was already active. - // Remember this touch as a potential to become the touchMouseId. - touchMouseIdCandidates.insert(tp.id()); - } break; case Qt::TouchPointReleased: t = QEvent::MouseButtonRelease; @@ -2533,7 +2524,7 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem } // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId - if ((touchMouseIdCandidates.contains(tp.id()) && touchMouseId == -1) || touchMouseId == tp.id()) { + if (touchMouseId == -1 || touchMouseId == tp.id()) { // targetEvent is already transformed wrt local position, velocity, etc. // FIXME: remove asTouchEvent!!! @@ -2547,7 +2538,6 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target); target->grabMouse(); } - touchMouseIdCandidates.clear(); filtered = true; } // Only one event can be filtered as a mouse event. diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 9b380a66f8..fca0f78de5 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -265,8 +265,6 @@ public: QOpenGLVertexArrayObjectHelper *vaoHelper; - QSet touchMouseIdCandidates; - mutable QQuickWindowIncubationController *incubationController; static bool defaultAlphaBuffer; -- cgit v1.2.3 From ab7d780d3366f00f08406461ec8feac0dabf8175 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 3 Aug 2016 18:07:08 +0200 Subject: Assign QPointF() to lastMousePosition to reset Change-Id: I446f7311d2deddba102613b33e391acda48efac4 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 022c930c28..22db30107c 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1515,7 +1515,7 @@ bool QQuickWindow::event(QEvent *e) break; case QEvent::Leave: d->clearHover(); - d->lastMousePosition = QPoint(); + d->lastMousePosition = QPointF(); break; #ifndef QT_NO_DRAGANDDROP case QEvent::DragEnter: -- cgit v1.2.3 From 7fc3ff63249b2db4fddf02bce38b5cd3afb64a2e Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 3 Aug 2016 14:22:28 +0200 Subject: Move setting lastMousePosition out of QQuickWindowPrivate::translateTouchEvent This setting of lastMousePosition is highly suspicious and was nicely hidden. Make it visible, then a check can be added, for example it shouldn't be set if we have a real mouse at the same time as touch events, so a check for the right device is certainly in order. Also remove a redundant .toPoint() which would result in converting to QPoint which was then cast back to QPointF. Change-Id: I8333cf366ce623f3beb20f556f31b50904c0112d Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 22db30107c..024f326c3e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -824,9 +824,6 @@ void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent) touchPoint.setSceneRect(touchPoint.rect()); touchPoint.setStartScenePos(touchPoint.startPos()); touchPoint.setLastScenePos(touchPoint.lastPos()); - - if (i == 0) - lastMousePosition = touchPoint.pos().toPoint(); } touchEvent->setTouchPoints(touchPoints); } @@ -1929,6 +1926,9 @@ bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event) void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) { translateTouchEvent(event); + if (event->touchPoints().size()) + lastMousePosition = event->touchPoints().at(0).pos(); + qCDebug(DBG_TOUCH) << event; if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) { -- cgit v1.2.3 From 1cd7f9bd3769f6aab9cd7aba12536b1e3fa532be Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 3 Aug 2016 19:35:46 +0200 Subject: Add QQuickPointerEvent::timestamp Change-Id: Ib2c99d25e5922c5146b82d1e00c9e97b621eaa81 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 3bc28ff95d..0169c8e1e1 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -373,6 +373,8 @@ public: // helpers for C++ only (during event delivery) virtual QVector grabbers() const = 0; virtual void clearGrabbers() const = 0; + ulong timestamp() const { return m_event->timestamp(); } + protected: QQuickPointerDevice *m_device; -- cgit v1.2.3 From 97b2fc9d643538b55e9738322ea4239a3c9f7851 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 2 Aug 2016 17:00:47 +0200 Subject: Relax negativeYear EcmaScript test EcmaScript doesn't in fact require the date to be represented in english. Thus, only test for the year number to be contained in the date string. Change-Id: I5b89c14a833b317f259f4cd2855b3f24310a7d72 Reviewed-by: Simon Hausmann --- tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index aaa72d48cd..b3b31e1a73 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -7427,8 +7427,11 @@ void tst_qqmlecmascript::negativeYear() QVariant q; QMetaObject::invokeMethod(object, "check_negative_tostring", Q_RETURN_ARG(QVariant, q)); - // Strip the timezone. It should be irrelevant as the date was created with the same one. - QCOMPARE(q.toString().left(32), QStringLiteral("result: Sat Jan 1 00:00:00 -2001")); + + // Only check for the year. We hope that every language writes the year in arabic numerals and + // in relation to a specific dude's date of birth. We also hope that no language adds a "-2001" + // junk string somewhere in the middle. + QVERIFY(q.toString().indexOf(QStringLiteral("-2001")) != -1); QMetaObject::invokeMethod(object, "check_negative_toisostring", Q_RETURN_ARG(QVariant, q)); QCOMPARE(q.toString().left(16), QStringLiteral("result: -002000-")); -- cgit v1.2.3 From 5ef56c550cd01b7e7d1d0ecdb4a60ff9e9eb3e73 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 3 Aug 2016 15:32:42 +0200 Subject: QQuickEvents: de-inline a few more functions Touching this header results in rebuilding a lot of stuff, and these functions are likely to need further modification. Change-Id: Idc99255fc8e20a190c31b5e3d47dd005f4573434 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 26 ++++++++++++++++++++++++++ src/quick/items/qquickevents_p_p.h | 30 +++++------------------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 69df946d6f..6f108162f7 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -501,6 +501,19 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) return nullptr; } +void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp) +{ + m_scenePos = scenePos; + m_pointId = pointId; + m_valid = true; + m_accept = false; + m_state = state; + m_timestamp = timestamp; + if (state == Qt::TouchPointPressed) + m_pressTimestamp = timestamp; + // TODO calculate velocity +} + QQuickItem *QQuickEventPoint::grabber() const { return m_grabber.data(); @@ -511,10 +524,23 @@ void QQuickEventPoint::setGrabber(QQuickItem *grabber) m_grabber = QPointer(grabber); } +void QQuickEventPoint::setAccepted(bool accepted) +{ + m_accept = accepted; +} + QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) {} +void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong timestamp) +{ + QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp); + m_rotation = tp.rotation(); + m_pressure = tp.pressure(); + m_uniqueId = tp.uniqueId(); +} + /*! \internal \class QQuickPointerEvent diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 0169c8e1e1..47fda2fe01 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -259,18 +259,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject public: QQuickEventPoint(QQuickPointerEvent *parent); - void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp) - { - m_scenePos = scenePos; - m_pointId = pointId; - m_valid = true; - m_accept = false; - m_state = state; - m_timestamp = timestamp; - if (state == Qt::TouchPointPressed) - m_pressTimestamp = timestamp; - // TODO calculate velocity - } + void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp); void invalidate() { m_valid = false; } @@ -281,7 +270,7 @@ public: bool isValid() const { return m_valid; } qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } bool isAccepted() const { return m_accept; } - void setAccepted(bool accepted = true) { m_accept = accepted; } + void setAccepted(bool accepted = true); QQuickItem *grabber() const; void setGrabber(QQuickItem *grabber); @@ -307,13 +296,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint public: QQuickEventTouchPoint(QQuickPointerTouchEvent *parent); - void reset(const QTouchEvent::TouchPoint &tp, ulong timestamp) - { - QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp); - m_rotation = tp.rotation(); - m_pressure = tp.pressure(); - m_uniqueId = tp.uniqueId(); - } + void reset(const QTouchEvent::TouchPoint &tp, ulong timestamp); qreal rotation() const { return m_rotation; } qreal pressure() const { return m_pressure; } @@ -390,9 +373,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent { public: QQuickPointerMouseEvent(QObject *parent = nullptr) - : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) - { - } + : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) { } QQuickPointerEvent *reset(QEvent *) override; bool isPressEvent() const override; @@ -415,8 +396,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent { public: QQuickPointerTouchEvent(QObject *parent = nullptr) - : QQuickPointerEvent(parent), m_pointCount(0) - {} + : QQuickPointerEvent(parent), m_pointCount(0) { } QQuickPointerEvent *reset(QEvent *) override; bool isPressEvent() const override; -- cgit v1.2.3 From 6bcea421991de4c89f4c9bc02b01b370788719c7 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 2 Aug 2016 09:20:21 +0200 Subject: Fix grammar in JavaScript Host Environment doc Change-Id: Ib7bc3f62fc27e982f59f1c8b2c2e0cf26306c3b9 Reviewed-by: Mitch Curtis Reviewed-by: Friedemann Kleint --- src/qml/doc/src/javascript/hostenvironment.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index e613c4fcfb..de8b967d72 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -110,7 +110,7 @@ console.log("Result: " + a); \endcode Any attempt to modify the global object - either implicitly or explicitly - will -cause an exception. If uncaught, this will result in an warning being printed, +cause an exception. If uncaught, this will result in a warning being printed, that includes the file and line number of the offending code. \li Global code is run in a reduced scope. @@ -120,7 +120,7 @@ code, it is executed in a scope that contains only the external file itself and the global object. That is, it will not have access to the QML objects and properties it \l {Scope and Naming Resolution}{normally would}. -Global code that only accesses script local variable is permitted. This is an +Global code that only accesses script local variables is permitted. This is an example of valid global code. \code -- cgit v1.2.3 From f18e81a20d843b8f5b03a6d38a6e985227a42739 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 3 Aug 2016 12:44:18 +0200 Subject: photoviewer depends on xmlpatterns Change-Id: Ifc047a7492d0452c86777f1e6a5671421b7065d3 Reviewed-by: Simon Hausmann --- examples/quick/demos/demos.pro | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/quick/demos/demos.pro b/examples/quick/demos/demos.pro index e6937683ab..0644b81a22 100644 --- a/examples/quick/demos/demos.pro +++ b/examples/quick/demos/demos.pro @@ -5,8 +5,7 @@ SUBDIRS = samegame \ tweetsearch \ maroon \ photosurface \ - photoviewer \ stocqt -qtHaveModule(xmlpatterns): SUBDIRS += rssnews +qtHaveModule(xmlpatterns): SUBDIRS += rssnews photoviewer -- cgit v1.2.3 From 0a4cebe6d35abe1019c0d86cf933af972dc8bc6c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 25 Jul 2016 09:25:11 +0200 Subject: Fix recursion during aggressive GC With aggressive GC enabled we may end up calling the GC recursively, which does not work at all, so disable that. Change-Id: I9ce0abbdb7b2bfa8499b33fd0be0d6e4a5212a15 Reviewed-by: Erik Verbruggen --- src/qml/memory/qv4mm.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 885784d7d3..7ab6d15041 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -542,6 +543,8 @@ void MemoryManager::runGC() return; } + QScopedValueRollback gcBlocker(m_d->gcBlocked, true); + if (!m_d->gcStats) { mark(); sweep(); -- cgit v1.2.3 From 5e60d281f1c84c76b80d001ddb3ac00c213de15f Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 25 Jul 2016 12:26:54 +0200 Subject: Test that we don't crash when items are sorted and filtered at the same time This is a test for commit 49c892328223dfa2502b462d8e5e8e181f4f6cd5 in qtbase Change-Id: Id7be42ddd9136b73af08093117316fe2e86a000a Reviewed-by: Robin Burchell --- .../quick/qquicklistview/tst_qquicklistview.cpp | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 08c0887fcf..a3b3a1d309 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -254,6 +254,7 @@ private slots: void QTBUG_50105(); void QTBUG_50097_stickyHeader_positionViewAtIndex(); + void itemFiltered(); private: template void items(const QUrl &source); @@ -8281,6 +8282,37 @@ void tst_QQuickListView::QTBUG_50097_stickyHeader_positionViewAtIndex() QTRY_COMPARE(listview->contentY(), -100.0); // back to the same position: header visible, items not under the header. } +void tst_QQuickListView::itemFiltered() +{ + QStringListModel model(QStringList() << "one" << "two" << "three" << "four" << "five" << "six"); + QSortFilterProxyModel proxy1; + proxy1.setSourceModel(&model); + proxy1.setSortRole(Qt::DisplayRole); + proxy1.setDynamicSortFilter(true); + proxy1.sort(0); + + QSortFilterProxyModel proxy2; + proxy2.setSourceModel(&proxy1); + proxy2.setFilterRole(Qt::DisplayRole); + proxy2.setFilterRegExp("^[^ ]*$"); + proxy2.setDynamicSortFilter(true); + + QScopedPointer window(createView()); + window->engine()->rootContext()->setContextProperty("_model", &proxy2); + QQmlComponent component(window->engine()); + component.setData("import QtQuick 2.4; ListView { " + "anchors.fill: parent; model: _model; delegate: Text { width: parent.width;" + "text: model.display; } }", + QUrl()); + window->setContent(QUrl(), &component, component.create()); + + window->show(); + QTest::qWaitForWindowExposed(window.data()); + + // this should not crash + model.setData(model.index(2), QStringLiteral("modified three"), Qt::DisplayRole); +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" -- cgit v1.2.3 From ffe113ab628adf6c22e96a22cf0bcda8e80c290d Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 4 Aug 2016 10:04:57 +0200 Subject: V4 Debugger: use engineAdded() instead of engineAboutToBeAdded() It does not make a difference in functionality, but after engineAdded() the server won't wait on a mutex anymore. Before this change, if you managed to send a message to the V4 debugger after the server had called aboutToBeAdded(), but before it had stopped waiting, you could produce a deadlock by scheduling an event for the GUI thread that was never delivered. This is a cherry-pick of 18c4295e25503ae637a715858de5c94a3d105a92 from 5.7 as apparently the problem also occurs in 5.6. Change-Id: Ie2343e6da4bd0b8956d41ff8ebd4d7594616ebd1 Reviewed-by: Simon Hausmann --- src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp | 4 ++-- src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index e9bcd772e5..7a9d4a66a4 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -611,7 +611,7 @@ QV4DebugServiceImpl::~QV4DebugServiceImpl() qDeleteAll(handlers); } -void QV4DebugServiceImpl::engineAboutToBeAdded(QQmlEngine *engine) +void QV4DebugServiceImpl::engineAdded(QQmlEngine *engine) { QMutexLocker lock(&m_configMutex); if (engine) { @@ -628,7 +628,7 @@ void QV4DebugServiceImpl::engineAboutToBeAdded(QQmlEngine *engine) } } } - QQmlConfigurableDebugService::engineAboutToBeAdded(engine); + QQmlConfigurableDebugService::engineAdded(engine); } void QV4DebugServiceImpl::engineAboutToBeRemoved(QQmlEngine *engine) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h index 658b1b3998..fafe7d90bf 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -70,7 +70,7 @@ public: explicit QV4DebugServiceImpl(QObject *parent = 0); ~QV4DebugServiceImpl(); - void engineAboutToBeAdded(QQmlEngine *engine); + void engineAdded(QQmlEngine *engine); void engineAboutToBeRemoved(QQmlEngine *engine); void stateAboutToBeChanged(State state); -- cgit v1.2.3 From e2329c91e2999b4533a38c2b9f204af88e0e31ec Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 3 Aug 2016 15:39:37 +0200 Subject: add categorized logging of QQuickEventPoint::setAccepted The same category can be for pointer event handling verbose details. Change-Id: Ife75dcbead6e002c5845a9882950211efe53d4fa Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 6f108162f7..420bbad0b1 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -45,6 +45,8 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcPointerEvents, "qt.quick.pointer.events") + /*! \qmltype KeyEvent \instantiates QQuickKeyEvent @@ -526,7 +528,10 @@ void QQuickEventPoint::setGrabber(QQuickItem *grabber) void QQuickEventPoint::setAccepted(bool accepted) { - m_accept = accepted; + if (m_accept != accepted) { + qCDebug(lcPointerEvents) << this << m_accept << "->" << accepted; + m_accept = accepted; + } } QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) -- cgit v1.2.3 From f36baf1e276457871c6c0233b4b95d01fa01a3c3 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 4 Aug 2016 09:42:45 +0200 Subject: Fix android build Change-Id: Id65119bef515c9b6336a343f39541f557a00e015 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 1 + src/qml/qml/qqmlpropertycache.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 2c164abd6f..ed72054e1e 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #ifndef V4_BOOTSTRAP #include diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 322e519706..d0bdb1c54f 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -51,6 +51,7 @@ #include #include +#include #include // for toupper #include -- cgit v1.2.3 From fdc3dcd43f8944eb33e778627c43ab9487a26960 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Thu, 4 Aug 2016 11:55:28 +0200 Subject: QSGSoftwareAdaptation: Also claim the "softwarecontext" key This key was formerly used by the external plugin, so we should continue to respect it to maintain compatibility with it. Change-Id: I76118250d8ce94aad200c6d4d9f00b8f8cd968ea Reviewed-by: Andy Nichols --- src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp index 144e75d3e6..8ad9b50b09 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp @@ -54,7 +54,7 @@ QSGSoftwareAdaptation::QSGSoftwareAdaptation(QObject *parent) QStringList QSGSoftwareAdaptation::keys() const { - return QStringList() << QLatin1String("software"); + return QStringList() << QLatin1String("software") << QLatin1String("softwarecontext"); } QSGContext *QSGSoftwareAdaptation::create(const QString &) const -- cgit v1.2.3 From 1534dd6d97c49f5c7e0392df9c95198311b5b817 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 22 Jul 2016 10:08:17 +0200 Subject: QML: Introduce QQmlPropertyIndex This helps in making it clear when an index is a plain old number and when it consists of an encoded value type index. Change-Id: Ic50d95caf244ed0ee2d62bdba53910a371cfee04 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlpropertycachecreator_p.h | 7 +- src/qml/compiler/qqmltypecompiler.cpp | 8 +- src/qml/jsruntime/qv4qobjectwrapper.cpp | 2 +- src/qml/qml/qml.pri | 1 + src/qml/qml/qqmlabstractbinding.cpp | 19 ++-- src/qml/qml/qqmlabstractbinding_p.h | 4 +- src/qml/qml/qqmlbinding.cpp | 31 ++---- src/qml/qml/qqmlbinding_p.h | 1 - src/qml/qml/qqmldata_p.h | 18 +-- src/qml/qml/qqmlengine.cpp | 18 ++- src/qml/qml/qqmlobjectcreator.cpp | 18 +-- src/qml/qml/qqmlproperty.cpp | 52 ++++----- src/qml/qml/qqmlproperty_p.h | 8 +- src/qml/qml/qqmlpropertycache_p.h | 14 +-- src/qml/qml/qqmlpropertyindex_p.h | 133 +++++++++++++++++++++++ src/qml/qml/qqmlpropertyvalueinterceptor_p.h | 4 +- src/qml/qml/qqmlvaluetypeproxybinding.cpp | 6 +- src/qml/qml/qqmlvaluetypeproxybinding_p.h | 4 +- src/qml/qml/qqmlvaluetypewrapper.cpp | 2 +- src/qml/qml/qqmlvmemetaobject.cpp | 26 +++-- src/qml/qml/qqmlvmemetaobject_p.h | 6 +- tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp | 39 ++++--- 22 files changed, 283 insertions(+), 138 deletions(-) create mode 100644 src/qml/qml/qqmlpropertyindex_p.h diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 09a093d911..fe10085829 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -576,8 +576,7 @@ inline void QQmlPropertyCacheAliasCreator::appendAliasPropertie const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); Q_ASSERT(targetCache); - int coreIndex; - QQmlPropertyData::decodeValueTypePropertyIndex(alias->encodedMetaPropertyIndex, &coreIndex); + int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex(); QQmlPropertyData *targetProperty = targetCache->property(coreIndex); if (!targetProperty) return false; @@ -655,8 +654,8 @@ inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias *propertyFlags |= QQmlPropertyData::IsQObjectDerived; } else { - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(alias.encodedMetaPropertyIndex, &coreIndex); + int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex(); + int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex(); QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); Q_ASSERT(targetCache); diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 68de9aee44..e1166286c2 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -1078,7 +1078,7 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv } else property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length()); - int propIdx = -1; + QQmlPropertyIndex propIdx; if (property.isEmpty()) { alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; @@ -1121,7 +1121,7 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv break; } - propIdx = targetProperty->coreIndex; + propIdx = QQmlPropertyIndex(targetProperty->coreIndex); if (!subProperty.isEmpty()) { const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType); @@ -1138,14 +1138,14 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv } Q_ASSERT(valueTypeIndex <= 0x0000FFFF); - propIdx = QQmlPropertyData::encodeValueTypePropertyIndex(propIdx, valueTypeIndex); + propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex); } else { if (targetProperty->isQObject()) alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; } } - alias->encodedMetaPropertyIndex = propIdx; + alias->encodedMetaPropertyIndex = propIdx.toEncoded(); alias->flags |= QV4::CompiledData::Alias::Resolved; numResolvedAliases++; } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 20179643fd..ce0c7bc04a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -328,7 +328,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired) { - QQmlData::flushPendingBinding(object, property->coreIndex); + QQmlData::flushPendingBinding(object, property->encodedIndex()); if (property->isFunction() && !property->isVarProperty()) { if (property->isVMEFunction()) { diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index cc394b78cb..87be140cbb 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -90,6 +90,7 @@ HEADERS += \ $$PWD/qqmlxmlhttprequest_p.h \ $$PWD/qqmlcleanup_p.h \ $$PWD/qqmlpropertycache_p.h \ + $$PWD/qqmlpropertyindex_p.h \ $$PWD/qqmlnotifier_p.h \ $$PWD/qqmltypenotavailable_p.h \ $$PWD/qqmltypenamecache_p.h \ diff --git a/src/qml/qml/qqmlabstractbinding.cpp b/src/qml/qml/qqmlabstractbinding.cpp index abaf95fa11..39d609454f 100644 --- a/src/qml/qml/qqmlabstractbinding.cpp +++ b/src/qml/qml/qqmlabstractbinding.cpp @@ -78,24 +78,26 @@ void QQmlAbstractBinding::addToObject() QQmlData *data = QQmlData::get(obj, true); - int coreIndex; - if (QQmlPropertyData::decodeValueTypePropertyIndex(targetPropertyIndex(), &coreIndex) != -1) { + int coreIndex = targetPropertyIndex().coreIndex(); + if (targetPropertyIndex().hasValueTypeIndex()) { // Value type // Find the value type proxy (if there is one) QQmlValueTypeProxyBinding *proxy = 0; if (data->hasBindingBit(coreIndex)) { QQmlAbstractBinding *b = data->bindings; - while (b && b->targetPropertyIndex() != coreIndex) + while (b && (b->targetPropertyIndex().coreIndex() != coreIndex || + b->targetPropertyIndex().hasValueTypeIndex())) b = b->nextBinding(); Q_ASSERT(b && b->isValueTypeProxy()); proxy = static_cast(b); } if (!proxy) { - proxy = new QQmlValueTypeProxyBinding(obj, coreIndex); + proxy = new QQmlValueTypeProxyBinding(obj, QQmlPropertyIndex(coreIndex)); - Q_ASSERT(proxy->targetPropertyIndex() == coreIndex); + Q_ASSERT(proxy->targetPropertyIndex().coreIndex() == coreIndex); + Q_ASSERT(!proxy->targetPropertyIndex().hasValueTypeIndex()); Q_ASSERT(proxy->targetObject() == obj); proxy->addToObject(); @@ -137,12 +139,13 @@ void QQmlAbstractBinding::removeFromObject() next = nextBinding(); setNextBinding(0); - int coreIndex; - if (QQmlPropertyData::decodeValueTypePropertyIndex(targetPropertyIndex(), &coreIndex) != -1) { + int coreIndex = targetPropertyIndex().coreIndex(); + if (targetPropertyIndex().hasValueTypeIndex()) { // Find the value type binding QQmlAbstractBinding *vtbinding = data->bindings; - while (vtbinding->targetPropertyIndex() != coreIndex) { + while (vtbinding && (vtbinding->targetPropertyIndex().coreIndex() != coreIndex || + vtbinding->targetPropertyIndex().hasValueTypeIndex())) { vtbinding = vtbinding->nextBinding(); Q_ASSERT(vtbinding); } diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index 0ccfae4610..bea2d253e4 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -76,7 +76,7 @@ public: // Should return the encoded property index for the binding. Should return this value // even if the binding is not enabled or added to an object. // Encoding is: coreIndex | (valueTypeIndex << 16) - int targetPropertyIndex() const { return m_targetIndex; } + QQmlPropertyIndex targetPropertyIndex() const { return m_targetIndex; } // Should return the object for the binding. Should return this object even if the // binding is not enabled or added to the object. @@ -113,7 +113,7 @@ protected: inline void setNextBinding(QQmlAbstractBinding *); - int m_targetIndex; + QQmlPropertyIndex m_targetIndex; // Pointer is the target object to which the binding binds // flag1 is the updating flag diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index ffe1b4287d..c96e5b661d 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -465,8 +465,7 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) m_nextBinding.setFlag2(); // Always use accessors, only not when: if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(targetObject())) { - int coreIndex = getPropertyCoreIndex(); - if (coreIndex == -1 || interceptorMetaObject->intercepts(coreIndex)) + if (!m_targetIndex.isValid() || interceptorMetaObject->intercepts(m_targetIndex)) m_nextBinding.clearFlag2(); } @@ -491,7 +490,7 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) m_target = object; if (!object) { - m_targetIndex = -1; + m_targetIndex = QQmlPropertyIndex(); return; } @@ -505,7 +504,7 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) int aValueTypeIndex; if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) { m_target = 0; - m_targetIndex = -1; + m_targetIndex = QQmlPropertyIndex(); return; } if (valueTypeIndex == -1) @@ -514,7 +513,7 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) QQmlData *data = QQmlData::get(object, false); if (!data || !data->propertyCache) { m_target = 0; - m_targetIndex = -1; + m_targetIndex = QQmlPropertyIndex(); return; } QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); @@ -543,39 +542,25 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) QQmlPropertyData QQmlBinding::getPropertyData() const { - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(m_targetIndex, &coreIndex); - QQmlData *data = QQmlData::get(*m_target, false); Q_ASSERT(data && data->propertyCache); - QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); + QQmlPropertyData *propertyData = data->propertyCache->property(m_targetIndex.coreIndex()); Q_ASSERT(propertyData); QQmlPropertyData d = *propertyData; - if (Q_UNLIKELY(valueTypeIndex != -1)) { + if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex())) { const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d.propType); Q_ASSERT(valueTypeMetaObject); - QMetaProperty vtProp = valueTypeMetaObject->property(valueTypeIndex); + QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex()); d.setFlags(d.getFlags() | QQmlPropertyData::IsValueTypeVirtual); d.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); d.valueTypePropType = vtProp.userType(); - d.valueTypeCoreIndex = valueTypeIndex; + d.valueTypeCoreIndex = m_targetIndex.valueTypeIndex(); } return d; } -Q_ALWAYS_INLINE int QQmlBinding::getPropertyCoreIndex() const -{ - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(m_targetIndex, &coreIndex); - if (valueTypeIndex != -1) { - return -1; - } else { - return coreIndex; - } -} - class QObjectPointerBinding: public QQmlNonbindingBinding { QQmlMetaObject targetMetaObject; diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 67fbeb693e..94fc0ccfc0 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -107,7 +107,6 @@ protected: const QV4::ScopedFunctionObject &f) = 0; QQmlPropertyData getPropertyData() const; - int getPropertyCoreIndex() const; int getPropertyType() const; bool slowWrite(const QQmlPropertyData &core, const QV4::Value &result, bool isUndefined, diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index a9f2acdb06..d9a69a9ca3 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -53,7 +53,7 @@ #include #include - +#include #include #include #include @@ -174,7 +174,7 @@ public: void clearBindingBit(int); void setBindingBit(QObject *obj, int); - inline bool hasPendingBindingBit(int) const; + inline bool hasPendingBindingBit(int index) const; void setPendingBindingBit(QObject *obj, int); void clearPendingBindingBit(int); @@ -227,7 +227,7 @@ public: static void markAsDeleted(QObject *); static void setQueuedForDeletion(QObject *); - static inline void flushPendingBinding(QObject *, int coreIndex); + static inline void flushPendingBinding(QObject *, QQmlPropertyIndex propertyIndex); static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object); @@ -235,7 +235,7 @@ private: // For attachedProperties mutable QQmlDataExtended *extendedData; - void flushPendingBindingImpl(int coreIndex); + void flushPendingBindingImpl(QQmlPropertyIndex index); }; bool QQmlData::wasDeleted(QObject *object) @@ -288,20 +288,20 @@ bool QQmlData::hasBindingBit(int coreIndex) const (bindingBits[bit / 32] & (1 << (bit % 32)))); } -bool QQmlData::hasPendingBindingBit(int coreIndex) const +bool QQmlData::hasPendingBindingBit(int index) const { - int bit = coreIndex * 2 + 1; + int bit = index * 2 + 1; return bindingBitsSize > bit && ((bindingBitsSize == 32) ? (bindingBitsValue & (1 << bit)) : (bindingBits[bit / 32] & (1 << (bit % 32)))); } -void QQmlData::flushPendingBinding(QObject *o, int coreIndex) +void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex) { QQmlData *data = QQmlData::get(o, false); - if (data && data->hasPendingBindingBit(coreIndex)) - data->flushPendingBindingImpl(coreIndex); + if (data && data->hasPendingBindingBit(propertyIndex.coreIndex())) + data->flushPendingBindingImpl(propertyIndex); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 5cfb546718..48832f3b07 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -837,16 +837,18 @@ void QQmlData::setQueuedForDeletion(QObject *object) } } -void QQmlData::flushPendingBindingImpl(int coreIndex) +void QQmlData::flushPendingBindingImpl(QQmlPropertyIndex index) { - clearPendingBindingBit(coreIndex); + clearPendingBindingBit(index.coreIndex()); // Find the binding QQmlAbstractBinding *b = bindings; - while (b && b->targetPropertyIndex() != coreIndex) + while (b && (b->targetPropertyIndex().coreIndex() != index.coreIndex() || + b->targetPropertyIndex().hasValueTypeIndex())) b = b->nextBinding(); - if (b && b->targetPropertyIndex() == coreIndex) + if (b && b->targetPropertyIndex().coreIndex() == index.coreIndex() && + !b->targetPropertyIndex().hasValueTypeIndex()) b->setEnabled(true, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); } @@ -1788,21 +1790,29 @@ static void QQmlData_clearBit(QQmlData *data, int bit) void QQmlData::clearBindingBit(int coreIndex) { + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); QQmlData_clearBit(this, coreIndex * 2); } void QQmlData::setBindingBit(QObject *obj, int coreIndex) { + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); QQmlData_setBit(this, obj, coreIndex * 2); } void QQmlData::clearPendingBindingBit(int coreIndex) { + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); QQmlData_clearBit(this, coreIndex * 2 + 1); } void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) { + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); QQmlData_setBit(this, obj, coreIndex * 2 + 1); } diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index b354104b6e..1e1c238b19 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -636,10 +636,10 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) // ### this is best done through type-compile-time binding skip lists. if (_valueTypeProperty) { - QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, _valueTypeProperty->coreIndex); + QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex)); if (binding && !binding->isValueTypeProxy()) { - QQmlPropertyPrivate::removeBinding(_bindingTarget, _valueTypeProperty->coreIndex); + QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex)); } else if (binding) { QQmlValueTypeProxyBinding *proxy = static_cast(binding); @@ -788,7 +788,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con if (_ddata->hasBindingBit(property->coreIndex) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) && !_valueTypeProperty) - QQmlPropertyPrivate::removeBinding(_bindingTarget, property->coreIndex); + QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex)); if (binding->type == QV4::CompiledData::Binding::Type_Script) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; @@ -857,14 +857,16 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QObject *target = createdSubObject->parent(); if (targetCorePropertyData.isAlias()) { - int propIndex; - QQmlPropertyPrivate::findAliasTarget(target, targetCorePropertyData.coreIndex, &target, &propIndex); + QQmlPropertyIndex propIndex; + QQmlPropertyPrivate::findAliasTarget(target, QQmlPropertyIndex(targetCorePropertyData.coreIndex), &target, &propIndex); QQmlData *data = QQmlData::get(target); if (!data || !data->propertyCache) { qWarning() << "can't resolve property alias for 'on' assignment"; return false; } - targetCorePropertyData = *data->propertyCache->property(propIndex); + + // we can't have aliasses on subproperties of value types, so: + targetCorePropertyData = *data->propertyCache->property(propIndex.coreIndex()); } QQmlProperty prop = @@ -874,7 +876,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlInterceptorMetaObject *mo = QQmlInterceptorMetaObject::get(target); if (!mo) mo = new QQmlInterceptorMetaObject(target, QQmlData::get(target)->propertyCache); - mo->registerInterceptor(prop.index(), QQmlPropertyPrivate::valueTypeCoreIndex(prop), vi); + mo->registerInterceptor(QQmlPropertyPrivate::propertyIndex(prop), vi); return true; } return false; @@ -1191,7 +1193,7 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru continue; QQmlData *data = QQmlData::get(b->targetObject()); Q_ASSERT(data); - data->clearPendingBindingBit(b->targetPropertyIndex()); + data->clearPendingBindingBit(b->targetPropertyIndex().coreIndex()); b->setEnabled(true, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index b3eb0a5619..260cf9deaa 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -741,10 +741,10 @@ QQmlPropertyPrivate::setBinding(const QQmlProperty &that, QQmlAbstractBinding *n setBinding(newBinding); } -static void removeOldBinding(QObject *object, int index, QQmlPropertyPrivate::BindingFlags flags = QQmlPropertyPrivate::None) +static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPropertyPrivate::BindingFlags flags = QQmlPropertyPrivate::None) { - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); + int coreIndex = index.coreIndex(); + int valueTypeIndex = index.valueTypeIndex(); QQmlData *data = QQmlData::get(object, false); @@ -754,7 +754,8 @@ static void removeOldBinding(QObject *object, int index, QQmlPropertyPrivate::Bi QQmlAbstractBinding::Ptr oldBinding; oldBinding = data->bindings; - while (oldBinding && oldBinding->targetPropertyIndex() != coreIndex) + while (oldBinding && (oldBinding->targetPropertyIndex().coreIndex() != coreIndex || + oldBinding->targetPropertyIndex().hasValueTypeIndex())) oldBinding = oldBinding->nextBinding(); if (!oldBinding) @@ -776,12 +777,12 @@ void QQmlPropertyPrivate::removeBinding(QQmlAbstractBinding *b) removeBinding(b->targetObject(), b->targetPropertyIndex()); } -void QQmlPropertyPrivate::removeBinding(QObject *o, int index) +void QQmlPropertyPrivate::removeBinding(QObject *o, QQmlPropertyIndex index) { Q_ASSERT(o); QObject *target; - int targetIndex; + QQmlPropertyIndex targetIndex; findAliasTarget(o, index, &target, &targetIndex); removeOldBinding(target, targetIndex); } @@ -795,7 +796,7 @@ void QQmlPropertyPrivate::removeBinding(const QQmlProperty &that) } QQmlAbstractBinding * -QQmlPropertyPrivate::binding(QObject *object, int index) +QQmlPropertyPrivate::binding(QObject *object, QQmlPropertyIndex index) { QQmlData *data = QQmlData::get(object); if (!data) @@ -803,19 +804,19 @@ QQmlPropertyPrivate::binding(QObject *object, int index) findAliasTarget(object, index, &object, &index); - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); + const int coreIndex = index.coreIndex(); + const int valueTypeIndex = index.valueTypeIndex(); if (!data->hasBindingBit(coreIndex)) return 0; QQmlAbstractBinding *binding = data->bindings; - while (binding && binding->targetPropertyIndex() != coreIndex) + while (binding && (binding->targetPropertyIndex().coreIndex() != coreIndex || + binding->targetPropertyIndex().hasValueTypeIndex())) binding = binding->nextBinding(); if (binding && valueTypeIndex != -1) { if (binding->isValueTypeProxy()) { - int index = QQmlPropertyData::encodeValueTypePropertyIndex(coreIndex, valueTypeIndex); binding = static_cast(binding)->binding(index); } } @@ -823,13 +824,14 @@ QQmlPropertyPrivate::binding(QObject *object, int index) return binding; } -void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, - QObject **targetObject, int *targetBindingIndex) +void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bindingIndex, + QObject **targetObject, + QQmlPropertyIndex *targetBindingIndex) { QQmlData *data = QQmlData::get(object, false); if (data) { - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(bindingIndex, &coreIndex); + int coreIndex = bindingIndex.coreIndex(); + int valueTypeIndex = bindingIndex.valueTypeIndex(); QQmlPropertyData *propertyData = data->propertyCache?data->propertyCache->property(coreIndex):0; @@ -841,11 +843,12 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, // This will either be a value type sub-reference or an alias to a value-type sub-reference not both Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); - int aBindingIndex = aCoreIndex; - if (aValueTypeIndex != -1) - aBindingIndex = QQmlPropertyData::encodeValueTypePropertyIndex(aBindingIndex, aValueTypeIndex); - else if (valueTypeIndex != -1) - aBindingIndex = QQmlPropertyData::encodeValueTypePropertyIndex(aBindingIndex, valueTypeIndex); + QQmlPropertyIndex aBindingIndex(aCoreIndex); + if (aValueTypeIndex != -1) { + aBindingIndex = QQmlPropertyIndex(aCoreIndex, aValueTypeIndex); + } else if (valueTypeIndex != -1) { + aBindingIndex = QQmlPropertyIndex(aCoreIndex, valueTypeIndex); + } findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex); return; @@ -863,11 +866,10 @@ void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags Q_ASSERT(binding); QObject *object = binding->targetObject(); - int index = binding->targetPropertyIndex(); + const QQmlPropertyIndex index = binding->targetPropertyIndex(); #ifndef QT_NO_DEBUG - int coreIndex; - QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); + int coreIndex = index.coreIndex(); QQmlData *data = QQmlData::get(object, true); if (data->propertyCache) { QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); @@ -1600,9 +1602,9 @@ int QQmlProperty::index() const return d ? d->core.coreIndex : -1; } -int QQmlPropertyPrivate::valueTypeCoreIndex(const QQmlProperty &that) +QQmlPropertyIndex QQmlPropertyPrivate::propertyIndex(const QQmlProperty &that) { - return that.d ? that.d->core.getValueTypeCoreIndex() : -1; + return that.d ? that.d->core.encodedIndex() : QQmlPropertyIndex(); } /*! diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 9398c74621..f23426f45c 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -101,7 +101,7 @@ public: QQmlPropertyData::WriteFlags flags = 0); static bool write(QObject *, const QQmlPropertyData &, const QVariant &, QQmlContextData *, QQmlPropertyData::WriteFlags flags = 0); - static void findAliasTarget(QObject *, int, QObject **, int *); + static void findAliasTarget(QObject *, QQmlPropertyIndex, QObject **, QQmlPropertyIndex *); enum BindingFlag { None = 0, @@ -112,9 +112,9 @@ public: static void setBinding(QQmlAbstractBinding *binding, BindingFlags flags = None, QQmlPropertyData::WriteFlags writeFlags = QQmlPropertyData::DontRemoveBinding); static void removeBinding(const QQmlProperty &that); - static void removeBinding(QObject *o, int index); + static void removeBinding(QObject *o, QQmlPropertyIndex index); static void removeBinding(QQmlAbstractBinding *b); - static QQmlAbstractBinding *binding(QObject *, int index); + static QQmlAbstractBinding *binding(QObject *, QQmlPropertyIndex index); static QQmlPropertyData saveValueType(const QQmlPropertyData &, const QMetaObject *, int, @@ -138,7 +138,7 @@ public: static void takeSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *); static bool write(const QQmlProperty &that, const QVariant &, QQmlPropertyData::WriteFlags); - static int valueTypeCoreIndex(const QQmlProperty &that); + static QQmlPropertyIndex propertyIndex(const QQmlProperty &that); static int bindingIndex(const QQmlProperty &that); static int bindingIndex(const QQmlPropertyData &that); static QMetaMethod findSignalByName(const QMetaObject *mo, const QByteArray &); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 750537e707..52e0fdc3bd 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -55,6 +55,7 @@ #include #include "qqmlcleanup_p.h" #include "qqmlnotifier_p.h" +#include #include #include @@ -174,13 +175,7 @@ public: // Returns the "encoded" index for use with bindings. Encoding is: // coreIndex | ((valueTypeCoreIndex + 1) << 16) - inline int encodedIndex() const; - static int encodeValueTypePropertyIndex(int coreIndex, int valueTypeCoreIndex) - { return coreIndex | ((valueTypeCoreIndex + 1) << 16); } - static int decodeValueTypePropertyIndex(int index, int *coreIndex = 0) { - if (coreIndex) *coreIndex = index & 0xffff; - return (index >> 16) - 1; - } + inline QQmlPropertyIndex encodedIndex() const; union { int propType; // When !NotFullyResolved @@ -557,9 +552,10 @@ int QQmlPropertyRawData::getValueTypeCoreIndex() const return isValueTypeVirtual()?valueTypeCoreIndex:-1; } -int QQmlPropertyRawData::encodedIndex() const +QQmlPropertyIndex QQmlPropertyRawData::encodedIndex() const { - return isValueTypeVirtual()?QQmlPropertyData::encodeValueTypePropertyIndex(coreIndex, valueTypeCoreIndex):coreIndex; + return isValueTypeVirtual() ? QQmlPropertyIndex(coreIndex, valueTypeCoreIndex) + : QQmlPropertyIndex(coreIndex); } inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const diff --git a/src/qml/qml/qqmlpropertyindex_p.h b/src/qml/qml/qqmlpropertyindex_p.h new file mode 100644 index 0000000000..ebc1828efb --- /dev/null +++ b/src/qml/qml/qqmlpropertyindex_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTYINDEX_P_H +#define QQMLPROPERTYINDEX_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QQmlPropertyIndex +{ + qint32 index; + +public: + QQmlPropertyIndex() + { index = -1; } + + static QQmlPropertyIndex fromEncoded(qint32 encodedIndex) + { + QQmlPropertyIndex idx; + idx.index = encodedIndex; + return idx; + } + + explicit QQmlPropertyIndex(int coreIndex) + { index = encode(coreIndex, -1); } + + explicit QQmlPropertyIndex(int coreIndex, int valueTypeIndex) + : index(encode(coreIndex, valueTypeIndex)) + {} + + bool isValid() const + { return index != -1; } + + int coreIndex() const + { + if (index == -1) + return -1; + return index & 0xffff; + } + + int valueTypeIndex() const + { + if (index == -1) + return -1; + return (index >> 16) - 1; + } + + bool hasValueTypeIndex() const + { + if (index == -1) + return false; + return index >> 16; + } + + qint32 toEncoded() const + { return index; } + + int intValue() const + { return index; } + + bool operator==(const QQmlPropertyIndex &other) const + { return index == other.index; } + + bool operator!=(const QQmlPropertyIndex &other) const + { return !operator==(other); } + +private: + static qint32 encode(int coreIndex, int valueTypeIndex) + { + Q_ASSERT(coreIndex >= -1); + Q_ASSERT(coreIndex <= 0xffff); + Q_ASSERT(valueTypeIndex >= -1); + Q_ASSERT(valueTypeIndex < 0xffff); + + if (coreIndex == -1) + return -1; + else + return coreIndex | ((valueTypeIndex + 1) << 16); + } +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYINDEX_P_H diff --git a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h index 0c10d13aea..a3d6b0c8c7 100644 --- a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h +++ b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h @@ -52,6 +52,7 @@ // #include +#include #include QT_BEGIN_NAMESPACE @@ -68,8 +69,7 @@ public: private: friend class QQmlInterceptorMetaObject; - int m_coreIndex; - int m_valueTypeCoreIndex; + QQmlPropertyIndex m_propertyIndex; QQmlPropertyValueInterceptor *m_next; }; diff --git a/src/qml/qml/qqmlvaluetypeproxybinding.cpp b/src/qml/qml/qqmlvaluetypeproxybinding.cpp index 595cd01d05..56f073121e 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding.cpp +++ b/src/qml/qml/qqmlvaluetypeproxybinding.cpp @@ -41,7 +41,7 @@ QT_BEGIN_NAMESPACE -QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index) +QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, QQmlPropertyIndex index) : QQmlAbstractBinding(), m_bindings(0) { @@ -72,7 +72,7 @@ bool QQmlValueTypeProxyBinding::isValueTypeProxy() const return true; } -QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex) +QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(QQmlPropertyIndex propertyIndex) { QQmlAbstractBinding *binding = m_bindings.data(); @@ -91,7 +91,7 @@ void QQmlValueTypeProxyBinding::removeBindings(quint32 mask) QQmlAbstractBinding *lastBinding = 0; while (binding) { - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(binding->targetPropertyIndex()); + const int valueTypeIndex = binding->targetPropertyIndex().valueTypeIndex(); if (valueTypeIndex != -1 && (mask & (1 << valueTypeIndex))) { QQmlAbstractBinding *remove = binding; remove->setAddedToObject(false); diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h index 6e297bb3ea..9a487d6992 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h +++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h @@ -58,9 +58,9 @@ QT_BEGIN_NAMESPACE class QQmlValueTypeProxyBinding : public QQmlAbstractBinding { public: - QQmlValueTypeProxyBinding(QObject *o, int coreIndex); + QQmlValueTypeProxyBinding(QObject *o, QQmlPropertyIndex coreIndex); - QQmlAbstractBinding *binding(int targetPropertyIndex); + QQmlAbstractBinding *binding(QQmlPropertyIndex targetPropertyIndex); void removeBindings(quint32 mask); virtual void setEnabled(bool, QQmlPropertyData::WriteFlags); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index b69eea61c5..e39acaf168 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -464,7 +464,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QQmlPropertyPrivate::setBinding(newBinding); return; } else { - QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyData::encodeValueTypePropertyIndex(reference->d()->property, pd->coreIndex)); + QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex)); } } diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index d5001674ad..81bc6846e6 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -159,8 +159,7 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() QQmlData *targetDData = QQmlData::get(target, /*create*/false); if (!targetDData) return; - int coreIndex; - QQmlPropertyData::decodeValueTypePropertyIndex(aliasData->encodedMetaPropertyIndex, &coreIndex); + int coreIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex).coreIndex(); const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); if (!pd) return; @@ -199,10 +198,9 @@ QQmlInterceptorMetaObject::~QQmlInterceptorMetaObject() } -void QQmlInterceptorMetaObject::registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor) +void QQmlInterceptorMetaObject::registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor) { - interceptor->m_coreIndex = index; - interceptor->m_valueTypeCoreIndex = valueIndex; + interceptor->m_propertyIndex = index; interceptor->m_next = interceptors; interceptors = interceptor; } @@ -223,10 +221,10 @@ bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) !(*reinterpret_cast(a[3]) & QQmlPropertyData::BypassInterceptor)) { for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) { - if (vi->m_coreIndex != id) + if (vi->m_propertyIndex.coreIndex() != id) continue; - int valueIndex = vi->m_valueTypeCoreIndex; + const int valueIndex = vi->m_propertyIndex.valueTypeIndex(); int type = QQmlData::get(object)->propertyCache->property(id)->propType; if (type != QVariant::Invalid) { @@ -866,8 +864,9 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (!targetDData) return -1; - int coreIndex; - const int valueTypePropertyIndex = QQmlPropertyData::decodeValueTypePropertyIndex(aliasData->encodedMetaPropertyIndex, &coreIndex); + QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex); + int coreIndex = encodedIndex.coreIndex(); + const int valueTypePropertyIndex = encodedIndex.valueTypeIndex(); // Remove binding (if any) on write if(c == QMetaObject::WriteProperty) { @@ -875,7 +874,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) { QQmlData *targetData = QQmlData::get(target); if (targetData && targetData->hasBindingBit(coreIndex)) - QQmlPropertyPrivate::removeBinding(target, aliasData->encodedMetaPropertyIndex); + QQmlPropertyPrivate::removeBinding(target, encodedIndex); } } @@ -1189,8 +1188,11 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, if (!*target) return false; - if (!aliasData->isObjectAlias()) - *valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(aliasData->encodedMetaPropertyIndex, coreIndex); + if (!aliasData->isObjectAlias()) { + QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex); + *coreIndex = encodedIndex.coreIndex(); + *valueTypeIndex = encodedIndex.valueTypeIndex(); + } return true; } diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index 4a81fc50d2..031bfdfb9b 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -97,7 +97,7 @@ public: QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache); ~QQmlInterceptorMetaObject(); - void registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor); + void registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor); static QQmlInterceptorMetaObject *get(QObject *obj); @@ -106,10 +106,10 @@ public: // Used by auto-tests for inspection QQmlPropertyCache *propertyCache() const { return cache; } - bool intercepts(int coreIndex) const + bool intercepts(QQmlPropertyIndex propertyIndex) const { for (auto it = interceptors; it; it = it->m_next) { - if (it->m_coreIndex == coreIndex) + if (it->m_propertyIndex == propertyIndex) return true; } return false; diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 63d7f1c12b..21e6d9efe6 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -194,7 +194,8 @@ void tst_qqmlproperty::qmlmetaproperty() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -445,7 +446,8 @@ void tst_qqmlproperty::qmlmetaproperty_object() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -495,7 +497,8 @@ void tst_qqmlproperty::qmlmetaproperty_object() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -548,7 +551,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -598,7 +602,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -647,7 +652,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QVERIFY(!sigExprWatcher.wasDeleted()); QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -696,7 +702,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QVERIFY(!sigExprWatcher.wasDeleted()); QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -749,7 +756,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -799,7 +807,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -852,7 +861,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -902,7 +912,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr); QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -951,7 +962,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QVERIFY(!sigExprWatcher.wasDeleted()); QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } @@ -1000,7 +1012,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QVERIFY(!sigExprWatcher.wasDeleted()); QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()")); - QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); + QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1); + QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex()); delete obj; } -- cgit v1.2.3 From c75c4201bc26d6e85abace15d5e773b0702bacd2 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 4 Aug 2016 13:48:40 +0200 Subject: Remove bogus claim There cannot be any change in grabber, when calling QCoreApplication::sendEvent. This kind of surprise take-over should be done through childFilterMouse which this function is already a part of. Change-Id: I10c3aad3a83b0045e3c407b936e39ba589e4c6d1 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ae3b272e72..cf50733d1b 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -682,10 +682,8 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *eve event->setAccepted(me->isAccepted()); if (me->isAccepted()) { qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem; - auto pointerEventPoint = device->pointerEvent()->pointById(p.id()); - pointerEventPoint->setGrabber(q->mouseGrabberItem()); // N.B. the mouseGrabberItem may be different after returning from sendEvent() - return true; } + return event->isAccepted(); } else { // no grabber, check if we care about mouse hover // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now. -- cgit v1.2.3 From 4ecd73fbed2afe71dc7302bafd595518a4779541 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 4 Aug 2016 13:20:24 +0200 Subject: Pass pointerEvent into QQuickWindowPrivate::deliverTouchAsMouse Change-Id: I53c737498f27db3e1fea4fd2f9e10f75a2b48d14 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 13 ++++++++----- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index cf50733d1b..80d867a05c 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -622,10 +622,14 @@ bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp) return doubleClicked; } -bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *event) +bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent) { Q_Q(QQuickWindow); - auto device = QQuickPointerDevice::touchDevice(event->device()); + auto device = pointerEvent->device(); + + // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here. + Q_ASSERT(pointerEvent->asPointerTouchEvent()); + QTouchEvent *event = pointerEvent->asPointerTouchEvent()->touchEventForItem(item); // For each point, check if it is accepted, if not, try the next point. // Any of the fingers can become the mouse one. @@ -640,8 +644,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *eve if (!item->contains(pos)) break; - // FIXME: this is a bit backwards, should just have the pointer event passed into the function - auto pointerEventPoint = device->pointerEvent()->pointById(p.id()); + auto pointerEventPoint = pointerEvent->pointById(p.id()); pointerEventPoint->setGrabber(item); qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item; QScopedPointer mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); @@ -2285,7 +2288,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - if (deliverTouchAsMouse(item, touchEvent.data())) + if (deliverTouchAsMouse(item, event)) eventAccepted = true; } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 90e75fb751..3328eb65c4 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -138,7 +138,7 @@ public: // Mouse positions are saved in widget coordinates QPointF lastMousePosition; - bool deliverTouchAsMouse(QQuickItem *item, QTouchEvent *event); + bool deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent); void translateTouchEvent(QTouchEvent *touchEvent); void setMouseGrabber(QQuickItem *grabber); void grabTouchPoints(QQuickItem *grabber, const QVector &ids); -- cgit v1.2.3 From b07b41edb266a2a5d7cf83aa63e6eb1e69183ccd Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 3 Aug 2016 22:58:11 +0200 Subject: Add missing Q_OBJECT macros Change-Id: I5f58f53a10b11906c1f784f1cab96ec761d8d588 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index f3e6fdddff..6dee1f5b45 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -375,6 +375,7 @@ protected: class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent { + Q_OBJECT public: QQuickPointerMouseEvent(QObject *parent = nullptr) : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) { } @@ -400,6 +401,7 @@ private: class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent { + Q_OBJECT public: QQuickPointerTouchEvent(QObject *parent = nullptr) : QQuickPointerEvent(parent), m_pointCount(0) { } -- cgit v1.2.3 From d77e544a568dbfff698c4d37c669bc991383fe9b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 28 Jul 2016 17:40:36 +0200 Subject: Safeguard disk cache loading with checksum verification When loading a QML component from the disk cache, compare the checksum of the dependent types against the checksum when the cache was created. Any change in the meta-object of a dependent type should trigger a re-creation discard of the cache and consequent re-creation (in the test-case). Unfortunately this also requires extending the existing hack in the unit test to deal with the low second precision on HFS+ in order to pass the tests. Change-Id: Ib8e899347680f7be676788388e9c23a09b0277e3 Reviewed-by: Ulf Hermann --- src/qml/compiler/qv4compileddata.cpp | 19 ++++ src/qml/compiler/qv4compileddata_p.h | 3 + src/qml/qml/qqmltypeloader.cpp | 98 +++++++++++------- src/qml/qml/qqmltypeloader_p.h | 10 +- tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 122 ++++++++++++++++++++++- 5 files changed, 207 insertions(+), 45 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 5948992f67..a4fffedbcd 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -301,6 +301,25 @@ void CompilationUnit::finalize(QQmlEnginePrivate *engine) totalObjectCount = objectCount; } +bool CompilationUnit::verifyChecksum(QQmlEngine *engine, + const ResolvedTypeReferenceMap &dependentTypes) const +{ + if (dependentTypes.isEmpty()) { + for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) { + if (data->dependencyMD5Checksum[i] != 0) + return false; + } + return true; + } + QCryptographicHash hash(QCryptographicHash::Md5); + if (!dependentTypes.addToHash(&hash, engine)) + return false; + QByteArray checksum = hash.result(); + Q_ASSERT(checksum.size() == sizeof(data->dependencyMD5Checksum)); + return memcmp(data->dependencyMD5Checksum, checksum.constData(), + sizeof(data->dependencyMD5Checksum)) == 0; +} + bool CompilationUnit::saveToDisk(QString *errorString) { errorString->clear(); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 1f253e02fd..c79f4cf007 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -865,6 +865,9 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QVector dependentScripts; ResolvedTypeReferenceMap resolvedTypes; + bool verifyChecksum(QQmlEngine *engine, + const ResolvedTypeReferenceMap &dependentTypes) const; + int metaTypeId; int listMetaTypeId; bool isRegisteredWithEngine; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 5bc9c8ac25..538e630e98 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2136,20 +2136,15 @@ bool QQmlTypeData::tryLoadFromDiskCache() return true; } -void QQmlTypeData::rebuildTypeAndPropertyCaches() +void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer &importCache, + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) { Q_ASSERT(m_compiledData); + m_compiledData->importCache = importCache; + m_compiledData->resolvedTypes = resolvedTypeCache; QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); - { - QQmlCompileError error = buildTypeResolutionCaches(&m_compiledData->importCache, &m_compiledData->resolvedTypes); - if (error.isSet()) { - setError(error); - return; - } - } - { QQmlPropertyCacheCreator propertyCacheCreator(&m_compiledData->propertyCaches, engine, m_compiledData, &m_importCache); QQmlCompileError error = propertyCacheCreator.buildMetaObjects(); @@ -2231,21 +2226,41 @@ void QQmlTypeData::done() } } + QQmlRefPointer importCache; + QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache; + { + QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache); + if (error.isSet()) { + setError(error); + return; + } + } + + QQmlEngine *const engine = typeLoader()->engine(); + + // verify if any dependencies changed if we're using a cache + if (m_document.isNull() && !m_compiledData->verifyChecksum(engine, resolvedTypeCache)) { + if (!loadFromSource()) + return; + m_backupSourceCode.clear(); + m_compiledData = nullptr; + } + if (!m_document.isNull()) { // Compile component - compile(); + compile(importCache, resolvedTypeCache); } else { - rebuildTypeAndPropertyCaches(); + createTypeAndPropertyCaches(importCache, resolvedTypeCache); } if (isError()) return; { - QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine); { // Sanity check property bindings - QQmlPropertyValidator validator(engine, m_importCache, m_compiledData); + QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData); QVector errors = validator.validate(); if (!errors.isEmpty()) { setError(errors); @@ -2253,7 +2268,7 @@ void QQmlTypeData::done() } } - m_compiledData->finalize(engine); + m_compiledData->finalize(enginePrivate); } { @@ -2336,19 +2351,43 @@ bool QQmlTypeData::loadImplicitImport() void QQmlTypeData::dataReceived(const Data &data) { + QString error; + m_backupSourceCode = data.readAll(&error, &m_sourceTimeStamp); + // if we failed to read the source code, process it _after_ we've tried + // to use the disk cache, in order to support scenarios where the source + // was removed deliberately. + if (tryLoadFromDiskCache()) return; - qint64 sourceTimeStamp; - QString error; - QString code = QString::fromUtf8(data.readAll(&error, &sourceTimeStamp)); + if (isError()) + return; + if (!error.isEmpty()) { setError(error); return; } + + if (!loadFromSource()) + return; + + continueLoadFromIR(); +} + +void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit) +{ QQmlEngine *qmlEngine = typeLoader()->engine(); m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0)); - m_document->jsModule.sourceTimeStamp = sourceTimeStamp; + unit->loadIR(m_document.data(), unit); + continueLoadFromIR(); +} + +bool QQmlTypeData::loadFromSource() +{ + QString code = QString::fromUtf8(m_backupSourceCode); + QQmlEngine *qmlEngine = typeLoader()->engine(); + m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0)); + m_document->jsModule.sourceTimeStamp = m_sourceTimeStamp; QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) { QList errors; @@ -2362,18 +2401,9 @@ void QQmlTypeData::dataReceived(const Data &data) errors << e; } setError(errors); - return; + return false; } - - continueLoadFromIR(); -} - -void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit) -{ - QQmlEngine *qmlEngine = typeLoader()->engine(); - m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0)); - unit->loadIR(m_document.data(), unit); - continueLoadFromIR(); + return true; } void QQmlTypeData::continueLoadFromIR() @@ -2468,18 +2498,10 @@ QString QQmlTypeData::stringAt(int index) const return m_document->jsGenerator.stringTable.stringForIndex(index); } -void QQmlTypeData::compile() +void QQmlTypeData::compile(const QQmlRefPointer &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) { Q_ASSERT(m_compiledData.isNull()); - QQmlRefPointer importCache; - QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache; - QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache); - if (error.isSet()) { - setError(error); - return; - } - QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), this, m_document.data(), importCache, resolvedTypeCache); m_compiledData = compiler.compile(); if (!m_compiledData) { diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 5f754df1fc..cfbaa2e92b 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -442,18 +442,24 @@ protected: private: bool tryLoadFromDiskCache(); + bool loadFromSource(); void continueLoadFromIR(); void resolveTypes(); QQmlCompileError buildTypeResolutionCaches( QQmlRefPointer *importCache, QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache ) const; - void compile(); - void rebuildTypeAndPropertyCaches(); + void compile(const QQmlRefPointer &importCache, + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); + void createTypeAndPropertyCaches(const QQmlRefPointer &importCache, + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref); virtual void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace); + + qint64 m_sourceTimeStamp = 0; + QByteArray m_backupSourceCode; // used when cache verification fails. QScopedPointer m_document; QV4::CompiledData::TypeReferenceMap m_typeReferences; diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 1fe63bb99a..a4e10205bd 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -48,8 +48,40 @@ private slots: void regenerateAfterChange(); void registerImportForImplicitComponent(); void basicVersionChecks(); + void recompileAfterChange(); }; +// A wrapper around QQmlComponent to ensure the temporary reference counts +// on the type data as a result of the main thread <> loader thread communication +// are dropped. Regular Synchronous loading will leave us with an event posted +// to the gui thread and an extra refcount that will only be dropped after the +// event delivery. A plain sendPostedEvents() however is insufficient because +// we can't be sure that the event is posted after the constructor finished. +class CleanlyLoadingComponent : public QQmlComponent +{ +public: + CleanlyLoadingComponent(QQmlEngine *engine, const QUrl &url) + : QQmlComponent(engine, url, QQmlComponent::Asynchronous) + { waitForLoad(); } + CleanlyLoadingComponent(QQmlEngine *engine, const QString &fileName) + : QQmlComponent(engine, fileName, QQmlComponent::Asynchronous) + { waitForLoad(); } + + void waitForLoad() + { + QTRY_VERIFY(status() == QQmlComponent::Ready || status() == QQmlComponent::Error); + } +}; + +static void waitForFileSystem() +{ + // On macOS with HFS+ the precision of file times is measured in seconds, so to ensure that + // the newly written file has a modification date newer than an existing cache file, we must + // wait. + // Similar effects of lacking precision have been observed on some Linux systems. + QThread::sleep(1); +} + struct TestCompiler { TestCompiler(QQmlEngine *engine) @@ -67,10 +99,8 @@ struct TestCompiler closeMapping(); engine->clearComponentCache(); - // Qt API limits the precision of QFileInfo::modificationTime() to seconds, so to ensure that - // the newly written file has a modification date newer than an existing cache file, we must - // wait. - QThread::sleep(1); + waitForFileSystem(); + { QFile f(testFilePath); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { @@ -83,7 +113,7 @@ struct TestCompiler } } - QQmlComponent component(engine, testFilePath); + CleanlyLoadingComponent component(engine, testFilePath); if (!component.isReady()) { lastErrorString = component.errorString(); return false; @@ -321,6 +351,88 @@ void tst_qmldiskcache::basicVersionChecks() } } +class TypeVersion1 : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged); +public: + + + int m_value = 0; + int value() const { return m_value; } + void setValue(int v) { m_value = v; emit valueChanged(); } + +signals: + void valueChanged(); +}; + +// Same as TypeVersion1 except the property type changed! +class TypeVersion2 : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged); +public: + + + QString m_value; + QString value() const { return m_value; } + void setValue(QString v) { m_value = v; emit valueChanged(); } + +signals: + void valueChanged(); +}; + +void tst_qmldiskcache::recompileAfterChange() +{ + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import TypeTest 1.0\n" + "TypeThatWillChange {\n" + "}"); + + qmlRegisterType("TypeTest", 1, 0, "TypeThatWillChange"); + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString)); + } + + QDateTime initialCacheTimeStamp = QFileInfo(testCompiler.cacheFilePath).lastModified(); + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer obj(qobject_cast(component.create())); + QVERIFY(!obj.isNull()); + QCOMPARE(QFileInfo(testCompiler.cacheFilePath).lastModified(), initialCacheTimeStamp); + } + + engine.clearComponentCache(); + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer obj(qobject_cast(component.create())); + QVERIFY(!obj.isNull()); + QCOMPARE(QFileInfo(testCompiler.cacheFilePath).lastModified(), initialCacheTimeStamp); + } + + engine.clearComponentCache(); + qmlClearTypeRegistrations(); + qmlRegisterType("TypeTest", 1, 0, "TypeThatWillChange"); + + waitForFileSystem(); + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer obj(qobject_cast(component.create())); + QVERIFY(!obj.isNull()); + QVERIFY(QFileInfo(testCompiler.cacheFilePath).lastModified() > initialCacheTimeStamp); + } +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" -- cgit v1.2.3 From 01575a22ea2d2e54ef1748733929243ca024234b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 1 Aug 2016 15:57:53 +0200 Subject: Fix interaction of files selectors with disk caching Make sure to save the .qmlc/.jsc files to the location determined by the file selectors. Change-Id: If535bb1e4f0d20ac692b3d8e6d563f77cd00446b Reviewed-by: Ulf Hermann --- src/qml/compiler/qv4compileddata.cpp | 3 +- src/qml/compiler/qv4compileddata_p.h | 2 +- src/qml/qml/qqmltypeloader.cpp | 4 +- tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 52 ++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index a4fffedbcd..cb5c14ebc2 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -320,7 +320,7 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine, sizeof(data->dependencyMD5Checksum)) == 0; } -bool CompilationUnit::saveToDisk(QString *errorString) +bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) { errorString->clear(); @@ -329,7 +329,6 @@ bool CompilationUnit::saveToDisk(QString *errorString) return false; } - const QUrl unitUrl = url(); if (!unitUrl.isLocalFile()) { *errorString = QStringLiteral("File has to be a local file."); return false; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index c79f4cf007..d6537bbab3 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -904,7 +904,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount void destroy() Q_DECL_OVERRIDE; - bool saveToDisk(QString *errorString); + bool saveToDisk(const QUrl &unitUrl, QString *errorString); bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString); protected: diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 538e630e98..0e1f9572e0 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2510,7 +2510,7 @@ void QQmlTypeData::compile(const QQmlRefPointer &importCache, } if (diskCache() || forceDiskCacheRefresh()) { QString errorString; - if (!m_compiledData->saveToDisk(&errorString)) { + if (!m_compiledData->saveToDisk(url(), &errorString)) { qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString; } } @@ -2944,7 +2944,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) if (diskCache() || forceDiskCacheRefresh()) { QString errorString; - if (!unit->saveToDisk(&errorString)) { + if (!unit->saveToDisk(url(), &errorString)) { qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString; } } diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index a4e10205bd..ca47aae92e 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include class tst_qmldiskcache: public QObject @@ -49,6 +50,7 @@ private slots: void registerImportForImplicitComponent(); void basicVersionChecks(); void recompileAfterChange(); + void fileSelectors(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -433,6 +435,56 @@ void tst_qmldiskcache::recompileAfterChange() } } +void tst_qmldiskcache::fileSelectors() +{ + QQmlEngine engine; + + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const QString testFilePath = tempDir.path() + "/test.qml"; + { + QFile f(testFilePath); + QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString())); + f.write(QByteArrayLiteral("import QtQml 2.0\nQtObject { property int value: 42 }")); + } + + const QString selector = QStringLiteral("testSelector"); + const QString selectorPath = tempDir.path() + "/+" + selector; + const QString selectedTestFilePath = selectorPath + "/test.qml"; + { + QVERIFY(QDir::root().mkpath(selectorPath)); + QFile f(selectorPath + "/test.qml"); + QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString())); + f.write(QByteArrayLiteral("import QtQml 2.0\nQtObject { property int value: 100 }")); + } + + { + QQmlComponent component(&engine, testFilePath); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 42); + + QFile cacheFile(testFilePath + "c"); + QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); + } + + QQmlFileSelector qmlSelector(&engine); + qmlSelector.setExtraSelectors(QStringList() << selector); + + engine.clearComponentCache(); + + { + QQmlComponent component(&engine, testFilePath); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 100); + + QFile cacheFile(selectedTestFilePath + "c"); + QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); + } +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" -- cgit v1.2.3 From 234e762265f0b538e65d610751b8488cb596824d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jul 2016 15:38:01 +0200 Subject: Use on-disk compilation unit if available If we succeeded in saving the compilation unit to disk, then attempt to use it right away. This replaces the C++ heap usage for the compilation unit data as well as the anonymous allocated executable memory with file-backed mmap'ed memory. That means the memory can be discarded when overall availability is low and paged in on-demand. Change-Id: Ide1b1e11752d861eb049a99a26ca12cec5e2502e Reviewed-by: Ulf Hermann --- src/qml/compiler/qv4compileddata.cpp | 2 ++ src/qml/compiler/qv4compileddata_p.h | 6 ++++-- src/qml/compiler/qv4isel_moth.cpp | 5 ++--- src/qml/jit/qv4assembler.cpp | 5 ++--- src/qml/qml/qqmltypeloader.cpp | 10 ++++++++-- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 9 +++++++-- 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index cb5c14ebc2..98eb7188cc 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -424,6 +424,7 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory return false; } + const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; QScopedValueRollback dataPtrChange(data, reinterpret_cast(cacheData)); { @@ -448,6 +449,7 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory return false; dataPtrChange.commit(); + free(const_cast(oldDataPtr)); backingFile.reset(cacheFile.take()); return true; } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index d6537bbab3..639c79f79b 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -666,8 +666,10 @@ struct Unit return QString(); #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN const QChar *characters = reinterpret_cast(str + 1); - if (flags & StaticData) - return QString::fromRawData(characters, str->size); + // Too risky to do this while we unmap disk backed compilation but keep pointers to string + // data in the identifier tables. + // if (flags & StaticData) + // return QString::fromRawData(characters, str->size); return QString(characters, str->size); #else const LEUInt16 *characters = reinterpret_cast(str + 1); diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 41790c04a9..c9143b6857 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -1665,8 +1665,7 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit bool CompilationUnit::memoryMapCode(QString *errorString) { Q_UNUSED(errorString); - Q_ASSERT(codeRefs.isEmpty()); - codeRefs.reserve(data->functionTableSize); + codeRefs.resize(data->functionTableSize); const char *basePtr = reinterpret_cast(data); @@ -1680,7 +1679,7 @@ bool CompilationUnit::memoryMapCode(QString *errorString) #else QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); #endif - codeRefs.append(code); + codeRefs[i] = code; } return true; diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 625f5b5e5a..9c839936c2 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -129,8 +129,7 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit bool CompilationUnit::memoryMapCode(QString *errorString) { Q_UNUSED(errorString); - Q_ASSERT(codeRefs.isEmpty()); - codeRefs.reserve(data->functionTableSize); + codeRefs.resize(data->functionTableSize); const char *basePtr = reinterpret_cast(data); @@ -139,7 +138,7 @@ bool CompilationUnit::memoryMapCode(QString *errorString) void *codePtr = const_cast(reinterpret_cast(basePtr + compiledFunction->codeOffset)); JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr)); JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize); - codeRefs.append(codeRef); + codeRefs[i] = codeRef; } return true; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 0e1f9572e0..a6cac8d8b5 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2502,7 +2502,8 @@ void QQmlTypeData::compile(const QQmlRefPointer &importCache, { Q_ASSERT(m_compiledData.isNull()); - QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), this, m_document.data(), importCache, resolvedTypeCache); + QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine()); + QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), importCache, resolvedTypeCache); m_compiledData = compiler.compile(); if (!m_compiledData) { setError(compiler.compilationErrors()); @@ -2510,7 +2511,12 @@ void QQmlTypeData::compile(const QQmlRefPointer &importCache, } if (diskCache() || forceDiskCacheRefresh()) { QString errorString; - if (!m_compiledData->saveToDisk(url(), &errorString)) { + if (m_compiledData->saveToDisk(url(), &errorString)) { + QString error; + if (!m_compiledData->loadFromDisk(url(), enginePrivate->v4engine()->iselFactory.data(), &error)) { + // ignore error, keep using the in-memory compilation unit. + } + } else { qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString; } } diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 1a035be5e0..f32051a26a 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2118,8 +2118,13 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() QQmlTypeData *td = eng->typeLoader.getType(url); Q_ASSERT(td); - const QV4::CompiledData::Unit *qmlUnit = td->compilationUnit()->data; - Q_ASSERT(qmlUnit); + const QV4::CompiledData::Unit *readOnlyQmlUnit = td->compilationUnit()->data; + Q_ASSERT(readOnlyQmlUnit); + QV4::CompiledData::Unit *qmlUnit = reinterpret_cast(malloc(readOnlyQmlUnit->unitSize)); + memcpy(qmlUnit, readOnlyQmlUnit, readOnlyQmlUnit->unitSize); + qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; + td->compilationUnit()->data = qmlUnit; + const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject); QCOMPARE(qmlUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); quint32 i; -- cgit v1.2.3 From b65b6bd5a6d571ad7047d85508f85c62ca9ad8ce Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 6 Jul 2016 11:14:17 +0200 Subject: Fix QQuickDefaultClipNode::updateGeometry() There are segments top + border and for each segment 2 points -> 4. Change-Id: I6df11e557054e4b942de430bd2cad8e2f798b0db Task-number: QTBUG-51894 Reviewed-by: Robin Burchell Reviewed-by: Laszlo Agocs --- src/quick/items/qquickclipnode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickclipnode.cpp b/src/quick/items/qquickclipnode.cpp index a46f758c3b..3b5933d910 100644 --- a/src/quick/items/qquickclipnode.cpp +++ b/src/quick/items/qquickclipnode.cpp @@ -87,7 +87,7 @@ void QQuickDefaultClipNode::updateGeometry() int segments = qMin(30, qCeil(radius)); // Number of segments per corner. - g->allocate((segments + 1) * 2); + g->allocate((segments + 1) * 4); QVector2D *vertices = (QVector2D *)g->vertexData(); -- cgit v1.2.3 From fe663bf863ffd6085c3022db7e9a923688befeb6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 3 Aug 2016 15:46:38 +0200 Subject: Disable disk caching when debugging JavaScript The debugging changes the code generation and is currently not suitable for caching. Change-Id: I40db58a5f24457cf3383d08a1de3a4168874056f Reviewed-by: Erik Verbruggen --- src/qml/qml/qqmltypeloader.cpp | 21 +++++++++++++++------ src/qml/qml/qqmltypeloader_p.h | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index a6cac8d8b5..38715f6cd3 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1515,6 +1515,11 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob) } } +bool QQmlTypeLoader::Blob::isDebugging() const +{ + return QV8Engine::getV4(typeLoader()->engine())->debugger() != 0; +} + bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList *errors) { bool resolve = true; @@ -2071,6 +2076,9 @@ bool QQmlTypeData::tryLoadFromDiskCache() if (forceDiskCacheRefresh()) return false; + if (isDebugging()) + return false; + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine()); if (!v4) return false; @@ -2376,8 +2384,7 @@ void QQmlTypeData::dataReceived(const Data &data) void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit) { - QQmlEngine *qmlEngine = typeLoader()->engine(); - m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0)); + m_document.reset(new QmlIR::Document(isDebugging())); unit->loadIR(m_document.data(), unit); continueLoadFromIR(); } @@ -2385,9 +2392,9 @@ void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *un bool QQmlTypeData::loadFromSource() { QString code = QString::fromUtf8(m_backupSourceCode); - QQmlEngine *qmlEngine = typeLoader()->engine(); - m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0)); + m_document.reset(new QmlIR::Document(isDebugging())); m_document->jsModule.sourceTimeStamp = m_sourceTimeStamp; + QQmlEngine *qmlEngine = typeLoader()->engine(); QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) { QList errors; @@ -2509,7 +2516,9 @@ void QQmlTypeData::compile(const QQmlRefPointer &importCache, setError(compiler.compilationErrors()); return; } - if (diskCache() || forceDiskCacheRefresh()) { + + const bool trySaveToDisk = (diskCache() || forceDiskCacheRefresh()) && !m_document->jsModule.debugMode; + if (trySaveToDisk) { QString errorString; if (m_compiledData->saveToDisk(url(), &errorString)) { QString error; @@ -2914,7 +2923,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) } - QmlIR::Document irUnit(v4->debugger() != 0); + QmlIR::Document irUnit(isDebugging()); QString error; QString source = QString::fromUtf8(data.readAll(&error, &irUnit.jsModule.sourceTimeStamp)); diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index cfbaa2e92b..5c779e450d 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -248,6 +248,8 @@ public: protected: virtual QString stringAt(int) const { return QString(); } + bool isDebugging() const; + QQmlImports m_importCache; QHash m_unresolvedImports; QList m_qmldirs; -- cgit v1.2.3 From 48deab9b69afc8d613e2b22dacd138be7c8c51a8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Aug 2016 14:16:05 +0200 Subject: Fix QML engine in MSVC debug builds In debug builds MSVC does not do return value optimization and therefore would call the destructor and thus callback on the temporary, a rather unwanted side-effect. Let's use a different approach that is guaranteed to avoid temporaries. Change-Id: I534258754331495db38cd7960b88bf4bec38ddba Reviewed-by: Erik Verbruggen --- src/qml/qml/qqmltypeloader.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 38715f6cd3..06cabfeb65 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -121,19 +121,17 @@ namespace { ~LockHolder() { lock.unlock(); } }; - struct DeferredCall + struct Defer { std::function callback; - ~DeferredCall() { callback(); } + template + Defer(Callback &&cb) + : callback(cb) + {} + ~Defer() { callback(); } + Defer(const Defer &) = delete; + Defer &operator=(const Defer &) = delete; }; - - template - DeferredCall defer(Callback &&cb) - { - DeferredCall c; - c.callback = std::move(cb); - return c; - } } #ifndef QT_NO_NETWORK @@ -2168,7 +2166,7 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer Date: Thu, 4 Aug 2016 12:27:02 +0200 Subject: QML: Change the property data flags into a bit field This will make it easier in follow-up patches to add or remove flags. It also shrinks the flags, because each type doesn't need its own bit (as those are mutually exclusive). Change-Id: I5ba6de5f330eb20c82aa16b4467ed6c952725979 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlpropertycachecreator_p.h | 57 +++--- src/qml/jsruntime/qv4qobjectwrapper.cpp | 4 +- src/qml/qml/qqmlbinding.cpp | 4 +- src/qml/qml/qqmlproperty.cpp | 7 +- src/qml/qml/qqmlpropertycache.cpp | 147 +++++++-------- src/qml/qml/qqmlpropertycache_p.h | 259 +++++++++++++++++--------- src/qml/qml/qqmlvaluetypewrapper.cpp | 5 +- 7 files changed, 274 insertions(+), 209 deletions(-) diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index fe10085829..557a1364f1 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -341,8 +341,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj // Set up notify signals for properties - first normal, then alias for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) { - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; + auto flags = QQmlPropertyData::defaultSignalFlags(); QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); seenSignals.insert(changedSigName); @@ -351,8 +350,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj } for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) { - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; + auto flags = QQmlPropertyData::defaultSignalFlags(); QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); seenSignals.insert(changedSigName); @@ -402,10 +400,9 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj } } - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; + auto flags = QQmlPropertyData::defaultSignalFlags(); if (paramCount) - flags |= QQmlPropertyData::HasArguments; + flags.hasArguments = true; QString signalName = stringAt(s->nameIndex); if (seenSignals.contains(signalName)) @@ -419,7 +416,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj // Dynamic slots for (auto function = objectContainer->objectFunctionsBegin(obj), end = objectContainer->objectFunctionsEnd(obj); function != end; ++function) { - quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; + auto flags = QQmlPropertyData::defaultSlotFlags(); const QString slotName = stringAt(function->nameIndex); if (seenSignals.contains(slotName)) @@ -429,7 +426,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj QList parameterNames; for (auto formal = function->formalsBegin(), end = function->formalsEnd(); formal != end; ++formal) { - flags |= QQmlPropertyData::HasArguments; + flags.hasArguments = true; parameterNames << stringAt(*formal).toUtf8(); } @@ -442,16 +439,16 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj int propertyIdx = 0; for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p, ++propertyIdx) { int propertyType = 0; - quint32 propertyFlags = 0; + QQmlPropertyData::Flags propertyFlags; if (p->type == QV4::CompiledData::Property::Var) { propertyType = QMetaType::QVariant; - propertyFlags = QQmlPropertyData::IsVarProperty; + propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType; } else if (p->type < builtinTypeCount) { propertyType = builtinTypes[p->type].metaType; if (p->type == QV4::CompiledData::Property::Variant) - propertyFlags |= QQmlPropertyData::IsQVariant; + propertyFlags.type = QQmlPropertyData::Flags::QVariantType; } else { Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || p->type == QV4::CompiledData::Property::Custom); @@ -485,13 +482,13 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj } if (p->type == QV4::CompiledData::Property::Custom) - propertyFlags |= QQmlPropertyData::IsQObjectDerived; + propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType; else - propertyFlags |= QQmlPropertyData::IsQList; + propertyFlags.type = QQmlPropertyData::Flags::QListType; } if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) - propertyFlags |= QQmlPropertyData::IsWritable; + propertyFlags.isWritable = true; QString propertyName = stringAt(p->nameIndex); @@ -521,7 +518,7 @@ public: private: void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex); - void propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, quint32 *propertyFlags); + void propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, QQmlPropertyRawData::Flags *propertyFlags); void collectObjectsWithAliasesRecursively(int objectIndex, QVector *objectsWithAliases) const; @@ -623,7 +620,9 @@ inline void QQmlPropertyCacheAliasCreator::collectObjectsWithAl } template -inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, quint32 *propertyFlags) +inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias( + const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, + QQmlPropertyData::Flags *propertyFlags) { const int targetObjectIndex = objectForId(component, alias.targetObjectId); Q_ASSERT(targetObjectIndex >= 0); @@ -634,7 +633,7 @@ inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias bool writable = false; bool resettable = false; - *propertyFlags = QQmlPropertyData::IsAlias; + propertyFlags->isAlias = true; if (alias.aliasToLocalAlias) { auto targetAlias = targetObject.aliasesBegin(); @@ -652,7 +651,7 @@ inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias else *type = typeRef->compilationUnit->metaTypeId; - *propertyFlags |= QQmlPropertyData::IsQObjectDerived; + propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType; } else { int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex(); int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex(); @@ -678,27 +677,21 @@ inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias *type = QVariant::Int; } else { // Copy type flags - *propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask; + propertyFlags->copyPropertyTypeFlags(targetProperty->getFlags()); if (targetProperty->isVarProperty()) - *propertyFlags |= QQmlPropertyData::IsQVariant; + propertyFlags->type = QQmlPropertyData::Flags::QVariantType; } } } - if (!(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable) - *propertyFlags |= QQmlPropertyData::IsWritable; - else - *propertyFlags &= ~QQmlPropertyData::IsWritable; - - if (resettable) - *propertyFlags |= QQmlPropertyData::IsResettable; - else - *propertyFlags &= ~QQmlPropertyData::IsResettable; + propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable; + propertyFlags->isResettable = resettable; } template -inline void QQmlPropertyCacheAliasCreator::appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex) +inline void QQmlPropertyCacheAliasCreator::appendAliasesToPropertyCache( + const CompiledObject &component, int objectIndex) { const CompiledObject &object = *objectContainer->objectAt(objectIndex); if (!object.aliasCount()) @@ -715,7 +708,7 @@ inline void QQmlPropertyCacheAliasCreator::appendAliasesToPrope Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); int type = 0; - quint32 propertyFlags = 0; + QQmlPropertyData::Flags propertyFlags; propertyDataForAlias(component, *alias, &type, &propertyFlags); const QString propertyName = objectContainer->stringAt(alias->nameIndex); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index ce0c7bc04a..074d3bf866 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1344,7 +1344,7 @@ static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object, QByteArray methodName = method.name(); for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) { if (methodName == mo->method(ii).name()) { - dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload); + dummy.setOverload(true); dummy.overrideIndexIsProperty = 0; dummy.overrideIndex = ii; return &dummy; @@ -1865,7 +1865,7 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const const int methodOffset = mo->methodOffset(); for (int ii = d()->index - 1; ii >= methodOffset; --ii) { if (methodName == mo->method(ii).name()) { - method.setFlags(method.getFlags() | QQmlPropertyData::IsOverload); + method.setOverload(true); method.overrideIndexIsProperty = 0; method.overrideIndex = ii; break; diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index c96e5b661d..d4a8b87aaa 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -525,7 +525,7 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(pd.propType); Q_ASSERT(valueTypeMetaObject); QMetaProperty vtProp = valueTypeMetaObject->property(valueTypeIndex); - pd.setFlags(pd.getFlags() | QQmlPropertyData::IsValueTypeVirtual); + pd.setAsValueTypeVirtual(); pd.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); pd.valueTypePropType = vtProp.userType(); pd.valueTypeCoreIndex = valueTypeIndex; @@ -553,7 +553,7 @@ QQmlPropertyData QQmlBinding::getPropertyData() const const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d.propType); Q_ASSERT(valueTypeMetaObject); QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex()); - d.setFlags(d.getFlags() | QQmlPropertyData::IsValueTypeVirtual); + d.setAsValueTypeVirtual(); d.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); d.valueTypePropType = vtProp.userType(); d.valueTypeCoreIndex = m_targetIndex.valueTypeIndex(); diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 260cf9deaa..33f3b96389 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -301,13 +301,12 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) QMetaProperty vtProp = valueTypeMetaObject->property(idx); - Q_ASSERT(QQmlPropertyData::flagsForProperty(vtProp) <= QQmlPropertyData::ValueTypeFlagMask); Q_ASSERT(vtProp.userType() <= 0x0000FFFF); Q_ASSERT(idx <= 0x0000FFFF); object = currentObject; core = *property; - core.setFlags(core.getFlags() | QQmlPropertyData::IsValueTypeVirtual); + core.setAsValueTypeVirtual(); core.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); core.valueTypePropType = vtProp.userType(); core.valueTypeCoreIndex = idx; @@ -1169,7 +1168,7 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, writeBack->read(object, core.coreIndex); QQmlPropertyData data = core; - data.setFlags(QQmlPropertyData::Flag(core.valueTypeFlags)); + data.setFlags(core.valueTypeFlags); data.coreIndex = core.valueTypeCoreIndex; data.propType = core.valueTypePropType; @@ -1634,7 +1633,7 @@ QQmlPropertyPrivate::saveValueType(const QQmlPropertyData &base, QMetaProperty subProp = subObject->property(subIndex); QQmlPropertyData core = base; - core.setFlags(core.getFlags() | QQmlPropertyData::IsValueTypeVirtual); + core.setAsValueTypeVirtual(); core.valueTypeFlags = QQmlPropertyData::flagsForProperty(subProp); core.valueTypeCoreIndex = subIndex; core.valueTypePropType = subProp.userType(); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index d0bdb1c54f..9ac137a315 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -86,51 +86,45 @@ static QQmlPropertyData::Flags fastFlagsForProperty(const QMetaProperty &p) { QQmlPropertyData::Flags flags; - if (p.isConstant()) - flags |= QQmlPropertyData::IsConstant; - if (p.isWritable()) - flags |= QQmlPropertyData::IsWritable; - if (p.isResettable()) - flags |= QQmlPropertyData::IsResettable; - if (p.isFinal()) - flags |= QQmlPropertyData::IsFinal; + flags.isConstant = p.isConstant(); + flags.isWritable = p.isWritable(); + flags.isResettable = p.isResettable(); + flags.isFinal = p.isFinal(); + if (p.isEnumType()) - flags |= QQmlPropertyData::IsEnumType; + flags.type = QQmlPropertyData::Flags::EnumType; return flags; } // Flags that do depend on the property's QMetaProperty::userType() and thus are slow to // load -static QQmlPropertyData::Flags flagsForPropertyType(int propType, QQmlEngine *engine) +static void flagsForPropertyType(int propType, QQmlEngine *engine, QQmlPropertyData::Flags &flags) { Q_ASSERT(propType != -1); - QQmlPropertyData::Flags flags; - if (propType == QMetaType::QObjectStar) { - flags |= QQmlPropertyData::IsQObjectDerived; + flags.type = QQmlPropertyData::Flags::QObjectDerivedType; } else if (propType == QMetaType::QVariant) { - flags |= QQmlPropertyData::IsQVariant; - } else if (propType < (int)QVariant::UserType) { + flags.type = QQmlPropertyData::Flags::QVariantType; + } else if (propType < static_cast(QVariant::UserType)) { + // nothing to do } else if (propType == qMetaTypeId()) { - flags |= QQmlPropertyData::IsQmlBinding; + flags.type = QQmlPropertyData::Flags::QmlBindingType; } else if (propType == qMetaTypeId()) { - flags |= QQmlPropertyData::IsQJSValue; + flags.type = QQmlPropertyData::Flags::QJSValueType; } else if (propType == qMetaTypeId()) { - flags |= QQmlPropertyData::IsV4Handle; + flags.type = QQmlPropertyData::Flags::V4HandleType; } else { QQmlMetaType::TypeCategory cat = engine ? QQmlEnginePrivate::get(engine)->typeCategory(propType) : QQmlMetaType::typeCategory(propType); if (cat == QQmlMetaType::Object || QMetaType::typeFlags(propType) & QMetaType::PointerToQObject) - flags |= QQmlPropertyData::IsQObjectDerived; + flags.type = QQmlPropertyData::Flags::QObjectDerivedType; else if (cat == QQmlMetaType::List) - flags |= QQmlPropertyData::IsQList; + flags.type = QQmlPropertyData::Flags::QListType; } - - return flags; } static int metaObjectSignalCount(const QMetaObject *metaObject) @@ -144,7 +138,9 @@ static int metaObjectSignalCount(const QMetaObject *metaObject) QQmlPropertyData::Flags QQmlPropertyData::flagsForProperty(const QMetaProperty &p, QQmlEngine *engine) { - return fastFlagsForProperty(p) | flagsForPropertyType(p.userType(), engine); + auto flags = fastFlagsForProperty(p); + flagsForPropertyType(p.userType(), engine, flags); + return flags; } void QQmlPropertyData::lazyLoad(const QMetaProperty &p) @@ -156,16 +152,16 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p) flags = fastFlagsForProperty(p); - int type = p.type(); + int type = static_cast(p.type()); if (type == QMetaType::QObjectStar) { propType = type; - flags |= QQmlPropertyData::IsQObjectDerived; + flags.type = Flags::QObjectDerivedType; } else if (type == QMetaType::QVariant) { propType = type; - flags |= QQmlPropertyData::IsQVariant; + flags.type = Flags::QVariantType; } else if (type == QVariant::UserType || type == -1) { propTypeName = p.typeName(); - flags |= QQmlPropertyData::NotFullyResolved; + flags.notFullyResolved = true; } else { propType = type; } @@ -176,7 +172,8 @@ void QQmlPropertyData::load(const QMetaProperty &p, QQmlEngine *engine) propType = p.userType(); coreIndex = p.propertyIndex(); notifyIndex = QMetaObjectPrivate::signalIndex(p.notifySignal()); - flags = fastFlagsForProperty(p) | flagsForPropertyType(propType, engine); + flags = fastFlagsForProperty(p); + flagsForPropertyType(propType, engine, flags); Q_ASSERT(p.revision() <= Q_INT16_MAX); revision = p.revision(); } @@ -188,23 +185,23 @@ void QQmlPropertyData::load(const QMetaMethod &m) propType = m.returnType(); - flags |= IsFunction; + flags.type = Flags::FunctionType; if (m.methodType() == QMetaMethod::Signal) - flags |= IsSignal; + flags.isSignal = true; else if (m.methodType() == QMetaMethod::Constructor) { - flags |= IsConstructor; + flags.isConstructor = true; propType = QMetaType::QObjectStar; } if (m.parameterCount()) { - flags |= HasArguments; + flags.hasArguments = true; if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { - flags |= IsV4Function; + flags.isV4Function = true; } } if (m.attributes() & QMetaMethod::Cloned) - flags |= IsCloned; + flags.isCloned = true; Q_ASSERT(m.revision() <= Q_INT16_MAX); revision = m.revision(); @@ -215,11 +212,11 @@ void QQmlPropertyData::lazyLoad(const QMetaMethod &m) coreIndex = m.methodIndex(); propType = QMetaType::Void; arguments = 0; - flags |= IsFunction; + flags.type = Flags::FunctionType; if (m.methodType() == QMetaMethod::Signal) - flags |= IsSignal; + flags.isSignal = true; else if (m.methodType() == QMetaMethod::Constructor) { - flags |= IsConstructor; + flags.isConstructor = true; propType = QMetaType::QObjectStar; } @@ -228,19 +225,19 @@ void QQmlPropertyData::lazyLoad(const QMetaMethod &m) returnType = "\0"; if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) { propTypeName = returnType; - flags |= NotFullyResolved; + flags.notFullyResolved = true; } const int paramCount = m.parameterCount(); if (paramCount) { - flags |= HasArguments; + flags.hasArguments = true; if ((paramCount == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { - flags |= IsV4Function; + flags.isV4Function = true; } } if (m.attributes() & QMetaMethod::Cloned) - flags |= IsCloned; + flags.isCloned = true; Q_ASSERT(m.revision() <= Q_INT16_MAX); revision = m.revision(); @@ -342,8 +339,8 @@ QQmlPropertyCache *QQmlPropertyCache::copyAndReserve(int propertyCount, int meth \a notifyIndex MUST be in the signal index range (see QObjectPrivate::signalIndex()). This is different from QMetaMethod::methodIndex(). */ -void QQmlPropertyCache::appendProperty(const QString &name, - quint32 flags, int coreIndex, int propType, int notifyIndex) +void QQmlPropertyCache::appendProperty(const QString &name, QQmlPropertyData::Flags flags, + int coreIndex, int propType, int notifyIndex) { QQmlPropertyData data; data.propType = propType; @@ -361,8 +358,9 @@ void QQmlPropertyCache::appendProperty(const QString &name, setNamedProperty(name, index + propertyOffset(), propertyIndexCache.data() + index, (old != 0)); } -void QQmlPropertyCache::appendSignal(const QString &name, quint32 flags, int coreIndex, - const int *types, const QList &names) +void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flags flags, + int coreIndex, const int *types, + const QList &names) { QQmlPropertyData data; data.propType = QVariant::Invalid; @@ -371,7 +369,7 @@ void QQmlPropertyCache::appendSignal(const QString &name, quint32 flags, int cor data.arguments = 0; QQmlPropertyData handler = data; - handler.flags |= QQmlPropertyData::IsSignalHandler; + handler.flags.isSignalHandler = true; if (types) { int argumentCount = *types; @@ -398,8 +396,8 @@ void QQmlPropertyCache::appendSignal(const QString &name, quint32 flags, int cor setNamedProperty(handlerName, signalHandlerIndex + signalOffset(), signalHandlerIndexCache.data() + signalHandlerIndex, (old != 0)); } -void QQmlPropertyCache::appendMethod(const QString &name, quint32 flags, int coreIndex, - const QList &names) +void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flags flags, + int coreIndex, const QList &names) { int argumentCount = names.count(); @@ -457,19 +455,19 @@ void QQmlPropertyCache::setParent(QQmlPropertyCache *newParent) QQmlPropertyCache * QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject, - QQmlPropertyData::Flag propertyFlags, - QQmlPropertyData::Flag methodFlags, - QQmlPropertyData::Flag signalFlags) + QQmlPropertyData::Flags propertyFlags, + QQmlPropertyData::Flags methodFlags, + QQmlPropertyData::Flags signalFlags) { return copyAndAppend(metaObject, -1, propertyFlags, methodFlags, signalFlags); } QQmlPropertyCache * QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject, - int revision, - QQmlPropertyData::Flag propertyFlags, - QQmlPropertyData::Flag methodFlags, - QQmlPropertyData::Flag signalFlags) + int revision, + QQmlPropertyData::Flags propertyFlags, + QQmlPropertyData::Flags methodFlags, + QQmlPropertyData::Flags signalFlags) { Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4); @@ -486,10 +484,10 @@ QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject, } void QQmlPropertyCache::append(const QMetaObject *metaObject, - int revision, - QQmlPropertyData::Flag propertyFlags, - QQmlPropertyData::Flag methodFlags, - QQmlPropertyData::Flag signalFlags) + int revision, + QQmlPropertyData::Flags propertyFlags, + QQmlPropertyData::Flags methodFlags, + QQmlPropertyData::Flags signalFlags) { Q_UNUSED(revision); @@ -571,15 +569,13 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QQmlPropertyData *data = &methodIndexCache[ii - methodIndexCacheStart]; QQmlPropertyData *sigdata = 0; - data->lazyLoad(m); - - if (data->isSignal()) - data->flags |= signalFlags; + if (m.methodType() == QMetaMethod::Signal) + data->flags = signalFlags; else - data->flags |= methodFlags; + data->flags = methodFlags; - if (!dynamicMetaObject) - data->flags |= QQmlPropertyData::IsDirect; + data->lazyLoad(m); + data->flags.isDirect = !dynamicMetaObject; Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); data->metaObjectOffset = allowedRevisionCache.count() - 1; @@ -587,7 +583,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, if (data->isSignal()) { sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart]; *sigdata = *data; - sigdata->flags |= QQmlPropertyData::IsSignalHandler; + sigdata->flags.isSignalHandler = true; } QQmlPropertyData *old = 0; @@ -629,7 +625,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, if (old) { // We only overload methods in the same class, exactly like C++ if (old->isFunction() && old->coreIndex >= methodOffset) - data->flags |= QQmlPropertyData::IsOverload; + data->flags.isOverload = true; data->markAsOverrideOf(old); } @@ -656,11 +652,10 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart]; + data->flags = propertyFlags; data->lazyLoad(p); - data->flags |= propertyFlags; - if (!dynamicMetaObject) - data->flags |= QQmlPropertyData::IsDirect; + data->flags.isDirect = !dynamicMetaObject; Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); data->metaObjectOffset = allowedRevisionCache.count() - 1; @@ -685,7 +680,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision == 0)); if (accessorProperty) { - data->flags |= QQmlPropertyData::HasAccessors; + data->flags.hasAccessors = true; data->accessors = accessorProperty->accessors; } else if (old) { data->markAsOverrideOf(old); @@ -721,10 +716,10 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const data->propType = registerResult == -1 ? QMetaType::UnknownType : registerResult; } } - data->flags |= flagsForPropertyType(data->propType, engine->qmlEngine()); + flagsForPropertyType(data->propType, engine->qmlEngine(), data->flags); } - data->flags &= ~QQmlPropertyData::NotFullyResolved; + data->flags.notFullyResolved = false; } void QQmlPropertyCache::updateRecur(const QMetaObject *metaObject) @@ -881,7 +876,7 @@ QString QQmlPropertyData::name(const QMetaObject *metaObject) const if (!metaObject || coreIndex == -1) return QString(); - if (flags & IsFunction) { + if (isFunction()) { QMetaMethod m = metaObject->method(coreIndex); return QString::fromUtf8(m.name().constData()); @@ -896,7 +891,7 @@ void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor) overrideIndexIsProperty = !predecessor->isFunction(); overrideIndex = predecessor->coreIndex; - predecessor->flags |= QQmlPropertyData::IsOverridden; + predecessor->flags.isOverridden = true; } QStringList QQmlPropertyCache::propertyNames() const diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 52e0fdc3bd..bc2e338348 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -84,91 +84,96 @@ template class QQmlPropertyCacheAliasCreator; class QQmlPropertyRawData { public: - enum Flag { - NoFlags = 0x00000000, - ValueTypeFlagMask = 0x0000FFFF, // Flags in valueTypeFlags must fit in this mask + struct Flags { + enum Types { + OtherType = 0, + FunctionType = 1, // Is an invokable + QObjectDerivedType = 2, // Property type is a QObject* derived type + EnumType = 3, // Property type is an enum + QListType = 4, // Property type is a QML list + QmlBindingType = 5, // Property type is a QQmlBinding* + QJSValueType = 6, // Property type is a QScriptValue + V4HandleType = 7, // Property type is a QQmlV4Handle + VarPropertyType = 8, // Property type is a "var" property of VMEMO + ValueTypeVirtualType = 9, // Property is a value type "virtual" property + QVariantType = 10 // Property is a QVariant + }; // Can apply to all properties, except IsFunction - IsConstant = 0x00000001, // Has CONST flag - IsWritable = 0x00000002, // Has WRITE function - IsResettable = 0x00000004, // Has RESET function - IsAlias = 0x00000008, // Is a QML alias to another property - IsFinal = 0x00000010, // Has FINAL flag - IsOverridden = 0x00000020, // Is overridden by a extension property - IsDirect = 0x00000040, // Exists on a C++ QMetaObject - HasAccessors = 0x00000080, // Has property accessors - - // These are mutualy exclusive - IsFunction = 0x00000100, // Is an invokable - IsQObjectDerived = 0x00000200, // Property type is a QObject* derived type - IsEnumType = 0x00000400, // Property type is an enum - IsQList = 0x00000800, // Property type is a QML list - IsQmlBinding = 0x00001000, // Property type is a QQmlBinding* - IsQJSValue = 0x00002000, // Property type is a QScriptValue - IsV4Handle = 0x00004000, // Property type is a QQmlV4Handle - IsVarProperty = 0x00008000, // Property type is a "var" property of VMEMO - IsValueTypeVirtual = 0x00010000, // Property is a value type "virtual" property - IsQVariant = 0x00020000, // Property is a QVariant + unsigned isConstant : 1; // Has CONST flag + unsigned isWritable : 1; // Has WRITE function + unsigned isResettable : 1; // Has RESET function + unsigned isAlias : 1; // Is a QML alias to another property + unsigned isFinal : 1; // Has FINAL flag + unsigned isOverridden : 1; // Is overridden by a extension property + unsigned isDirect : 1; // Exists on a C++ QMetaObject + unsigned hasAccessors : 1; // Has property accessors + + unsigned type : 4; // stores an entry of Types // Apply only to IsFunctions - IsVMEFunction = 0x00040000, // Function was added by QML - HasArguments = 0x00080000, // Function takes arguments - IsSignal = 0x00100000, // Function is a signal - IsVMESignal = 0x00200000, // Signal was added by QML - IsV4Function = 0x00400000, // Function takes QQmlV4Function* args - IsSignalHandler = 0x00800000, // Function is a signal handler - IsOverload = 0x01000000, // Function is an overload of another function - IsCloned = 0x02000000, // The function was marked as cloned - IsConstructor = 0x04000000, // The function was marked is a constructor + unsigned isVMEFunction : 1; // Function was added by QML + unsigned hasArguments : 1; // Function takes arguments + unsigned isSignal : 1; // Function is a signal + unsigned isVMESignal : 1; // Signal was added by QML + unsigned isV4Function : 1; // Function takes QQmlV4Function* args + unsigned isSignalHandler : 1; // Function is a signal handler + unsigned isOverload : 1; // Function is an overload of another function + unsigned isCloned : 1; // The function was marked as cloned + unsigned isConstructor : 1; // The function was marked is a constructor // Internal QQmlPropertyCache flags - NotFullyResolved = 0x08000000, // True if the type data is to be lazily resolved + unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved - // Flags that are set based on the propType field - PropTypeFlagMask = IsQObjectDerived | IsEnumType | IsQList | IsQmlBinding | IsQJSValue | - IsV4Handle | IsQVariant, + unsigned _padding : 10; // align to 32 bits + + inline Flags(); + inline bool operator==(const Flags &other) const; + inline void copyPropertyTypeFlags(Flags from); }; - Q_DECLARE_FLAGS(Flags, Flag) - Flags getFlags() const { return Flag(flags); } + Flags getFlags() const { return flags; } void setFlags(Flags f) { flags = f; } bool isValid() const { return coreIndex != -1; } - bool isConstant() const { return flags & IsConstant; } - bool isWritable() const { return flags & IsWritable; } - bool isResettable() const { return flags & IsResettable; } - bool isAlias() const { return flags & IsAlias; } - bool isFinal() const { return flags & IsFinal; } - bool isOverridden() const { return flags & IsOverridden; } - bool isDirect() const { return flags & IsDirect; } - bool hasAccessors() const { return flags & HasAccessors; } - bool isFunction() const { return flags & IsFunction; } - bool isQObject() const { return flags & IsQObjectDerived; } - bool isEnum() const { return flags & IsEnumType; } - bool isQList() const { return flags & IsQList; } - bool isQmlBinding() const { return flags & IsQmlBinding; } - bool isQJSValue() const { return flags & IsQJSValue; } - bool isV4Handle() const { return flags & IsV4Handle; } - bool isVarProperty() const { return flags & IsVarProperty; } - bool isValueTypeVirtual() const { return flags & IsValueTypeVirtual; } - bool isQVariant() const { return flags & IsQVariant; } - bool isVMEFunction() const { return flags & IsVMEFunction; } - bool hasArguments() const { return flags & HasArguments; } - bool isSignal() const { return flags & IsSignal; } - bool isVMESignal() const { return flags & IsVMESignal; } - bool isV4Function() const { return flags & IsV4Function; } - bool isSignalHandler() const { return flags & IsSignalHandler; } - bool isOverload() const { return flags & IsOverload; } - bool isCloned() const { return flags & IsCloned; } - bool isConstructor() const { return flags & IsConstructor; } - - bool hasOverride() const { return !(flags & IsValueTypeVirtual) && - !(flags & HasAccessors) && + bool isConstant() const { return flags.isConstant; } + bool isWritable() const { return flags.isWritable; } + void setWritable(bool onoff) { flags.isWritable = onoff; } + bool isResettable() const { return flags.isResettable; } + bool isAlias() const { return flags.isAlias; } + bool isFinal() const { return flags.isFinal; } + bool isOverridden() const { return flags.isOverridden; } + bool isDirect() const { return flags.isDirect; } + bool hasAccessors() const { return flags.hasAccessors; } + bool isFunction() const { return flags.type == Flags::FunctionType; } + bool isQObject() const { return flags.type == Flags::QObjectDerivedType; } + bool isEnum() const { return flags.type == Flags::EnumType; } + bool isQList() const { return flags.type == Flags::QListType; } + bool isQmlBinding() const { return flags.type == Flags::QmlBindingType; } + bool isQJSValue() const { return flags.type == Flags::QJSValueType; } + bool isV4Handle() const { return flags.type == Flags::V4HandleType; } + bool isVarProperty() const { return flags.type == Flags::VarPropertyType; } + bool isValueTypeVirtual() const { return flags.type == Flags::ValueTypeVirtualType; } + void setAsValueTypeVirtual() { flags.type = Flags::ValueTypeVirtualType; } + bool isQVariant() const { return flags.type == Flags::QVariantType; } + bool isVMEFunction() const { return flags.isVMEFunction; } + bool hasArguments() const { return flags.hasArguments; } + bool isSignal() const { return flags.isSignal; } + bool isVMESignal() const { return flags.isVMESignal; } + bool isV4Function() const { return flags.isV4Function; } + bool isSignalHandler() const { return flags.isSignalHandler; } + bool isOverload() const { return flags.isOverload; } + void setOverload(bool onoff) { flags.isOverload = onoff; } + bool isCloned() const { return flags.isCloned; } + bool isConstructor() const { return flags.isConstructor; } + + bool hasOverride() const { return flags.type != Flags::ValueTypeVirtualType && + !(flags.hasAccessors) && overrideIndex >= 0; } - bool hasRevision() const { return !(flags & HasAccessors) && revision != 0; } + bool hasRevision() const { return !(flags.hasAccessors) && revision != 0; } - bool isFullyResolved() const { return !(flags & NotFullyResolved); } + bool isFullyResolved() const { return !flags.notFullyResolved; } // Returns -1 if not a value type virtual property inline int getValueTypeCoreIndex() const; @@ -195,8 +200,6 @@ public: union { struct { // When IsValueTypeVirtual - quint16 valueTypeFlags; // flags of the access property on the value type proxy - // object quint16 valueTypePropType; // The QVariant::Type of access property on the value // type proxy object quint16 valueTypeCoreIndex; // The prop index of the access property on the value @@ -215,12 +218,13 @@ public: }; int coreIndex; + Flags valueTypeFlags; // flags of the access property on the value type proxy + // object private: friend class QQmlPropertyData; friend class QQmlPropertyCache; - quint32 flags; + Flags flags; }; -Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyRawData::Flags) class QQmlPropertyData : public QQmlPropertyRawData { @@ -272,11 +276,28 @@ public: return true; } + static Flags defaultSignalFlags() + { + Flags f; + f.isSignal = true; + f.type = Flags::FunctionType; + f.isVMESignal = true; + return f; + } + + static Flags defaultSlotFlags() + { + Flags f; + f.type = Flags::FunctionType; + f.isVMEFunction = true; + return f; + } + private: friend class QQmlPropertyCache; void lazyLoad(const QMetaProperty &); void lazyLoad(const QMetaMethod &); - bool notFullyResolved() const { return flags & NotFullyResolved; } + bool notFullyResolved() const { return flags.notFullyResolved; } }; class QQmlPropertyCacheMethodArguments; @@ -295,21 +316,21 @@ public: QQmlPropertyCache *copy(); QQmlPropertyCache *copyAndAppend(const QMetaObject *, - QQmlPropertyData::Flag propertyFlags = QQmlPropertyData::NoFlags, - QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags, - QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags); + QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(), + QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(), + QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags()); QQmlPropertyCache *copyAndAppend(const QMetaObject *, int revision, - QQmlPropertyData::Flag propertyFlags = QQmlPropertyData::NoFlags, - QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags, - QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags); + QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(), + QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(), + QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags()); QQmlPropertyCache *copyAndReserve(int propertyCount, int methodCount, int signalCount); - void appendProperty(const QString &, - quint32 flags, int coreIndex, int propType, int notifyIndex); - void appendSignal(const QString &, quint32, int coreIndex, const int *types = 0, - const QList &names = QList()); - void appendMethod(const QString &, quint32 flags, int coreIndex, + void appendProperty(const QString &, QQmlPropertyRawData::Flags flags, int coreIndex, + int propType, int notifyIndex); + void appendSignal(const QString &, QQmlPropertyRawData::Flags, int coreIndex, + const int *types = 0, const QList &names = QList()); + void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex, const QList &names = QList()); const QMetaObject *metaObject() const; @@ -382,9 +403,9 @@ private: inline QQmlPropertyCache *copy(int reserve); void append(const QMetaObject *, int revision, - QQmlPropertyData::Flag propertyFlags = QQmlPropertyData::NoFlags, - QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags, - QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags); + QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyRawData::Flags(), + QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(), + QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags()); QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList &names); @@ -518,6 +539,65 @@ public: int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const; }; +QQmlPropertyRawData::Flags::Flags() + : isConstant(false) + , isWritable(false) + , isResettable(false) + , isAlias(false) + , isFinal(false) + , isOverridden(false) + , isDirect(false) + , hasAccessors(false) + , type(OtherType) + , isVMEFunction(false) + , hasArguments(false) + , isSignal(false) + , isVMESignal(false) + , isV4Function(false) + , isSignalHandler(false) + , isOverload(false) + , isCloned(false) + , isConstructor(false) + , notFullyResolved(false) + , _padding(0) +{} + +bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &other) const +{ + return isConstant == other.isConstant && + isWritable == other.isWritable && + isResettable == other.isResettable && + isAlias == other.isAlias && + isFinal == other.isFinal && + isOverridden == other.isOverridden && + hasAccessors == other.hasAccessors && + type == other.type && + isVMEFunction == other.isVMEFunction && + hasArguments == other.hasArguments && + isSignal == other.isSignal && + isVMESignal == other.isVMESignal && + isV4Function == other.isV4Function && + isSignalHandler == other.isSignalHandler && + isOverload == other.isOverload && + isCloned == other.isCloned && + isConstructor == other.isConstructor && + notFullyResolved == other.notFullyResolved; +} + +void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flags from) +{ + switch (from.type) { + case QObjectDerivedType: + case EnumType: + case QListType: + case QmlBindingType: + case QJSValueType: + case V4HandleType: + case QVariantType: + type = from.type; + } +} + QQmlPropertyData::QQmlPropertyData() { propType = 0; @@ -527,7 +607,6 @@ QQmlPropertyData::QQmlPropertyData() overrideIndex = -1; revision = 0; metaObjectOffset = -1; - flags = 0; } QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d) diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index e39acaf168..a1bf692f19 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -448,11 +448,10 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QQmlContextData *context = v4->callingQmlContext(); QQmlPropertyData cacheData; - cacheData.setFlags(QQmlPropertyData::IsWritable | - QQmlPropertyData::IsValueTypeVirtual); + cacheData.setWritable(true); + cacheData.setAsValueTypeVirtual(); cacheData.propType = writeBackPropertyType; cacheData.coreIndex = reference->d()->property; - cacheData.valueTypeFlags = 0; cacheData.valueTypeCoreIndex = pd->coreIndex; cacheData.valueTypePropType = property.userType(); -- cgit v1.2.3 From ebf07c3f68415099132856b2831633c310bc3395 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 14 Apr 2015 14:55:24 +0200 Subject: Flickable: avoid infinite velocity during release after drag It sometimes happens on touchscreens that mouse events occur too close together. We cannot calculate velocity based on zero elapsed time, so just ignore the event. Task-number: QTBUG-45527 Change-Id: I120e73cfa60e2fcc594cb1f3b69f530e746abddd Reviewed-by: Timur Pocheptsov --- src/quick/items/qquickflickable.cpp | 8 ++++++-- tests/auto/qmltest/listview/tst_listview.qml | 4 ++++ tests/auto/quick/qquickflickable/tst_qquickflickable.cpp | 1 + .../tst_qquickmultipointtoucharea.cpp | 8 ++++++++ tests/auto/quick/touchmouse/tst_touchmouse.cpp | 13 +++++++++++++ 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index b0980cd2c1..760eeed452 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1224,13 +1224,17 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) return; qint64 currentTimestamp = computeCurrentTime(event); - qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.; QVector2D deltas = QVector2D(event->localPos() - pressPos); bool overThreshold = false; QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event); // TODO guarantee that events always have velocity so that it never needs to be computed here - if (!(QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)) + if (!(QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)) { + qint64 lastTimestamp = (lastPos.isNull() ? lastPressTime : lastPosTime); + if (currentTimestamp == lastTimestamp) + return; // events are too close together: velocity would be infinite + qreal elapsed = qreal(currentTimestamp - lastTimestamp) / 1000.; velocity = QVector2D(event->localPos() - (lastPos.isNull() ? pressPos : lastPos)) / elapsed; + } if (q->yflick()) overThreshold |= QQuickWindowPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, event); diff --git a/tests/auto/qmltest/listview/tst_listview.qml b/tests/auto/qmltest/listview/tst_listview.qml index a3cae7fce2..ef9f73e005 100644 --- a/tests/auto/qmltest/listview/tst_listview.qml +++ b/tests/auto/qmltest/listview/tst_listview.qml @@ -304,9 +304,13 @@ Item { function test_listInteractiveCurrentIndexEnforce() { mousePress(listInteractiveCurrentIndexEnforce, 10, 50); + wait(1); // because Flickable pays attention to velocity, we need some time between movements mouseMove(listInteractiveCurrentIndexEnforce, 10, 40); + wait(1); mouseMove(listInteractiveCurrentIndexEnforce, 10, 30); + wait(1); mouseMove(listInteractiveCurrentIndexEnforce, 10, 20); + wait(1); mouseMove(listInteractiveCurrentIndexEnforce, 10, 10); compare(listInteractiveCurrentIndexEnforce.interactive, false); mouseRelease(listInteractiveCurrentIndexEnforce, 10, 10); diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 2e134ff5ad..294f7069c0 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -1466,6 +1466,7 @@ void tst_qquickflickable::flickWithTouch(QQuickWindow *window, QTouchDevice *tou for (int i = 1; i <= 8; ++i) { QTest::touchEvent(window, touchDevice).move(0, from + i*diff/8, window); QQuickTouchUtils::flush(window); + QTest::qWait(1); // because Flickable pays attention to velocity, we need some time between movements } QTest::touchEvent(window, touchDevice).release(0, to, window); QQuickTouchUtils::flush(window); diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 2f432e57bc..d3b576e092 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -601,18 +601,22 @@ void tst_QQuickMultiPointTouchArea::inFlickable() QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); + QTest::qWait(1); // because Flickable pays attention to velocity, we need some time between movements QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); + QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); + QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); + QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); @@ -788,18 +792,22 @@ void tst_QQuickMultiPointTouchArea::inFlickable2() QCOMPARE(point11->pressed(), true); p1 += QPoint(0,15); + QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); + QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); + QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); + QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index e14b7d7c84..0608af7cd4 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -591,10 +591,13 @@ void tst_TouchMouse::buttonOnFlickable() QPoint p2 = p1 + QPoint(0, -10); QPoint p3 = p2 + QPoint(0, -10); QQuickTouchUtils::flush(window); + QTest::qWait(1); // because Flickable pays attention to velocity, we need some time between movements QTest::touchEvent(window, device).move(0, p1, window); QQuickTouchUtils::flush(window); + QTest::qWait(1); QTest::touchEvent(window, device).move(0, p2, window); QQuickTouchUtils::flush(window); + QTest::qWait(1); QTest::touchEvent(window, device).move(0, p3, window); QQuickTouchUtils::flush(window); @@ -676,10 +679,13 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() QPoint p2 = p1 + QPoint(0, -10); QPoint p3 = p2 + QPoint(0, -10); QQuickTouchUtils::flush(window); + QTest::qWait(1); QTest::touchEvent(window, device).move(0, p1, window); QQuickTouchUtils::flush(window); + QTest::qWait(1); QTest::touchEvent(window, device).move(0, p2, window); QQuickTouchUtils::flush(window); + QTest::qWait(1); QTest::touchEvent(window, device).move(0, p3, window); QQuickTouchUtils::flush(window); QVERIFY(flickable->isMovingVertically()); @@ -871,15 +877,19 @@ void tst_TouchMouse::pinchOnFlickable() QQuickTouchUtils::flush(window); QCOMPARE(rect->position(), QPointF(200.0, 200.0)); p -= QPoint(10, 0); + QTest::qWait(1); QTest::touchEvent(window, device).move(0, p, window); QQuickTouchUtils::flush(window); p -= QPoint(10, 0); + QTest::qWait(1); QTest::touchEvent(window, device).move(0, p, window); QQuickTouchUtils::flush(window); p -= QPoint(10, 0); + QTest::qWait(1); QTest::touchEvent(window, device).move(0, p, window); QQuickTouchUtils::flush(window); p -= QPoint(10, 0); + QTest::qWait(1); QTest::touchEvent(window, device).move(0, p, window); QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).release(0, p, window); @@ -1092,13 +1102,16 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() QQuickTouchUtils::flush(window); QCOMPARE(rect->position(), QPointF(200.0, 200.0)); p -= QPoint(10, 0); + QTest::qWait(1); pinchSequence.move(0, p, window).commit(); QQuickTouchUtils::flush(window); p -= QPoint(10, 0); + QTest::qWait(1); pinchSequence.move(0, p, window).commit(); QQuickTouchUtils::flush(window); QGuiApplication::processEvents(); p -= QPoint(10, 0); + QTest::qWait(1); pinchSequence.move(0, p, window).commit(); QQuickTouchUtils::flush(window); -- cgit v1.2.3 From f2d174012bcaa88e90a80ec2120182ad9e6e8f48 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Thu, 4 Aug 2016 23:03:23 +0200 Subject: tst_creation: Remove redundant widgets dependency Absolutely no need for this. Change-Id: I06ca2dab157fecf2c585b9f863d9893cd4ce7300 Reviewed-by: Simon Hausmann --- tests/benchmarks/qml/creation/creation.pro | 2 +- tests/benchmarks/qml/creation/tst_creation.cpp | 2 -- tests/benchmarks/qml/qml.pro | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/benchmarks/qml/creation/creation.pro b/tests/benchmarks/qml/creation/creation.pro index c6f0bf7e17..8f5c2edc1d 100644 --- a/tests/benchmarks/qml/creation/creation.pro +++ b/tests/benchmarks/qml/creation/creation.pro @@ -1,7 +1,7 @@ CONFIG += benchmark TEMPLATE = app TARGET = tst_creation -QT += core-private gui-private qml-private quick-private widgets testlib +QT += core-private gui-private qml-private quick-private testlib macx:CONFIG -= app_bundle SOURCES += tst_creation.cpp diff --git a/tests/benchmarks/qml/creation/tst_creation.cpp b/tests/benchmarks/qml/creation/tst_creation.cpp index 38ab577120..b6e2801c99 100644 --- a/tests/benchmarks/qml/creation/tst_creation.cpp +++ b/tests/benchmarks/qml/creation/tst_creation.cpp @@ -36,8 +36,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/tests/benchmarks/qml/qml.pro b/tests/benchmarks/qml/qml.pro index 5d48ec0067..87779bee0a 100644 --- a/tests/benchmarks/qml/qml.pro +++ b/tests/benchmarks/qml/qml.pro @@ -11,9 +11,9 @@ SUBDIRS += \ qqmlmetaproperty \ librarymetrics_performance \ # script \ ### FIXME: doesn't build - js + js \ + creation qtHaveModule(opengl): SUBDIRS += painting qquickwindow -qtHaveModule(widgets): SUBDIRS += creation include(../trusted-benchmarks.pri) -- cgit v1.2.3 From 3631c77ff01f40f83fa59a90c28d69ee9953c327 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 5 Aug 2016 00:07:45 +0200 Subject: tst_creation: Remove qobject_alloc benchmark This is literally just testing operator new and delete, with a little PLT overhead. It is not useful as a result (not a real-world scenario, and obscenely variant thanks to allocators not being predictable in behavior). Change-Id: I42f758c503b37ff880fc4f0e38c220d0638356e9 Reviewed-by: Simon Hausmann --- tests/benchmarks/qml/creation/tst_creation.cpp | 30 -------------------------- 1 file changed, 30 deletions(-) diff --git a/tests/benchmarks/qml/creation/tst_creation.cpp b/tests/benchmarks/qml/creation/tst_creation.cpp index b6e2801c99..595b8346ca 100644 --- a/tests/benchmarks/qml/creation/tst_creation.cpp +++ b/tests/benchmarks/qml/creation/tst_creation.cpp @@ -50,7 +50,6 @@ private slots: void qobject_cpp(); void qobject_qml(); void qobject_qmltype(); - void qobject_alloc(); void qobject_10flat_qml(); void qobject_10flat_cpp(); @@ -206,35 +205,6 @@ void tst_creation::qobject_qmltype() } } -struct QObjectFakeData { - char data[sizeof(QObjectPrivate)]; -}; - -struct QObjectFake { - QObjectFake(); - virtual ~QObjectFake(); -private: - QObjectFakeData *d; -}; - -QObjectFake::QObjectFake() -{ - d = new QObjectFakeData; -} - -QObjectFake::~QObjectFake() -{ - delete d; -} - -void tst_creation::qobject_alloc() -{ - QBENCHMARK { - QObjectFake *obj = new QObjectFake; - delete obj; - } -} - struct QQmlGraphics_Derived : public QObject { void setParent_noEvent(QObject *parent) { -- cgit v1.2.3 From e0e591fb80ab86f6044e5140ddbbc979c0c8d380 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 5 Aug 2016 00:22:27 +0200 Subject: tst_creation: Remove tst_creation::elements There is no real sense in testing what boils down to operator new/delete plus a little PLT overhead. For one thing, the allocator is too variant to test at such a level. For another, it's not representative of any real-world scenario. Change-Id: Ib455bb00839ff4e25099977059759a7b328db306 Reviewed-by: Simon Hausmann --- tests/benchmarks/qml/creation/tst_creation.cpp | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/tests/benchmarks/qml/creation/tst_creation.cpp b/tests/benchmarks/qml/creation/tst_creation.cpp index 595b8346ca..dbda07be5a 100644 --- a/tests/benchmarks/qml/creation/tst_creation.cpp +++ b/tests/benchmarks/qml/creation/tst_creation.cpp @@ -64,9 +64,6 @@ private slots: void itemtree_qml(); void itemtree_scene_cpp(); - void elements_data(); - void elements(); - void itemtests_qml_data(); void itemtests_qml(); @@ -299,28 +296,6 @@ void tst_creation::itemtree_scene_cpp() delete root; } -void tst_creation::elements_data() -{ - QTest::addColumn("type"); - - QList types = QQmlMetaType::qmlTypeNames(); - foreach (QString type, types) - QTest::newRow(type.toLatin1()) << type; -} - -void tst_creation::elements() -{ - QFETCH(QString, type); - QQmlType *t = QQmlMetaType::qmlType(type, 2, 0); - if (!t || !t->isCreatable()) - QSKIP("Non-creatable type"); - - QBENCHMARK { - QObject *obj = t->create(); - delete obj; - } -} - void tst_creation::itemtests_qml_data() { QTest::addColumn("filepath"); -- cgit v1.2.3 From fde6e1fe83e049de4441494d32bbcf88c13517a8 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 5 Aug 2016 00:28:58 +0200 Subject: benchmarks: Remove odd qqmlimage benchmark I have no idea what this was theoretically supposed to be testing, but it looks completely bogus (not representative of a real world scenario), is subject to massive variance thanks to memory allocation, and generally doesn't seem useful Change-Id: Ib7adc8a4753e49d2a3bd9515273bca79a88a5749 Reviewed-by: Simon Hausmann --- tests/benchmarks/qml/qml.pro | 1 - tests/benchmarks/qml/qqmlimage/image.png | Bin 611 -> 0 bytes tests/benchmarks/qml/qqmlimage/qqmlimage.pro | 12 --- tests/benchmarks/qml/qqmlimage/tst_qqmlimage.cpp | 100 ----------------------- 4 files changed, 113 deletions(-) delete mode 100644 tests/benchmarks/qml/qqmlimage/image.png delete mode 100644 tests/benchmarks/qml/qqmlimage/qqmlimage.pro delete mode 100644 tests/benchmarks/qml/qqmlimage/tst_qqmlimage.cpp diff --git a/tests/benchmarks/qml/qml.pro b/tests/benchmarks/qml/qml.pro index 87779bee0a..2cf2dff413 100644 --- a/tests/benchmarks/qml/qml.pro +++ b/tests/benchmarks/qml/qml.pro @@ -7,7 +7,6 @@ SUBDIRS += \ holistic \ qqmlchangeset \ qqmlcomponent \ - qqmlimage \ qqmlmetaproperty \ librarymetrics_performance \ # script \ ### FIXME: doesn't build diff --git a/tests/benchmarks/qml/qqmlimage/image.png b/tests/benchmarks/qml/qqmlimage/image.png deleted file mode 100644 index 623d36233d..0000000000 Binary files a/tests/benchmarks/qml/qqmlimage/image.png and /dev/null differ diff --git a/tests/benchmarks/qml/qqmlimage/qqmlimage.pro b/tests/benchmarks/qml/qqmlimage/qqmlimage.pro deleted file mode 100644 index dfe0027ab4..0000000000 --- a/tests/benchmarks/qml/qqmlimage/qqmlimage.pro +++ /dev/null @@ -1,12 +0,0 @@ -CONFIG += benchmark -TEMPLATE = app -TARGET = tst_qqmlimage -QT += qml quick-private testlib -macx:CONFIG -= app_bundle -CONFIG += release - -SOURCES += tst_qqmlimage.cpp - -DEFINES += SRCDIR=\\\"$$PWD\\\" - -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/benchmarks/qml/qqmlimage/tst_qqmlimage.cpp b/tests/benchmarks/qml/qqmlimage/tst_qqmlimage.cpp deleted file mode 100644 index 992ad5de67..0000000000 --- a/tests/benchmarks/qml/qqmlimage/tst_qqmlimage.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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: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 -#include -#include -#include - -class tst_qmlgraphicsimage : public QObject -{ - Q_OBJECT -public: - tst_qmlgraphicsimage() {} - -private slots: - void qmlgraphicsimage(); - void qmlgraphicsimage_file(); - void qmlgraphicsimage_url(); - -private: - QQmlEngine engine; -}; - -void tst_qmlgraphicsimage::qmlgraphicsimage() -{ - int x = 0; - QUrl url(SRCDIR "/image.png"); - QBENCHMARK { - QUrl url2("http://localhost/image" + QString::number(x++) + ".png"); - QQuickImage *image = new QQuickImage; - QQmlEngine::setContextForObject(image, engine.rootContext()); - delete image; - } -} - -void tst_qmlgraphicsimage::qmlgraphicsimage_file() -{ - int x = 0; - QUrl url(SRCDIR "/image.png"); - //get rid of initialization effects - { - QQuickImage *image = new QQuickImage; - QQmlEngine::setContextForObject(image, engine.rootContext()); - image->setSource(url); - } - QBENCHMARK { - QUrl url2("http://localhost/image" + QString::number(x++) + ".png"); - QQuickImage *image = new QQuickImage; - QQmlEngine::setContextForObject(image, engine.rootContext()); - image->setSource(url); - delete image; - } -} - -void tst_qmlgraphicsimage::qmlgraphicsimage_url() -{ - int x = 0; - QUrl url(SRCDIR "/image.png"); - QBENCHMARK { - QUrl url2("http://localhost/image" + QString::number(x++) + ".png"); - QQuickImage *image = new QQuickImage; - QQmlEngine::setContextForObject(image, engine.rootContext()); - image->setSource(url2); - delete image; - } -} - -QTEST_MAIN(tst_qmlgraphicsimage) - -#include "tst_qqmlimage.moc" -- cgit v1.2.3 From 7377e8f950d550d8823914588c35e541c48ab3ce Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 5 Aug 2016 11:41:52 +0200 Subject: tst_librarymetrics_performance: Use QBENCHMARK macro instead of rolling our own This means we now respect -callgrind to show instruction counts (for instance). If benchmarks don't already throw out outliers and perform averaging, we should roll those features into testlib, not replace it. Change-Id: I21a3c4b41ec80a49b5b61bfe957f1165ac865010 Reviewed-by: Simon Hausmann --- .../tst_librarymetrics_performance.cpp | 147 ++------------------- 1 file changed, 14 insertions(+), 133 deletions(-) diff --git a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp index ad72ebc6f0..4b69975dac 100644 --- a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp +++ b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp @@ -41,9 +41,6 @@ // for the standard set of elements, properties and expressions which // are provided in the QtDeclarative library (QtQml and QtQuick). -#define AVERAGE_OVER_N 10 -#define IGNORE_N_OUTLIERS 2 - class ModuleApi : public QObject { Q_OBJECT @@ -219,123 +216,37 @@ void tst_librarymetrics_performance::compilation() } } - QList nResults; - - // generate AVERAGE_OVER_N results - for (int i = 0; i < AVERAGE_OVER_N; ++i) { + QBENCHMARK { cleanState(&e); - { - QElapsedTimer et; - et.start(); - QQmlComponent c(e, this); - c.loadUrl(qmlfile); // just compile. - qint64 etime = et.nsecsElapsed(); - nResults.append(etime); - } - } - - // sort the list - qSort(nResults); - - // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference) - for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) { - if (!nResults.isEmpty()) nResults.removeLast(); - if (!nResults.isEmpty()) nResults.removeLast(); + QQmlComponent c(e, this); + c.loadUrl(qmlfile); // just compile. } - - // now generate an average - qint64 totaltime = 0; - if (nResults.size() == 0) nResults.append(9999); - for (int i = 0; i < nResults.size(); ++i) - totaltime += nResults.at(i); - double average = ((double)totaltime) / nResults.count(); - - // and return it as the result - QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); - QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib } void tst_librarymetrics_performance::instantiation_cached() { QFETCH(QUrl, qmlfile); - cleanState(&e); - QList nResults; - // generate AVERAGE_OVER_N results - for (int i = 0; i < AVERAGE_OVER_N; ++i) { - QElapsedTimer et; - et.start(); + QBENCHMARK { QQmlComponent c(e, this); c.loadUrl(qmlfile); // just compile. QObject *o = c.create(); - qint64 etime = et.nsecsElapsed(); - nResults.append(etime); delete o; } - - // sort the list - qSort(nResults); - - // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference) - for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) { - if (!nResults.isEmpty()) nResults.removeLast(); - if (!nResults.isEmpty()) nResults.removeLast(); - } - - // now generate an average - qint64 totaltime = 0; - if (nResults.size() == 0) nResults.append(9999); - for (int i = 0; i < nResults.size(); ++i) - totaltime += nResults.at(i); - double average = ((double)totaltime) / nResults.count(); - - // and return it as the result - QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); - QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib } void tst_librarymetrics_performance::instantiation() { QFETCH(QUrl, qmlfile); - cleanState(&e); - QList nResults; - - // generate AVERAGE_OVER_N results - for (int i = 0; i < AVERAGE_OVER_N; ++i) { + QBENCHMARK { cleanState(&e); - { - QElapsedTimer et; - et.start(); - QQmlComponent c(e, this); - c.loadUrl(qmlfile); // just compile. - QObject *o = c.create(); - qint64 etime = et.nsecsElapsed(); - nResults.append(etime); - delete o; - } - } - - // sort the list - qSort(nResults); - - // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference) - for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) { - if (!nResults.isEmpty()) nResults.removeLast(); - if (!nResults.isEmpty()) nResults.removeLast(); + QQmlComponent c(e, this); + c.loadUrl(qmlfile); // just compile. + QObject *o = c.create(); + delete o; } - - // now generate an average - qint64 totaltime = 0; - if (nResults.size() == 0) nResults.append(9999); - for (int i = 0; i < nResults.size(); ++i) - totaltime += nResults.at(i); - double average = ((double)totaltime) / nResults.count(); - - // and return it as the result - QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); - QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib } void tst_librarymetrics_performance::positioners_data() @@ -356,43 +267,13 @@ void tst_librarymetrics_performance::positioners() { QFETCH(QUrl, qmlfile); - cleanState(&e); - QList nResults; - - // generate AVERAGE_OVER_N results - for (int i = 0; i < AVERAGE_OVER_N; ++i) { + QBENCHMARK { cleanState(&e); - { - QElapsedTimer et; - et.start(); - QQmlComponent c(e, this); - c.loadUrl(qmlfile); // just compile. - QObject *o = c.create(); - qint64 etime = et.nsecsElapsed(); - nResults.append(etime); - delete o; - } - } - - // sort the list - qSort(nResults); - - // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference) - for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) { - if (!nResults.isEmpty()) nResults.removeLast(); - if (!nResults.isEmpty()) nResults.removeLast(); + QQmlComponent c(e, this); + c.loadUrl(qmlfile); // just compile. + QObject *o = c.create(); + delete o; } - - // now generate an average - qint64 totaltime = 0; - if (nResults.size() == 0) nResults.append(9999); - for (int i = 0; i < nResults.size(); ++i) - totaltime += nResults.at(i); - double average = ((double)totaltime) / nResults.count(); - - // and return it as the result - QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); - QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib } QTEST_MAIN(tst_librarymetrics_performance) -- cgit v1.2.3 From 8d1bae4c0f45ea6853d5ff63fda4f625f1e4bb83 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 3 Aug 2016 13:50:44 +0200 Subject: Enable QML/JS disk cache by default Except on Windows, where there is still one bug to fix. Change-Id: I1a22f42859733eedd37596a3e8fc09680720ff10 Reviewed-by: Erik Verbruggen --- src/qml/qml/qqmltypeloader.cpp | 25 ++++++++++++++-------- tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 2 +- tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 6 ++++++ .../tst_qquickfolderlistmodel.cpp | 10 +++++++++ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 06cabfeb65..40a2c596e3 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -103,12 +103,22 @@ #endif DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); -DEFINE_BOOL_CONFIG_OPTION(diskCache, QML_DISK_CACHE); -DEFINE_BOOL_CONFIG_OPTION(forceDiskCacheRefresh, QML_FORCE_DISK_CACHE_REFRESH); +DEFINE_BOOL_CONFIG_OPTION(_disableDiskCache, QML_DISABLE_DISK_CACHE); +DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE); Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache") +static bool disableDiskCache() +{ + return _disableDiskCache() + // ### FIXME: Fix crashes on Windows with mmap'ed code. +#if defined(Q_OS_WIN) + || true +#endif + ; +} + QT_BEGIN_NAMESPACE namespace { @@ -2068,10 +2078,7 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) bool QQmlTypeData::tryLoadFromDiskCache() { - if (!diskCache()) - return false; - - if (forceDiskCacheRefresh()) + if (disableDiskCache() && !forceDiskCache()) return false; if (isDebugging()) @@ -2515,7 +2522,7 @@ void QQmlTypeData::compile(const QQmlRefPointer &importCache, return; } - const bool trySaveToDisk = (diskCache() || forceDiskCacheRefresh()) && !m_document->jsModule.debugMode; + const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode; if (trySaveToDisk) { QString errorString; if (m_compiledData->saveToDisk(url(), &errorString)) { @@ -2909,7 +2916,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) { QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); - if (diskCache() && !forceDiskCacheRefresh()) { + if (!disableDiskCache() || forceDiskCache()) { QQmlRefPointer unit = v4->iselFactory->createUnitForLoading(); QString error; if (unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { @@ -2955,7 +2962,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) // The js unit owns the data and will free the qml unit. unit->data = unitData; - if (diskCache() || forceDiskCacheRefresh()) { + if (!disableDiskCache() || forceDiskCache()) { QString errorString; if (!unit->saveToDisk(url(), &errorString)) { qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString; diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index ca47aae92e..c3a80eebde 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -190,7 +190,7 @@ struct TestCompiler void tst_qmldiskcache::initTestCase() { - qputenv("QML_DISK_CACHE", "1"); + qputenv("QML_FORCE_DISK_CACHE", "1"); } void tst_qmldiskcache::regenerateAfterChange() diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 74e54b64e9..9c155eda5b 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -275,6 +275,12 @@ void tst_qqmlengine::clearComponentCache() // Modify qml file { + // On macOS with HFS+ the precision of file times is measured in seconds, so to ensure that + // the newly written file has a modification date newer than an existing cache file, we must + // wait. + // Similar effects of lacking precision have been observed on some Linux systems. + QThread::sleep(1); + QFile file("temp.qml"); QVERIFY(file.open(QIODevice::WriteOnly)); file.write("import QtQuick 2.0\nQtObject {\nproperty int test: 11\n}\n"); diff --git a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp index f3ba3e8971..f19e82032a 100644 --- a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp +++ b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp @@ -56,6 +56,7 @@ public slots: } private slots: + void initTestCase(); void basicProperties(); void showFiles(); void resetFiltering(); @@ -97,6 +98,15 @@ void tst_qquickfolderlistmodel::checkNoErrors(const QQmlComponent& component) QVERIFY(!component.isError()); } +void tst_qquickfolderlistmodel::initTestCase() +{ + // The tests rely on a fixed number of files in the directory with the qml files + // (the data dir), so disable the disk cache to avoid creating .qmlc files and + // confusing the test. + qputenv("QML_DISABLE_DISK_CACHE", "1"); + QQmlDataTest::initTestCase(); +} + void tst_qquickfolderlistmodel::basicProperties() { QQmlComponent component(&engine, testFileUrl("basic.qml")); -- cgit v1.2.3 From 837c5d6284eeec8adb9b2caa3a0ce82b0da019a1 Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Fri, 5 Aug 2016 13:18:44 +0200 Subject: Fix crash when cache property can not be found For some reason if a cache property is not found this cause an application crash instead of just reporting an error. Now instead of a crash we get this kind of error visible: qrc:///the_file.qml line 64 column 30: Cannot assign object to property Change-Id: Ic420713df30603f1d164da439cba30a18af8f2bc Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmltypecompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 7e4ee344ff..82332dfc35 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -1414,7 +1414,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI continue; QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType); - const QMetaObject *mo = pc->firstCppMetaObject(); + const QMetaObject *mo = pc ? pc->firstCppMetaObject() : 0; while (mo) { if (mo == &QQmlComponent::staticMetaObject) break; -- cgit v1.2.3 From ff5e0800cea669cb9b398ea81bc0c1e0d2446f01 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 5 Aug 2016 13:22:35 +0200 Subject: Move m_synthMouseEvent into TouchPointerEvent The mouse pointer event doesn't need it. Change-Id: I68999f79a0d979cf414b11a2e6b4863df1a1e817 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 6dee1f5b45..197166526c 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -327,7 +327,7 @@ public: , m_event(nullptr) , m_button(Qt::NoButton) , m_pressedButtons(Qt::NoButton) - , m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier) { } + { } virtual ~QQuickPointerEvent(); @@ -368,7 +368,6 @@ protected: QInputEvent *m_event; // original event as received by QQuickWindow Qt::MouseButton m_button; Qt::MouseButtons m_pressedButtons; - mutable QMouseEvent m_synthMouseEvent; Q_DISABLE_COPY(QQuickPointerEvent) }; @@ -404,7 +403,10 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent Q_OBJECT public: QQuickPointerTouchEvent(QObject *parent = nullptr) - : QQuickPointerEvent(parent), m_pointCount(0) { } + : QQuickPointerEvent(parent) + , m_pointCount(0) + , m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier) + { } QQuickPointerEvent *reset(QEvent *) override; bool isPressEvent() const override; @@ -426,6 +428,7 @@ public: private: int m_pointCount; QVector m_touchPoints; + mutable QMouseEvent m_synthMouseEvent; Q_DISABLE_COPY(QQuickPointerTouchEvent) }; -- cgit v1.2.3 From 51b88fad924989e7ba0a3ad7c53f55114030463f Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 5 Aug 2016 13:27:39 +0200 Subject: Remove unused function Change-Id: I604fe1470bfab831e31d129d3b734213273fc333 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 10 ---------- src/quick/items/qquickevents_p_p.h | 1 - 2 files changed, 11 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 420bbad0b1..caab5a656a 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -707,16 +707,6 @@ bool QQuickPointerTouchEvent::isPressEvent() const return static_cast(m_event)->touchPointStates() & Qt::TouchPointPressed; } -QVector QQuickPointerEvent::unacceptedPointScenePositions() const -{ - QVector points; - for (int i = 0; i < pointCount(); ++i) { - if (!point(i)->isAccepted()) - points << point(i)->scenePos(); - } - return points; -} - QVector QQuickPointerEvent::unacceptedPressedPointScenePositions() const { QVector points; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 197166526c..6e0c9d3db2 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -351,7 +351,6 @@ public: // helpers for C++ only (during event delivery) virtual bool allPointsAccepted() const = 0; bool isAccepted() { return m_event->isAccepted(); } void setAccepted(bool accepted) { m_event->setAccepted(accepted); } - QVector unacceptedPointScenePositions() const; QVector unacceptedPressedPointScenePositions() const; virtual int pointCount() const = 0; -- cgit v1.2.3 From 796b0cf366a6e6ab53fcb096e442261865ca397b Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 5 Aug 2016 13:30:06 +0200 Subject: Remove silly newlines Change-Id: I35bd1aa524705c8c6541428b2737b992de2ad05d Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 3 --- src/quick/items/qquickevents_p_p.h | 1 - 2 files changed, 4 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index caab5a656a..b6e2299f3a 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -771,9 +771,6 @@ QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickIte \fn QQuickPointerEvent::pointById(quint64 pointId) const */ - - - QQuickEventPoint *QQuickPointerMouseEvent::pointById(quint64 pointId) const { if (m_mousePoint && pointId == m_mousePoint->pointId()) return m_mousePoint; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 6e0c9d3db2..cda0e87c5e 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -362,7 +362,6 @@ public: // helpers for C++ only (during event delivery) ulong timestamp() const { return m_event->timestamp(); } protected: - QQuickPointerDevice *m_device; QInputEvent *m_event; // original event as received by QQuickWindow Qt::MouseButton m_button; -- cgit v1.2.3 From 43db88a5c5614bcab212be1e4703e001f1fbb295 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 5 Aug 2016 15:04:03 +0200 Subject: QQuickEventPoint: replace Qt::TouchPointState with State enum This allows extension later on, and makes it clear that the state is for any kind of point, not just a touchpoint. Change-Id: I1f8aaa01ea65ac1731645129fedcf7a51ee66e77 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickevents.cpp | 8 ++++---- src/quick/items/qquickevents_p_p.h | 15 ++++++++++++--- src/quick/items/qquickwindow.cpp | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index b6e2299f3a..8d8c58beaa 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -509,7 +509,7 @@ void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint6 m_pointId = pointId; m_valid = true; m_accept = false; - m_state = state; + m_state = static_cast(state); m_timestamp = timestamp; if (state == Qt::TouchPointPressed) m_pressTimestamp = timestamp; @@ -636,7 +636,7 @@ QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent) : QObject(parent), m_pointId(0), m_grabber(nullptr), m_timestamp(0), m_pressTimestamp(0), - m_state(Qt::TouchPointReleased), m_valid(false), m_accept(false) + m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false) { Q_UNUSED(m_reserved); } @@ -711,7 +711,7 @@ QVector QQuickPointerEvent::unacceptedPressedPointScenePositions() cons { QVector points; for (int i = 0; i < pointCount(); ++i) { - if (!point(i)->isAccepted() && point(i)->state() == Qt::TouchPointPressed) + if (!point(i)->isAccepted() && point(i)->state() == QQuickEventPoint::Pressed) points << point(i)->scenePos(); } return points; @@ -823,7 +823,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i if (p->isAccepted()) continue; bool isGrabber = p->grabber() == item; - bool isPressInside = p->state() == Qt::TouchPointPressed && item->contains(item->mapFromScene(p->scenePos())); + bool isPressInside = p->state() == QQuickEventPoint::Pressed && item->contains(item->mapFromScene(p->scenePos())); if (!(isGrabber || isPressInside || isFiltering)) continue; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index cda0e87c5e..61bbb4ecda 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -250,13 +250,22 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject { Q_OBJECT Q_PROPERTY(QPointF scenePos READ scenePos) - Q_PROPERTY(Qt::TouchPointState state READ state) + Q_PROPERTY(State state READ state) Q_PROPERTY(quint64 pointId READ pointId) Q_PROPERTY(qreal timeHeld READ timeHeld) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) Q_PROPERTY(QQuickItem *grabber READ grabber WRITE setGrabber) public: + enum State { + Pressed = Qt::TouchPointPressed, + Updated = Qt::TouchPointMoved, + Stationary = Qt::TouchPointStationary, + Released = Qt::TouchPointReleased + // Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe + }; + Q_ENUM(State) + QQuickEventPoint(QQuickPointerEvent *parent); void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp); @@ -265,7 +274,7 @@ public: QQuickPointerEvent *pointerEvent() const; QPointF scenePos() const { return m_scenePos; } - Qt::TouchPointState state() const { return m_state; } + State state() const { return m_state; } quint64 pointId() const { return m_pointId; } bool isValid() const { return m_valid; } qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } @@ -280,7 +289,7 @@ private: QPointer m_grabber; ulong m_timestamp; ulong m_pressTimestamp; - Qt::TouchPointState m_state; + State m_state; bool m_valid : 1; bool m_accept : 1; int m_reserved : 30; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 80d867a05c..64acfa4020 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2179,7 +2179,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) int pointCount = event->pointCount(); for (int i = 0; i < pointCount; ++i) { QQuickEventPoint *point = event->point(i); - if (point->state() == Qt::TouchPointReleased) { + if (point->state() == QQuickEventPoint::Released) { int id = point->pointId(); qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released"; point->setGrabber(nullptr); -- cgit v1.2.3 From ff645deb25c18eeae3d248a6a60bc2954129ee28 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Tue, 2 Aug 2016 11:52:58 +0200 Subject: Remove artifacts in font rendering The distancefield cache did not clear the textures before using them. Hence, random values could leak through in the edges of the distancefields, leading to random pixels at the edges of the rendered glyphs. This issue was rarely visible before, because of the way the glyphs were stacked on the textures. That stacking was changed as a result of 7190aa26f65ab97b4f54c156a107ed7748a11df5, which made the issue happen more often, so it was detected by lancelot. Change-Id: Ibe7a20dd7ba557ab92966e714c25a100e218ed24 Reviewed-by: Yoann Lopes --- src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index 728d8b6541..31275b304d 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -253,7 +253,8 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int const GLenum format = GL_ALPHA; #endif - m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, 0); + QByteArray zeroBuf(width * height, 0); + m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, zeroBuf.constData()); texInfo->size = QSize(width, height); -- cgit v1.2.3 From c83c9a1f7ac7ff322f696d1119d4e9c06620e716 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 5 Aug 2016 13:56:45 +0200 Subject: Invalidate QQuickPointerEvents after delivery We must not hold on to events after delivery, so better set them to nullptr. On the other hand the grabbers need to be persistent, but that's achieved since the points are copied anyway. Change-Id: I0c01ee2ec6c9238c6594f0b7e2a9533185070667 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 16 ++++++++++++---- src/quick/items/qquickwindow.cpp | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 8d8c58beaa..bc9e5d0ad2 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -563,10 +563,14 @@ void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong times QQuickPointerEvent::~QQuickPointerEvent() {} -QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) { +QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) +{ auto ev = static_cast(event); - m_device = QQuickPointerDevice::genericMouseDevice(); m_event = ev; + if (!event) + return this; + + m_device = QQuickPointerDevice::genericMouseDevice(); m_button = ev->button(); m_pressedButtons = ev->buttons(); Qt::TouchPointState state = Qt::TouchPointStationary; @@ -588,10 +592,14 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) { return this; } -QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) { +QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) +{ auto ev = static_cast(event); - m_device = QQuickPointerDevice::touchDevice(ev->device()); m_event = ev; + if (!event) + return this; + + m_device = QQuickPointerDevice::touchDevice(ev->device()); m_button = Qt::NoButton; m_pressedButtons = Qt::NoButton; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 64acfa4020..172311d997 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2109,6 +2109,8 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) Q_ASSERT(false); } + event->reset(nullptr); + --pointerEventRecursionGuard; } -- cgit v1.2.3 From 1f4bfc099b7e48e2cc3dd9488b7b42a96122b299 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 4 Aug 2016 13:35:19 +0200 Subject: QQuickWindowPrivate::deliverTouchAsMouse: don't pre-grab Change-Id: I127d3e6dafbe4207941aef3f507b25c2cb7a251b Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 172311d997..ec0c50f09d 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -644,8 +644,6 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (!item->contains(pos)) break; - auto pointerEventPoint = pointerEvent->pointById(p.id()); - pointerEventPoint->setGrabber(item); qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item; QScopedPointer mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); @@ -657,7 +655,8 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve touchMouseId = p.id(); if (!q->mouseGrabberItem()) item->grabMouse(); - item->grabTouchPoints(QVector() << touchMouseId); + auto pointerEventPoint = pointerEvent->pointById(p.id()); + pointerEventPoint->setGrabber(item); if (checkIfDoubleClicked(event->timestamp())) { QScopedPointer mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); -- cgit v1.2.3 From 68bfc9332cd65c1eb88d1ec87164447b0db43237 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 25 Jul 2016 16:07:16 +0200 Subject: Fix crash with Component.onDestruction A call to a handler of Component.onDestruction may end up causing WeakValues such as QQmlData::jsWrapper to be set again, even though they've been set to undefined in an earlier iteration of the loop that walks through the weak references. That in turn may result in invalid object references to objects that are scheduled for destruction by the collector. So after calling all destroy handlers for QObjects, reset all of the weak values again. Task-number: QTBUG-54939 Change-Id: I00ebabb76274e296fb1bd90d8d3e21dbbb920b57 Reviewed-by: Lars Knoll --- src/qml/memory/qv4mm.cpp | 13 ++- .../qml/qqmlecmascript/data/DestructionHelper.qml | 3 + .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 102 +++++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/DestructionHelper.qml diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 7ab6d15041..4592dd5c9b 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -428,7 +428,7 @@ void MemoryManager::sweep(bool lastSweep) Managed *m = (*it).as(); if (m->markBit()) continue; - // we need to call detroyObject on qobjectwrappers now, so that they can emit the destroyed + // we need to call destroyObject on qobjectwrappers now, so that they can emit the destroyed // signal before we start sweeping the heap if (QObjectWrapper *qobjectWrapper = (*it).as()) qobjectWrapper->destroyObject(lastSweep); @@ -436,6 +436,17 @@ void MemoryManager::sweep(bool lastSweep) (*it) = Primitive::undefinedValue(); } + // onDestruction handlers may have accessed other QObject wrappers and reset their value, so ensure + // that they are all set to undefined. + for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { + if (!(*it).isManaged()) + continue; + Managed *m = (*it).as(); + if (m->markBit()) + continue; + (*it) = Primitive::undefinedValue(); + } + // Now it is time to free QV4::QObjectWrapper Value, we must check the Value's tag to make sure its object has been destroyed const int pendingCount = m_pendingFreedObjectWrapperValue.count(); if (pendingCount) { diff --git a/tests/auto/qml/qqmlecmascript/data/DestructionHelper.qml b/tests/auto/qml/qqmlecmascript/data/DestructionHelper.qml new file mode 100644 index 0000000000..8bb0a3554e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/DestructionHelper.qml @@ -0,0 +1,3 @@ +import Test 1.0 +WeakReferenceMutator { +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index b3b31e1a73..d65cec843c 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -49,6 +49,8 @@ #include #include #include +#include +#include #ifdef Q_CC_MSVC #define NO_INLINE __declspec(noinline) @@ -287,6 +289,7 @@ private slots: void replaceBinding(); void deleteRootObjectInCreation(); void onDestruction(); + void onDestructionViaGC(); void bindingSuppression(); void signalEmitted(); void threadSignal(); @@ -7157,6 +7160,105 @@ void tst_qqmlecmascript::onDestruction() } } +class WeakReferenceMutator : public QObject +{ + Q_OBJECT +public: + WeakReferenceMutator() + : resultPtr(Q_NULLPTR) + , weakRef(Q_NULLPTR) + {} + + void init(QV4::ExecutionEngine *v4, QV4::WeakValue *weakRef, bool *resultPtr) + { + QV4::QObjectWrapper::wrap(v4, this); + QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); + + this->resultPtr = resultPtr; + this->weakRef = weakRef; + + QObject::connect(QQmlComponent::qmlAttachedProperties(this), &QQmlComponentAttached::destruction, this, &WeakReferenceMutator::reviveFirstWeakReference); + } + +private slots: + void reviveFirstWeakReference() { + *resultPtr = weakRef->valueRef() && weakRef->isNullOrUndefined(); + if (!*resultPtr) + return; + QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine(this)); + weakRef->set(v4, v4->newObject()); + *resultPtr = weakRef->valueRef() && !weakRef->isNullOrUndefined(); + } + +public: + bool *resultPtr; + + QV4::WeakValue *weakRef; +}; + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { +struct WeakReferenceSentinel : public Object { + WeakReferenceSentinel(WeakValue *weakRef, bool *resultPtr) + : weakRef(weakRef) + , resultPtr(resultPtr) { + + } + + ~WeakReferenceSentinel() { + *resultPtr = weakRef->isNullOrUndefined(); + } + + WeakValue *weakRef; + bool *resultPtr; +}; +} // namespace Heap + +struct WeakReferenceSentinel : public Object { + V4_OBJECT2(WeakReferenceSentinel, Object) + V4_NEEDS_DESTROY +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +DEFINE_OBJECT_VTABLE(QV4::WeakReferenceSentinel); + +void tst_qqmlecmascript::onDestructionViaGC() +{ + qmlRegisterType("Test", 1, 0, "WeakReferenceMutator"); + + QQmlEngine engine; + QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); + + QQmlComponent component(&engine, testFileUrl("DestructionHelper.qml")); + + QScopedPointer weakRef; + + bool mutatorResult = false; + bool sentinelResult = false; + + { + weakRef.reset(new QV4::WeakValue); + weakRef->set(v4, v4->newObject()); + QVERIFY(!weakRef->isNullOrUndefined()); + + QPointer weakReferenceMutator = qobject_cast(component.create()); + QVERIFY2(!weakReferenceMutator.isNull(), qPrintable(component.errorString())); + weakReferenceMutator->init(v4, weakRef.data(), &mutatorResult); + + v4->memoryManager->allocObject(weakRef.data(), &sentinelResult); + } + gc(engine); + + QVERIFY2(mutatorResult, "We failed to re-assign the weak reference a new value during GC"); + QVERIFY2(sentinelResult, "The weak reference was not cleared properly"); +} + struct EventProcessor : public QObject { Q_OBJECT -- cgit v1.2.3 From a3aad1a470dc9cb1feea7c6b3bf35f725a6b898d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 5 Aug 2016 21:10:17 +0200 Subject: Fix restoring of local aliases from disk cache Local aliases are always "resolved". We must be careful not to access the same field in the union otherwise and mistake it as property index. Change-Id: I6369cdba145a62dcdaa10d8f4ee84bfa3cbfa0e3 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlpropertycachecreator_p.h | 3 ++ tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 44 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 557a1364f1..a1b0a14890 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -567,6 +567,9 @@ inline void QQmlPropertyCacheAliasCreator::appendAliasPropertie const int targetObjectIndex = objectForId(component, alias->targetObjectId); Q_ASSERT(targetObjectIndex >= 0); + if (alias->aliasToLocalAlias) + continue; + if (alias->encodedMetaPropertyIndex == -1) continue; diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index c3a80eebde..dd23dfca07 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -51,6 +51,7 @@ private slots: void basicVersionChecks(); void recompileAfterChange(); void fileSelectors(); + void localAliases(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -485,6 +486,49 @@ void tst_qmldiskcache::fileSelectors() } } +void tst_qmldiskcache::localAliases() +{ + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n" + "QtObject {\n" + " id: root\n" + " property int prop: 100\n" + " property alias dummy1: root.prop\n" + " property alias dummy2: root.prop\n" + " property alias dummy3: root.prop\n" + " property alias dummy4: root.prop\n" + " property alias dummy5: root.prop\n" + " property alias foo: root.prop\n" + " property alias bar: root.foo\n" + "}"); + + { + testCompiler.clearCache(); + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString)); + } + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("bar").toInt(), 100); + } + + engine.clearComponentCache(); + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("bar").toInt(), 100); + } +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" -- cgit v1.2.3 From f9f4f1cbda1b4ac19819f21dc3af33f0b7ebf071 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 8 Aug 2016 10:05:32 +0200 Subject: QML: fix inconsistent-missing-override warning Change-Id: I3399f1ce4abb8cb4b78c5a7a218321f308e916fb Reviewed-by: Simon Hausmann --- src/qml/jit/qv4assembler_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index d76a21c74c..e29b165c2d 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -84,7 +84,7 @@ struct CompilationUnit : public QV4::CompiledData::CompilationUnit void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; - bool memoryMapCode(QString *errorString); + bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; // Coderef + execution engine -- cgit v1.2.3 From 13a427475d3638de843f33145378587037841a86 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 5 Aug 2016 12:33:03 +0200 Subject: Clarify doc on modification of the global object in JS imported by QML Currently, reading the documentation for modification of the global object in JavaScript can be confusing. http://doc.qt.io/qt-5/qtqml-javascript-hostenvironment.html says: JavaScript code cannot modify the global object. In QML, the global object is constant - existing properties cannot be modified or deleted, and no new properties may be created. ... Any attempt to modify the global object - either implicitly or explicitly - will cause an exception. If uncaught, this will result in a warning being printed, that includes the file and line number of the offending code. http://doc.qt.io/qt-5/qjsengine.html#globalObject says: Returns this engine's Global Object. By default, the Global Object contains the built-in objects that are part of ECMA-262, such as Math, Date and String. Additionally, you can set properties of the Global Object to make your own extensions available to all script code. Non-local variables in script code will be created as properties of the Global Object, as well as local variables in global code. If QQmlEngine "is-a" QJSEngine, and QJSEngine can have its global object modified, it might seem reasonable to expect that imported JavaScript code should be able to modify the global object. This patch aims to be more explicit about the restrictions and give examples of how libraries should expose their APIs correctly for use by QML code. Change-Id: I11beb894a88d52038be90ffe6baa9337943810db Reviewed-by: Simon Hausmann --- src/qml/doc/src/javascript/hostenvironment.qdoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index de8b967d72..1e33f2f641 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -80,7 +80,10 @@ Note that QML makes the following modifications to native objects: QML implements the following restrictions for JavaScript code: \list -\li JavaScript code cannot modify the global object. +\li JavaScript code written in a \c .qml file cannot modify the global object. + JavaScript code in a .js file can modify the global object, + and those modifications will be visible to the .qml file when + \l {Importing a JavaScript Resource from a QML Document}{imported}. In QML, the global object is constant - existing properties cannot be modified or deleted, and no new properties may be created. -- cgit v1.2.3 From 72e6195925f5390670d78f115a405971162cb4eb Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 4 Aug 2016 10:59:36 +0200 Subject: Remove impossible condition We don't pre-grab any more. The first if checks for touchMouseId==-1. Then we only set touchMouseId if the event was accepted. Change-Id: I507c72930af958732ec8f4ef8ea2750ce3ea8f2d Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ec0c50f09d..16cbb2aa2f 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -670,9 +670,6 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve return true; } - // The event was not accepted but touchMouseId was set. - if (touchMouseId != -1) - return false; // try the next point // Touch point was there before and moved -- cgit v1.2.3 From 076640e0e7ac31574c41bd6298a89b0cb5d7f9a7 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 5 Aug 2016 14:56:56 +0200 Subject: Handle press events without release gracefully In the old code, touch point grabbers would be ignored in case of a press without release for a point. To make sure not to break existing bad test code or clash with broken devices, warn, but reset the grabber for new press events. Change-Id: Ia76ed4d3ee32c983c451f760837e83f8d90dab41 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index bc9e5d0ad2..59faafa023 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -624,7 +624,13 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) for (int i = 0; i < newPointCount; ++i) { auto point = m_touchPoints.at(i); point->reset(tps.at(i), ev->timestamp()); - point->setGrabber(grabbers.at(i)); + if (point->state() == QQuickEventPoint::Pressed) { + if (grabbers.at(i)) + qWarning() << "TouchPointPressed without previous release event" << point; + point->setGrabber(nullptr); + } else { + point->setGrabber(grabbers.at(i)); + } } m_pointCount = newPointCount; return this; -- cgit v1.2.3 From 3e5152be4100cbe649d252bb5d95dc98be5df2f0 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 5 Aug 2016 10:02:52 +0200 Subject: QJSValue: Adapt to introduction of QMetaType::Nullptr [ChangeLog][QtQml][Important Behavior Changes] A JS null value converted to a QVariant now has type QMetaType::Nullptr rather than QMetaType::VoidStar. Change-Id: I91a64e444ada0f1884fe807f9973348ba1a878ff Reviewed-by: Simon Hausmann --- src/qml/jsapi/qjsvalue.cpp | 11 +++++++---- src/qml/jsapi/qjsvalue_p.h | 1 + src/qml/jsruntime/qv4engine.cpp | 4 +++- src/qml/jsruntime/qv4qobjectwrapper.cpp | 1 + src/qml/qml/qqmlbinding.cpp | 9 +++++---- src/qml/qml/qqmlvaluetype.cpp | 1 + tests/auto/qml/qjsengine/tst_qjsengine.cpp | 1 + tests/auto/qml/qjsvalue/tst_qjsvalue.cpp | 8 ++++---- tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 2 +- 9 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index e5c1dcdb80..a4a96a96a7 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -178,7 +178,7 @@ QJSValue::QJSValue(SpecialValue value) : d(0) { if (value == NullValue) - QJSValuePrivate::setVariant(this, QVariant(QMetaType::VoidStar, (void *)0)); + QJSValuePrivate::setVariant(this, QVariant::fromValue(nullptr)); } /*! @@ -293,7 +293,10 @@ bool QJSValue::isNull() const if (val) return val->isNull(); QVariant *variant = QJSValuePrivate::getVariant(this); - return variant && variant->userType() == QMetaType::VoidStar; + if (!variant) + return false; + const int type = variant->userType(); + return type == QMetaType::Nullptr || type == QMetaType::VoidStar; } /*! @@ -582,7 +585,7 @@ quint32 QJSValue::toUInt() const \table \header \li Input Type \li Result \row \li Undefined \li An invalid QVariant. - \row \li Null \li A QVariant containing a null pointer (QMetaType::VoidStar). + \row \li Null \li A QVariant containing a null pointer (QMetaType::Nullptr). \row \li Boolean \li A QVariant containing the value of the boolean. \row \li Number \li A QVariant containing the value of the number. \row \li String \li A QVariant containing the value of the string. @@ -619,7 +622,7 @@ QVariant QJSValue::toVariant() const return QVariant(val->asDouble()); } if (val->isNull()) - return QVariant(QMetaType::VoidStar, 0); + return QVariant(QMetaType::Nullptr, 0); Q_ASSERT(val->isUndefined()); return QVariant(); } diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index 25afd9275c..c4761ad6ea 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -132,6 +132,7 @@ public: case QMetaType::Void: *v = QV4::Encode::undefined(); break; + case QMetaType::Nullptr: case QMetaType::VoidStar: *v = QV4::Encode::null(); break; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index f5bae4b258..20294700f6 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1130,7 +1130,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (value.isUndefined()) return QVariant(); if (value.isNull()) - return QVariant(QMetaType::VoidStar, (void *)0); + return QVariant::fromValue(nullptr); if (value.isBoolean()) return value.booleanValue(); if (value.isInteger()) @@ -1255,6 +1255,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) case QMetaType::UnknownType: case QMetaType::Void: return QV4::Encode::undefined(); + case QMetaType::Nullptr: case QMetaType::VoidStar: return QV4::Encode::null(); case QMetaType::Bool: @@ -1426,6 +1427,7 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) case QMetaType::UnknownType: case QMetaType::Void: return QV4::Encode::undefined(); + case QMetaType::Nullptr: case QMetaType::VoidStar: return QV4::Encode::null(); case QMetaType::Bool: diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 074d3bf866..9cfc5dcb1f 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1247,6 +1247,7 @@ static int MatchScore(const QV4::Value &actual, int conversionType) } } else if (actual.isNull()) { switch (conversionType) { + case QMetaType::Nullptr: case QMetaType::VoidStar: case QMetaType::QObjectStar: case QMetaType::QJsonValue: diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index d4a8b87aaa..5c9b14ba74 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -386,7 +386,8 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const Q const char *valueType = 0; const char *propertyType = 0; - if (value.userType() == QMetaType::QObjectStar) { + const int userType = value.userType(); + if (userType == QMetaType::QObjectStar) { if (QObject *o = *(QObject *const *)value.constData()) { valueType = o->metaObject()->className(); @@ -394,11 +395,11 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const Q if (!propertyMetaObject.isNull()) propertyType = propertyMetaObject.className(); } - } else if (value.userType() != QVariant::Invalid) { - if (value.userType() == QMetaType::VoidStar) + } else if (userType != QVariant::Invalid) { + if (userType == QMetaType::Nullptr || userType == QMetaType::VoidStar) valueType = "null"; else - valueType = QMetaType::typeName(value.userType()); + valueType = QMetaType::typeName(userType); } if (!valueType) diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 8e87ec7f63..eefe7a5fb8 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -88,6 +88,7 @@ bool QQmlValueTypeFactoryImpl::isValueType(int idx) && idx != QVariant::StringList && idx != QMetaType::QObjectStar && idx != QMetaType::VoidStar + && idx != QMetaType::Nullptr && idx != QMetaType::QVariant && idx != QMetaType::QLocale) { return true; diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 3ceeb97718..cd55ae191d 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -1444,6 +1444,7 @@ void tst_QJSEngine::valueConversion_QVariant() QCOMPARE(qjsvalue_cast(QJSValue(123)), QVariant(123)); QVERIFY(eng.toScriptValue(QVariant(QMetaType::VoidStar, 0)).isNull()); + QVERIFY(eng.toScriptValue(QVariant::fromValue(nullptr)).isNull()); { QVariantMap map; diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index 859caf72c7..28b9adea38 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -978,8 +978,8 @@ void tst_QJSValue::toVariant() QCOMPARE(qjsvalue_cast(undefined), QVariant()); QJSValue null = eng.evaluate("null"); - QCOMPARE(null.toVariant(), QVariant(QMetaType::VoidStar, 0)); - QCOMPARE(qjsvalue_cast(null), QVariant(QMetaType::VoidStar, 0)); + QCOMPARE(null.toVariant(), QVariant::fromValue(nullptr)); + QCOMPARE(qjsvalue_cast(null), QVariant::fromValue(nullptr)); { QJSValue number = eng.toScriptValue(123.0); @@ -1055,8 +1055,8 @@ void tst_QJSValue::toVariant() QCOMPARE(qjsvalue_cast(undef), QVariant()); QJSValue nil = QJSValue(QJSValue::NullValue); - QCOMPARE(nil.toVariant(), QVariant(QMetaType::VoidStar, 0)); - QCOMPARE(qjsvalue_cast(nil), QVariant(QMetaType::VoidStar, 0)); + QCOMPARE(nil.toVariant(), QVariant::fromValue(nullptr)); + QCOMPARE(qjsvalue_cast(nil), QVariant::fromValue(nullptr)); } // array diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index de3ead917f..3afd761388 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -5814,7 +5814,7 @@ void tst_qqmlecmascript::variants() QVERIFY(object != 0); QCOMPARE(object->property("undefinedVariant").type(), QVariant::Invalid); - QCOMPARE(int(object->property("nullVariant").type()), int(QMetaType::VoidStar)); + QCOMPARE(int(object->property("nullVariant").type()), int(QMetaType::Nullptr)); QCOMPARE(object->property("intVariant").type(), QVariant::Int); QCOMPARE(object->property("doubleVariant").type(), QVariant::Double); -- cgit v1.2.3 From ded3fa4ba21430eaa48ca4dcfdd4f297ad8e7b4a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 22 Jul 2016 10:46:17 +0200 Subject: QML: Make use of all inline bits for bindingBits on 64bit platforms Also initialize the size of the bindingBits to the size of the inline value, so there is no need to check this every time a bit is set. Change-Id: I56be4511ceb6801d6a109a6ab78f7430124fa82b Reviewed-by: Simon Hausmann --- src/qml/qml/qqmldata_p.h | 37 ++++++++++++++++++++++++------------- src/qml/qml/qqmlengine.cpp | 40 ++++++++++++++++++++-------------------- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index d9a69a9ca3..dce75c51aa 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -129,14 +129,16 @@ public: quint32 parentFrozen:1; quint32 dummy:21; - // When bindingBitsSize < 32, we store the binding bit flags inside - // bindingBitsValue. When we need more than 32 bits, we allocated + // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside + // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated // sufficient space and use bindingBits to point to it. int bindingBitsSize; + typedef quintptr BindingBitsType; union { - quint32 *bindingBits; - quint32 bindingBitsValue; + BindingBitsType *bindingBits; + BindingBitsType bindingBitsValue; }; + enum { MaxInlineBits = sizeof(BindingBitsType) * 8 }; struct NotifyList { quint64 connectionMask; @@ -236,6 +238,17 @@ private: mutable QQmlDataExtended *extendedData; void flushPendingBindingImpl(QQmlPropertyIndex index); + + Q_ALWAYS_INLINE bool hasBitSet(int bit) const + { + if (bindingBitsSize <= bit) + return false; + + if (bindingBitsSize == MaxInlineBits) + return bindingBitsValue & (BindingBitsType(1) << bit); + else + return bindingBits[bit / MaxInlineBits] & (BindingBitsType(1) << (bit % MaxInlineBits)); + } }; bool QQmlData::wasDeleted(QObject *object) @@ -281,20 +294,18 @@ inline bool QQmlData::signalHasEndpoint(int index) const bool QQmlData::hasBindingBit(int coreIndex) const { - int bit = coreIndex * 2; + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); - return bindingBitsSize > bit && - ((bindingBitsSize == 32) ? (bindingBitsValue & (1 << bit)) : - (bindingBits[bit / 32] & (1 << (bit % 32)))); + return hasBitSet(coreIndex * 2); } -bool QQmlData::hasPendingBindingBit(int index) const +bool QQmlData::hasPendingBindingBit(int coreIndex) const { - int bit = index * 2 + 1; + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); - return bindingBitsSize > bit && - ((bindingBitsSize == 32) ? (bindingBitsValue & (1 << bit)) : - (bindingBits[bit / 32] & (1 << (bit % 32)))); + return hasBitSet(coreIndex * 2 + 1); } void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 48832f3b07..2b49eae4de 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -105,6 +105,9 @@ Q_DECLARE_METATYPE(QQmlProperty) QT_BEGIN_NAMESPACE +typedef QQmlData::BindingBitsType BindingBitsType; +enum { MaxInlineBits = QQmlData::MaxInlineBits }; + void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor) { QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor); @@ -677,7 +680,8 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) QQmlData::QQmlData() : ownedByQml1(false), ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false), hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), - hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(0), notifyList(0), context(0), outerContext(0), + hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), + bindingBitsSize(MaxInlineBits), bindingBitsValue(0), notifyList(0), context(0), outerContext(0), bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0), deferredData(0), propertyCache(0), guards(0), extendedData(0) @@ -1690,7 +1694,7 @@ void QQmlData::destroyed(QObject *object) signalHandler = next; } - if (bindingBitsSize > 32) + if (bindingBitsSize > MaxInlineBits) free(bindingBits); if (propertyCache) @@ -1740,31 +1744,27 @@ void QQmlData::parentChanged(QObject *object, QObject *parent) static void QQmlData_setBit(QQmlData *data, QObject *obj, int bit) { - if (data->bindingBitsSize == 0 && bit < 32) { - data->bindingBitsSize = 32; - } - - if (data->bindingBitsSize <= bit) { + if (Q_UNLIKELY(data->bindingBitsSize <= bit)) { int props = QQmlMetaObject(obj).propertyCount(); Q_ASSERT(bit < 2 * props); - int arraySize = (2 * props + 31) / 32; + int arraySize = (2 * props + MaxInlineBits - 1) / MaxInlineBits; Q_ASSERT(arraySize > 1); // special handling for 32 here is to make sure we wipe the first byte // when going from bindingBitsValue to bindingBits, and preserve the old // set bits so we can restore them after the allocation - int oldArraySize = data->bindingBitsSize > 32 ? data->bindingBitsSize / 32 : 0; - quint32 oldValue = data->bindingBitsSize == 32 ? data->bindingBitsValue : 0; + int oldArraySize = data->bindingBitsSize > MaxInlineBits ? data->bindingBitsSize / MaxInlineBits : 0; + quintptr oldValue = data->bindingBitsSize == MaxInlineBits ? data->bindingBitsValue : 0; - data->bindingBits = (quint32 *)realloc((data->bindingBitsSize == 32) ? 0 : data->bindingBits, - arraySize * sizeof(quint32)); + data->bindingBits = static_cast(realloc((data->bindingBitsSize == MaxInlineBits) ? 0 : data->bindingBits, + arraySize * sizeof(BindingBitsType))); memset(data->bindingBits + oldArraySize, 0x00, - sizeof(quint32) * (arraySize - oldArraySize)); + sizeof(BindingBitsType) * (arraySize - oldArraySize)); - data->bindingBitsSize = arraySize * 32; + data->bindingBitsSize = arraySize * MaxInlineBits; // reinstate bindingBitsValue after we dropped it if (oldValue) { @@ -1772,19 +1772,19 @@ static void QQmlData_setBit(QQmlData *data, QObject *obj, int bit) } } - if (data->bindingBitsSize == 32) - data->bindingBitsValue |= (1 << (bit % 32)); + if (data->bindingBitsSize == MaxInlineBits) + data->bindingBitsValue |= BindingBitsType(1) << bit; else - data->bindingBits[bit / 32] |= (1 << (bit % 32)); + data->bindingBits[bit / MaxInlineBits] |= (BindingBitsType(1) << (bit % MaxInlineBits)); } static void QQmlData_clearBit(QQmlData *data, int bit) { if (data->bindingBitsSize > bit) { - if (data->bindingBitsSize == 32) - data->bindingBitsValue &= ~(1 << (bit % 32)); + if (data->bindingBitsSize == MaxInlineBits) + data->bindingBitsValue &= ~(BindingBitsType(1) << (bit % MaxInlineBits)); else - data->bindingBits[bit / 32] &= ~(1 << (bit % 32)); + data->bindingBits[bit / MaxInlineBits] &= ~(BindingBitsType(1) << (bit % MaxInlineBits)); } } -- cgit v1.2.3 From 158914f98b872605a20fe63d57f7f423f93b8e09 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 8 Aug 2016 14:41:16 +0200 Subject: Fix crashes with attached property access We currently embed the attached object ids in the generated code, which is not stable because they come from a hash and the meta-objects of the attached types are also not part of the check-sum verification. So for now disable accelerated access in order to proceed with qtquickcontrols2 integrations. This needs to be re-enabled later with proper checking and stable lookups. Change-Id: I54a3f0c905ee26fb80f4e2687a2901a11a921b38 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder.cpp | 7 ++++++- src/qml/compiler/qv4compileddata_p.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index ed72054e1e..1bc2b30087 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1725,7 +1725,11 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, member->kind = QV4::IR::Member::MemberOfSingletonObject; return newResolver->resolveMember(qmlEngine, newResolver, member); } - } else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) { + } +#if 0 + else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) { + // Right now the attached property IDs are not stable and cannot be embedded in the + // code that is cached on disk. QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta); auto newResolver = resolver->owner->New(); newResolver->owner = resolver->owner; @@ -1733,6 +1737,7 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, member->setAttachedPropertiesId(type->attachedPropertiesId(qmlEngine)); return newResolver->resolveMember(qmlEngine, newResolver, member); } +#endif return result; } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 639c79f79b..0903654e51 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x01 +#define QV4_DATA_STRUCTURE_VERSION 0x02 class QIODevice; class QQmlPropertyCache; -- cgit v1.2.3 From 9699c569ae94c98d606625cd2cec9e38df630544 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 22 Jul 2016 15:56:02 +0300 Subject: QQmlPropertyCache: remove unused method propertyNames() Change-Id: Ibcae6b396677c5d4648f59a4c5998a08862cb033 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache.cpp | 8 -------- src/qml/qml/qqmlpropertycache_p.h | 1 - 2 files changed, 9 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 9ac137a315..b283b4128e 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -894,14 +894,6 @@ void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor) predecessor->flags.isOverridden = true; } -QStringList QQmlPropertyCache::propertyNames() const -{ - QStringList keys; - for (StringCache::ConstIterator iter = stringCache.begin(), cend = stringCache.end(); iter != cend; ++iter) - keys.append(iter.key()); - return keys; -} - struct StaticQtMetaObject : public QObject { static const QMetaObject *get() diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index bc2e338348..dfe0d02dcd 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -347,7 +347,6 @@ public: QQmlPropertyData *method(int) const; QQmlPropertyData *signal(int index) const; int methodIndexToSignalIndex(int) const; - QStringList propertyNames() const; QString defaultPropertyName() const; QQmlPropertyData *defaultProperty() const; -- cgit v1.2.3 From 4edeaa48abf8caccb62e623c32f97b0897c3167f Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 22 Jul 2016 15:47:27 +0300 Subject: QQmlPropertyCache: remove unused var Change-Id: I2d2085e2df51efcb8ea3b24127a06632eca839ff Reviewed-by: Shawn Rutledge Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index b283b4128e..31218f5e4c 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -942,7 +942,6 @@ QString QQmlPropertyCache::signalParameterStringForJS(QV4::ExecutionEngine *engi { bool unnamedParameter = false; const QSet &illegalNames = engine->v8Engine->illegalNames(); - QString error; QString parameters; for (int i = 0; i < parameterNameList.count(); ++i) { -- cgit v1.2.3 From c3cbbf362c769b065fd0586b0510d043cbae92a4 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 22 Jul 2016 15:44:53 +0300 Subject: Qml: use const (and const APIs) more For CoW types, prefer const methods to avoid needless detach()ing. Change-Id: I13e4d5b091877319984c1ddaed4da8dc59a7c0a3 Reviewed-by: Shawn Rutledge --- src/qml/qml/qqmlapplicationengine.cpp | 2 +- src/qml/qml/qqmlmetatype.cpp | 8 ++++---- src/qml/qml/qqmlopenmetaobject.cpp | 2 +- src/qml/qml/qqmlpropertycache.cpp | 6 +++--- src/qml/qml/qqmlproxymetaobject.cpp | 6 +++--- src/qml/qml/qqmltypeloader.cpp | 3 +-- src/qml/qml/qqmlxmlhttprequest.cpp | 4 ++-- 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 8c342e0592..30fc186391 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -134,7 +134,7 @@ void QQmlApplicationEnginePrivate::_q_finishLoad(QObject *o) break; case QQmlComponent::Ready: objects << c->create(); - q->objectCreated(objects.last(), c->url()); + q->objectCreated(objects.constLast(), c->url()); break; case QQmlComponent::Loading: case QQmlComponent::Null: diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 271b4f1b31..ce0f4b798a 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -634,7 +634,7 @@ void QQmlTypePrivate::init() const QMetaObject *mmo = builder.toMetaObject(); mmo->d.superdata = baseMetaObject; if (!metaObjects.isEmpty()) - metaObjects.last().metaObject->d.superdata = mmo; + metaObjects.constLast().metaObject->d.superdata = mmo; QQmlProxyMetaObject::ProxyData data = { mmo, t->d->extraData.cd->extFunc, 0, 0 }; metaObjects << data; } @@ -656,7 +656,7 @@ void QQmlTypePrivate::init() const if (metaObjects.isEmpty()) mo = baseMetaObject; else - mo = metaObjects.first().metaObject; + mo = metaObjects.constFirst().metaObject; for (int ii = 0; !containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) { if (isPropertyRevisioned(mo, ii)) @@ -851,7 +851,7 @@ const QMetaObject *QQmlType::metaObject() const if (d->metaObjects.isEmpty()) return d->baseMetaObject; else - return d->metaObjects.first().metaObject; + return d->metaObjects.constFirst().metaObject; } @@ -1861,7 +1861,7 @@ QQmlType *QQmlMetaType::qmlTypeFromIndex(int idx) if (idx < 0 || idx >= data->types.count()) return 0; - return data->types[idx]; + return data->types.at(idx); } /*! diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp index 0fd9e63bde..49f02476a2 100644 --- a/src/qml/qml/qqmlopenmetaobject.cpp +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -278,7 +278,7 @@ int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void * propertyRead(propId); *reinterpret_cast(a[0]) = d->getData(propId); } else if (c == QMetaObject::WriteProperty) { - if (propId >= d->data.count() || d->data[propId].first != *reinterpret_cast(a[0])) { + if (propId >= d->data.count() || d->data.at(propId).first != *reinterpret_cast(a[0])) { propertyWrite(propId); QPair &prop = d->getDataRef(propId); prop.first = propertyWriteValue(propId, *reinterpret_cast(a[0])); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 31218f5e4c..71b3fd8309 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -195,7 +195,7 @@ void QQmlPropertyData::load(const QMetaMethod &m) if (m.parameterCount()) { flags.hasArguments = true; - if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { + if ((m.parameterCount() == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) { flags.isV4Function = true; } } @@ -231,7 +231,7 @@ void QQmlPropertyData::lazyLoad(const QMetaMethod &m) const int paramCount = m.parameterCount(); if (paramCount) { flags.hasArguments = true; - if ((paramCount == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { + if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) { flags.isV4Function = true; } } @@ -390,7 +390,7 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag signalHandlerIndexCache.append(handler); QString handlerName = QLatin1String("on") + name; - handlerName[2] = handlerName[2].toUpper(); + handlerName[2] = handlerName.at(2).toUpper(); setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0)); setNamedProperty(handlerName, signalHandlerIndex + signalOffset(), signalHandlerIndexCache.data() + signalHandlerIndex, (old != 0)); diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp index bbaab1e155..27e3c13ff8 100644 --- a/src/qml/qml/qqmlproxymetaobject.cpp +++ b/src/qml/qml/qqmlproxymetaobject.cpp @@ -45,7 +45,7 @@ QT_BEGIN_NAMESPACE QQmlProxyMetaObject::QQmlProxyMetaObject(QObject *obj, QList *mList) : metaObjects(mList), proxies(0), parent(0), object(obj) { - *static_cast(this) = *metaObjects->first().metaObject; + *static_cast(this) = *metaObjects->constFirst().metaObject; QObjectPrivate *op = QObjectPrivate::get(obj); if (op->metaObject) @@ -71,7 +71,7 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void if ((c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) && - id >= metaObjects->last().propertyOffset) { + id >= metaObjects->constLast().propertyOffset) { for (int ii = 0; ii < metaObjects->count(); ++ii) { const ProxyData &data = metaObjects->at(ii); @@ -107,7 +107,7 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void } } } else if (c == QMetaObject::InvokeMetaMethod && - id >= metaObjects->last().methodOffset) { + id >= metaObjects->constLast().methodOffset) { QMetaMethod m = object->metaObject()->method(id); if (m.methodType() == QMetaMethod::Signal) { QMetaObject::activate(object, id, a); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 40a2c596e3..75bc98f278 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2002,8 +2002,7 @@ void QQmlTypeLoader::trimCache() break; while (!unneededTypes.isEmpty()) { - TypeCache::Iterator iter = unneededTypes.last(); - unneededTypes.removeLast(); + TypeCache::Iterator iter = unneededTypes.takeLast(); iter.value()->release(); m_typeCache.erase(iter); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 201d634411..3f56cc24e2 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -502,7 +502,7 @@ ReturnedValue NodePrototype::method_get_firstChild(CallContext *ctx) if (r->d()->d->children.isEmpty()) return Encode::null(); else - return Node::create(scope.engine, r->d()->d->children.first()); + return Node::create(scope.engine, r->d()->d->children.constFirst()); } ReturnedValue NodePrototype::method_get_lastChild(CallContext *ctx) @@ -515,7 +515,7 @@ ReturnedValue NodePrototype::method_get_lastChild(CallContext *ctx) if (r->d()->d->children.isEmpty()) return Encode::null(); else - return Node::create(scope.engine, r->d()->d->children.last()); + return Node::create(scope.engine, r->d()->d->children.constLast()); } ReturnedValue NodePrototype::method_get_previousSibling(CallContext *ctx) -- cgit v1.2.3 From 31ca52cee47f189ee1c80245f7b114c29ac7c675 Mon Sep 17 00:00:00 2001 From: Eike Hein Date: Thu, 4 Jun 2015 21:25:32 +0200 Subject: Don't accept left clicks when text format is plain. Text elements may contain rich text with embedded links, and need to accept left clicks to open them. However, setting the textFormat to PlainText can disable mouse handling entirely, as it is not required. Accepting left clicks if there can be nothing to interact with is unexpected and surprising, and can cause bugs in code that performs child event filtering and doesn't expect Text elements to produce child events. Change-Id: Ibd5b9cf8d06fd30ea26f78b5393cc43e94646e73 Reviewed-by: Marco Martin Reviewed-by: Kai Uwe Broulik Reviewed-by: Robin Burchell Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/items/qquicktext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index e67e2cee9c..ea2d2f3133 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -2013,6 +2013,7 @@ void QQuickText::setTextFormat(TextFormat format) } d->updateLayout(); setAcceptHoverEvents(d->richText || d->styledText); + setAcceptedMouseButtons(d->richText || d->styledText ? Qt::LeftButton : Qt::NoButton); emit textFormatChanged(d->format); } -- cgit v1.2.3 From 107a52b8a7019b66b4cb3279e7bf070d5b57c2e0 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 3 Aug 2016 16:17:19 +0200 Subject: QML: Split off value type information from property data Change-Id: I2ae2fb0f18af9b866cc9482fd4f42d9d4269f8cb Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4qobjectwrapper.cpp | 9 ++- src/qml/qml/qqmlbinding.cpp | 117 +++++++++++++++++--------------- src/qml/qml/qqmlbinding_p.h | 8 +-- src/qml/qml/qqmlobjectcreator.cpp | 52 ++++++++------ src/qml/qml/qqmlproperty.cpp | 90 ++++++------------------ src/qml/qml/qqmlproperty_p.h | 18 ++--- src/qml/qml/qqmlpropertycache_p.h | 48 ++----------- src/qml/qml/qqmlvaluetypewrapper.cpp | 10 +-- 8 files changed, 143 insertions(+), 209 deletions(-) diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 9cfc5dcb1f..8fef45891a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -328,7 +328,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired) { - QQmlData::flushPendingBinding(object, property->encodedIndex()); + QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex)); if (property->isFunction() && !property->isVarProperty()) { if (property->isVMEFunction()) { @@ -466,14 +466,14 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP bindingFunction->initBindingLocation(); newBinding = QQmlBinding::create(property, value, object, callingQmlContext); - newBinding->setTarget(object, *property); + newBinding->setTarget(object, *property, nullptr); } } if (newBinding) QQmlPropertyPrivate::setBinding(newBinding); else - QQmlPropertyPrivate::removeBinding(object, property->encodedIndex()); + QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex)); if (!newBinding && property->isVarProperty()) { // allow assignment of "special" values (null, undefined, function) to var properties @@ -648,6 +648,9 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, con void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value) { + Q_ASSERT(propertyIndex < 0xffff); + Q_ASSERT(propertyIndex >= 0); + if (QQmlData::wasDeleted(object)) return; QQmlData *ddata = QQmlData::get(object, /*create*/false); diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 5c9b14ba74..72c7347a68 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -170,7 +170,11 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) // Check for a binding update loop if (Q_UNLIKELY(updatingFlag())) { - QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), getPropertyData(), 0); + QQmlPropertyData *d = nullptr; + QQmlPropertyData vtd; + getPropertyData(&d, &vtd); + Q_ASSERT(d); + QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), *d, &vtd, 0); QQmlAbstractBinding::printBindingLoopError(p); return; } @@ -205,8 +209,10 @@ protected: QQmlPropertyData::WriteFlags flags, QV4::Scope &, const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL { - QQmlPropertyData pd = getPropertyData(); - pd.writeProperty(*m_target, &binding, flags); + Q_ASSERT(!m_targetIndex.hasValueTypeIndex()); + QQmlPropertyData *pd = nullptr; + getPropertyData(&pd, nullptr); + pd->writeProperty(*m_target, &binding, flags); } }; @@ -260,13 +266,18 @@ protected: Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL { - QQmlPropertyData pd = getPropertyData(); + Q_ASSERT(targetObject()); + + QQmlPropertyData *pd; + QQmlPropertyData vpd; + getPropertyData(&pd, &vpd); + Q_ASSERT(pd); + int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded. if (propertyType == QMetaType::UnknownType) - propertyType = pd.propType; - Q_ASSERT(targetObject()); + propertyType = pd->propType; - if (Q_LIKELY(!isUndefined && !pd.isValueTypeVirtual())) { + if (Q_LIKELY(!isUndefined && !vpd.isValid())) { switch (propertyType) { case QMetaType::Bool: if (result.isBoolean()) @@ -293,32 +304,34 @@ protected: break; default: if (const QV4::QQmlValueTypeWrapper *vtw = result.as()) { - if (vtw->d()->valueType->typeId == pd.propType) { - return vtw->write(m_target.data(), pd.coreIndex); + if (vtw->d()->valueType->typeId == pd->propType) { + return vtw->write(m_target.data(), pd->coreIndex); } } break; } } - return slowWrite(pd, result, isUndefined, flags); + return slowWrite(*pd, vpd, result, isUndefined, flags); } template - Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData &pd, QQmlPropertyData::WriteFlags flags) const + Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData *pd, QQmlPropertyData::WriteFlags flags) const { void *o = &value; - return pd.writeProperty(targetObject(), o, flags); + return pd->writeProperty(targetObject(), o, flags); } }; -Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const QV4::Value &result, +Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, + const QQmlPropertyData &valueTypeData, + const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) { QQmlEngine *engine = context()->engine; QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); - int type = core.isValueTypeVirtual() ? core.valueTypePropType : core.propType; + int type = valueTypeData.isValid() ? valueTypeData.propType : core.propType; QQmlJavaScriptExpression::DeleteWatcher watcher(this); @@ -354,14 +367,14 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const Q void *args[] = { 0 }; QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex, args); } else if (isUndefined && type == qMetaTypeId()) { - QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, QVariant(), context(), flags); + QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant(), context(), flags); } else if (type == qMetaTypeId()) { const QV4::FunctionObject *f = result.as(); if (f && f->isBinding()) { delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); return false; } - QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, QVariant::fromValue( + QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant::fromValue( QJSValue(QV8Engine::getV4(v8engine), result.asReturnedValue())), context(), flags); } else if (isUndefined) { @@ -378,7 +391,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const Q else delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var.")); return false; - } else if (!QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, value, context(), flags)) { + } else if (!QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, value, context(), flags)) { if (watcher.wasDeleted()) return true; @@ -483,10 +496,11 @@ QString QQmlBinding::expression() const void QQmlBinding::setTarget(const QQmlProperty &prop) { - setTarget(prop.object(), QQmlPropertyPrivate::get(prop)->core); + auto pd = QQmlPropertyPrivate::get(prop); + setTarget(prop.object(), pd->core, &pd->valueTypeData); } -void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) +void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) { m_target = object; @@ -495,11 +509,9 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) return; } - QQmlPropertyData pd = core; - - while (pd.isAlias()) { - int coreIndex = pd.coreIndex; - int valueTypeIndex = pd.getValueTypeCoreIndex(); + int coreIndex = core.coreIndex; + int valueTypeIndex = valueType ? valueType->coreIndex : -1; + for (bool isAlias = core.isAlias(); isAlias; ) { QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); int aValueTypeIndex; @@ -521,18 +533,10 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) Q_ASSERT(propertyData); m_target = object; - pd = *propertyData; - if (valueTypeIndex != -1) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(pd.propType); - Q_ASSERT(valueTypeMetaObject); - QMetaProperty vtProp = valueTypeMetaObject->property(valueTypeIndex); - pd.setAsValueTypeVirtual(); - pd.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); - pd.valueTypePropType = vtProp.userType(); - pd.valueTypeCoreIndex = valueTypeIndex; - } + isAlias = propertyData->isAlias(); + coreIndex = propertyData->coreIndex; } - m_targetIndex = pd.encodedIndex(); + m_targetIndex = QQmlPropertyIndex(coreIndex, valueTypeIndex); QQmlData *data = QQmlData::get(*m_target, true); if (!data->propertyCache) { @@ -541,25 +545,24 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) } } -QQmlPropertyData QQmlBinding::getPropertyData() const +void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const { + Q_ASSERT(propertyData); + QQmlData *data = QQmlData::get(*m_target, false); Q_ASSERT(data && data->propertyCache); - QQmlPropertyData *propertyData = data->propertyCache->property(m_targetIndex.coreIndex()); - Q_ASSERT(propertyData); + *propertyData = data->propertyCache->property(m_targetIndex.coreIndex()); + Q_ASSERT(*propertyData); - QQmlPropertyData d = *propertyData; - if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex())) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d.propType); + if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType((*propertyData)->propType); Q_ASSERT(valueTypeMetaObject); QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex()); - d.setAsValueTypeVirtual(); - d.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); - d.valueTypePropType = vtProp.userType(); - d.valueTypeCoreIndex = m_targetIndex.valueTypeIndex(); + valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp)); + valueTypeData->propType = vtProp.userType(); + valueTypeData->coreIndex = m_targetIndex.valueTypeIndex(); } - return d; } class QObjectPointerBinding: public QQmlNonbindingBinding @@ -575,20 +578,22 @@ protected: Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL { - QQmlPropertyData pd = getPropertyData(); - if (Q_UNLIKELY(isUndefined || pd.isValueTypeVirtual())) - return slowWrite(pd, result, isUndefined, flags); + QQmlPropertyData *pd; + QQmlPropertyData vtpd; + getPropertyData(&pd, &vtpd); + if (Q_UNLIKELY(isUndefined || vtpd.isValid())) + return slowWrite(*pd, vtpd, result, isUndefined, flags); // Check if the result is a QObject: QObject *resultObject = nullptr; QQmlMetaObject resultMo; if (result.isNull()) { // Special case: we can always write a nullptr. Don't bother checking anything else. - return pd.writeProperty(targetObject(), &resultObject, flags); + return pd->writeProperty(targetObject(), &resultObject, flags); } else if (auto wrapper = result.as()) { resultObject = wrapper->object(); if (!resultObject) - return pd.writeProperty(targetObject(), &resultObject, flags); + return pd->writeProperty(targetObject(), &resultObject, flags); if (QQmlData *ddata = QQmlData::get(resultObject, false)) resultMo = ddata->propertyCache; if (resultMo.isNull()) { @@ -599,22 +604,22 @@ protected: QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()); resultMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, value.userType()); if (resultMo.isNull()) - return slowWrite(pd, result, isUndefined, flags); + return slowWrite(*pd, vtpd, result, isUndefined, flags); resultObject = *static_cast(value.constData()); } else { - return slowWrite(pd, result, isUndefined, flags); + return slowWrite(*pd, vtpd, result, isUndefined, flags); } // Compare & set: if (QQmlMetaObject::canConvert(resultMo, targetMetaObject)) { - return pd.writeProperty(targetObject(), &resultObject, flags); + return pd->writeProperty(targetObject(), &resultObject, flags); } else if (!resultObject && QQmlMetaObject::canConvert(targetMetaObject, resultMo)) { // In the case of a null QObject, we assign the null if there is // any change that the null variant type could be up or down cast to // the property type. - return pd.writeProperty(targetObject(), &resultObject, flags); + return pd->writeProperty(targetObject(), &resultObject, flags); } else { - return slowWrite(pd, result, isUndefined, flags); + return slowWrite(*pd, vtpd, result, isUndefined, flags); } } }; diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 94fc0ccfc0..1801c3040c 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -81,7 +81,7 @@ public: ~QQmlBinding(); void setTarget(const QQmlProperty &); - void setTarget(QObject *, const QQmlPropertyData &); + void setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); void setNotifyOnValueChanged(bool); @@ -106,11 +106,11 @@ protected: QQmlPropertyData::WriteFlags flags, QV4::Scope &scope, const QV4::ScopedFunctionObject &f) = 0; - QQmlPropertyData getPropertyData() const; + void getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const; int getPropertyType() const; - bool slowWrite(const QQmlPropertyData &core, const QV4::Value &result, bool isUndefined, - QQmlPropertyData::WriteFlags flags); + bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, + const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags); private: inline bool updatingFlag() const; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 1e1c238b19..993331b0a0 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -810,16 +810,18 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // the point property (_qobjectForBindings) and after evaluating the expression, // the result is written to a value type virtual property, that contains the sub-index // of the "x" property. - QQmlPropertyData targetCorePropertyData = *property; - if (_valueTypeProperty) - targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); + QQmlBinding *qmlBinding; + if (_valueTypeProperty) { + qmlBinding = QQmlBinding::create(_valueTypeProperty, function, _scopeObject, context); + qmlBinding->setTarget(_bindingTarget, *_valueTypeProperty, property); + } else { + qmlBinding = QQmlBinding::create(property, function, _scopeObject, context); + qmlBinding->setTarget(_bindingTarget, *property, nullptr); + } - QQmlBinding *qmlBinding = QQmlBinding::create(&targetCorePropertyData, function, _scopeObject, context); sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); - qmlBinding->setTarget(_bindingTarget, targetCorePropertyData); - - if (targetCorePropertyData.isAlias()) { + if (property->isAlias()) { QQmlPropertyPrivate::setBinding(qmlBinding, QQmlPropertyPrivate::DontEnable); } else { qmlBinding->addToObject(); @@ -840,15 +842,16 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlType *type = qmlTypeForObject(createdSubObject); Q_ASSERT(type); - QQmlPropertyData targetCorePropertyData = *property; - if (_valueTypeProperty) - targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); - int valueSourceCast = type->propertyValueSourceCast(); if (valueSourceCast != -1) { QQmlPropertyValueSource *vs = reinterpret_cast(reinterpret_cast(createdSubObject) + valueSourceCast); QObject *target = createdSubObject->parent(); - vs->setTarget(QQmlPropertyPrivate::restore(target, targetCorePropertyData, context)); + QQmlProperty prop; + if (_valueTypeProperty) + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + else + prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + vs->setTarget(prop); return true; } int valueInterceptorCast = type->propertyValueInterceptorCast(); @@ -856,9 +859,11 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyValueInterceptor *vi = reinterpret_cast(reinterpret_cast(createdSubObject) + valueInterceptorCast); QObject *target = createdSubObject->parent(); - if (targetCorePropertyData.isAlias()) { + QQmlPropertyIndex propertyIndex; + if (property->isAlias()) { + QQmlPropertyIndex originalIndex(property->coreIndex, _valueTypeProperty ? _valueTypeProperty->coreIndex : -1); QQmlPropertyIndex propIndex; - QQmlPropertyPrivate::findAliasTarget(target, QQmlPropertyIndex(targetCorePropertyData.coreIndex), &target, &propIndex); + QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); QQmlData *data = QQmlData::get(target); if (!data || !data->propertyCache) { qWarning() << "can't resolve property alias for 'on' assignment"; @@ -866,17 +871,24 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } // we can't have aliasses on subproperties of value types, so: - targetCorePropertyData = *data->propertyCache->property(propIndex.coreIndex()); + QQmlPropertyData targetPropertyData = *data->propertyCache->property(propIndex.coreIndex()); + auto prop = QQmlPropertyPrivate::restore(target, targetPropertyData, nullptr, context); + vi->setTarget(prop); + propertyIndex = QQmlPropertyPrivate::propertyIndex(prop); + } else { + QQmlProperty prop; + if (_valueTypeProperty) + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + else + prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + vi->setTarget(prop); + propertyIndex = QQmlPropertyPrivate::propertyIndex(prop); } - QQmlProperty prop = - QQmlPropertyPrivate::restore(target, targetCorePropertyData, context); - - vi->setTarget(prop); QQmlInterceptorMetaObject *mo = QQmlInterceptorMetaObject::get(target); if (!mo) mo = new QQmlInterceptorMetaObject(target, QQmlData::get(target)->propertyCache); - mo->registerInterceptor(QQmlPropertyPrivate::propertyIndex(prop), vi); + mo->registerInterceptor(propertyIndex, vi); return true; } return false; diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 33f3b96389..ffa9bfa3a7 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -306,10 +306,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) object = currentObject; core = *property; - core.setAsValueTypeVirtual(); - core.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); - core.valueTypePropType = vtProp.userType(); - core.valueTypeCoreIndex = idx; + valueTypeData.setFlags(QQmlPropertyData::flagsForProperty(vtProp)); + valueTypeData.propType = vtProp.userType(); + valueTypeData.coreIndex = idx; return; } else { @@ -473,7 +472,7 @@ const char *QQmlProperty::propertyTypeName() const if (d->isValueType()) { const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType); Q_ASSERT(valueTypeMetaObject); - return valueTypeMetaObject->property(d->core.valueTypeCoreIndex).typeName(); + return valueTypeMetaObject->property(d->valueTypeData.coreIndex).typeName(); } else if (d->object && type() & Property && d->core.isValid()) { return d->object->metaObject()->property(d->core.coreIndex).typeName(); } else { @@ -493,10 +492,7 @@ bool QQmlProperty::operator==(const QQmlProperty &other) const // from the other members return d->object == other.d->object && d->core.coreIndex == other.d->core.coreIndex && - d->core.isValueTypeVirtual() == other.d->core.isValueTypeVirtual() && - (!d->core.isValueTypeVirtual() || - (d->core.valueTypeCoreIndex == other.d->core.valueTypeCoreIndex && - d->core.valueTypePropType == other.d->core.valueTypePropType)); + d->valueTypeData.coreIndex == other.d->valueTypeData.coreIndex; } /*! @@ -510,14 +506,14 @@ int QQmlProperty::propertyType() const bool QQmlPropertyPrivate::isValueType() const { - return core.isValueTypeVirtual(); + return valueTypeData.isValid(); } int QQmlPropertyPrivate::propertyType() const { uint type = this->type(); if (isValueType()) { - return core.valueTypePropType; + return valueTypeData.propType; } else if (type & QQmlProperty::Property) { return core.propType; } else { @@ -653,7 +649,7 @@ QString QQmlProperty::name() const const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType); Q_ASSERT(valueTypeMetaObject); - const char *vtName = valueTypeMetaObject->property(d->core.valueTypeCoreIndex).name(); + const char *vtName = valueTypeMetaObject->property(d->valueTypeData.coreIndex).name(); rv += QString::fromUtf8(vtName); d->nameCache = rv; @@ -708,7 +704,8 @@ QQmlPropertyPrivate::binding(const QQmlProperty &that) if (!that.d || !that.isProperty() || !that.d->object) return 0; - return binding(that.d->object, that.d->core.encodedIndex()); + QQmlPropertyIndex thatIndex(that.d->core.coreIndex, that.d->valueTypeData.coreIndex); + return binding(that.d->object, thatIndex); } /*! @@ -791,7 +788,7 @@ void QQmlPropertyPrivate::removeBinding(const QQmlProperty &that) if (!that.d || !that.isProperty() || !that.d->object) return; - removeBinding(that.d->object, that.d->core.encodedIndex()); + removeBinding(that.d->object, that.d->encodedIndex()); } QQmlAbstractBinding * @@ -1030,7 +1027,7 @@ QVariant QQmlPropertyPrivate::readValueProperty() QQmlValueType *valueType = QQmlValueTypeFactory::valueType(core.propType); Q_ASSERT(valueType); valueType->read(object, core.coreIndex); - return valueType->metaObject()->property(core.valueTypeCoreIndex).read(valueType); + return valueType->metaObject()->property(valueTypeData.coreIndex).read(valueType); } else if (core.isQList()) { @@ -1148,38 +1145,28 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, QQmlPropertyData::WriteFlags flags) { - return writeValueProperty(object, core, value, effectiveContext(), flags); + return writeValueProperty(object, core, valueTypeData, value, effectiveContext(), flags); } bool QQmlPropertyPrivate::writeValueProperty(QObject *object, const QQmlPropertyData &core, + const QQmlPropertyData &valueTypeData, const QVariant &value, QQmlContextData *context,QQmlPropertyData::WriteFlags flags) { // Remove any existing bindings on this property if (!(flags & QQmlPropertyData::DontRemoveBinding) && object) - removeBinding(object, core.encodedIndex()); + removeBinding(object, encodedIndex(core, valueTypeData)); bool rv = false; - if (core.isValueTypeVirtual()) { - + if (valueTypeData.isValid()) { QQmlValueType *writeBack = QQmlValueTypeFactory::valueType(core.propType); writeBack->read(object, core.coreIndex); - - QQmlPropertyData data = core; - data.setFlags(core.valueTypeFlags); - data.coreIndex = core.valueTypeCoreIndex; - data.propType = core.valueTypePropType; - - rv = write(writeBack, data, value, context, flags); - + rv = write(writeBack, valueTypeData, value, context, flags); writeBack->write(object, core.coreIndex, flags); - } else { - rv = write(object, core, value, context, flags); - } return rv; @@ -1603,56 +1590,23 @@ int QQmlProperty::index() const QQmlPropertyIndex QQmlPropertyPrivate::propertyIndex(const QQmlProperty &that) { - return that.d ? that.d->core.encodedIndex() : QQmlPropertyIndex(); -} - -/*! - Returns the "property index" for use in bindings. The top 16 bits are the value type - offset, and 0 otherwise. The bottom 16 bits are the regular property index. -*/ -int QQmlPropertyPrivate::bindingIndex(const QQmlProperty &that) -{ - if (!that.d) - return -1; - return bindingIndex(that.d->core); -} - -int QQmlPropertyPrivate::bindingIndex(const QQmlPropertyData &that) -{ - int rv = that.coreIndex; - if (rv != -1 && that.isValueTypeVirtual()) - rv = rv | (that.valueTypeCoreIndex << 16); - return rv; -} - -QQmlPropertyData -QQmlPropertyPrivate::saveValueType(const QQmlPropertyData &base, - const QMetaObject *subObject, int subIndex, - QQmlEngine *) -{ - QMetaProperty subProp = subObject->property(subIndex); - - QQmlPropertyData core = base; - core.setAsValueTypeVirtual(); - core.valueTypeFlags = QQmlPropertyData::flagsForProperty(subProp); - core.valueTypeCoreIndex = subIndex; - core.valueTypePropType = subProp.userType(); - - return core; + return that.d ? that.d->encodedIndex() : QQmlPropertyIndex(); } QQmlProperty QQmlPropertyPrivate::restore(QObject *object, const QQmlPropertyData &data, - QQmlContextData *ctxt) + const QQmlPropertyData *valueTypeData, QQmlContextData *ctxt) { QQmlProperty prop; prop.d = new QQmlPropertyPrivate; prop.d->object = object; prop.d->context = ctxt; - prop.d->engine = ctxt?ctxt->engine:0; + prop.d->engine = ctxt ? ctxt->engine : nullptr; prop.d->core = data; + if (valueTypeData) + prop.d->valueTypeData = *valueTypeData; return prop; } diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index f23426f45c..16d2bfef64 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -73,12 +73,18 @@ public: QPointer object; QQmlPropertyData core; + QQmlPropertyData valueTypeData; bool isNameCached:1; QString nameCache; QQmlPropertyPrivate(); + QQmlPropertyIndex encodedIndex() const + { return encodedIndex(core, valueTypeData); } + static QQmlPropertyIndex encodedIndex(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData) + { return QQmlPropertyIndex(core.coreIndex, valueTypeData.coreIndex); } + inline QQmlContextData *effectiveContext() const; void initProperty(QObject *obj, const QString &name); @@ -96,7 +102,7 @@ public: static bool writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, const QVariant &value, int flags); static bool writeValueProperty(QObject *, - const QQmlPropertyData &, + const QQmlPropertyData &, const QQmlPropertyData &valueTypeData, const QVariant &, QQmlContextData *, QQmlPropertyData::WriteFlags flags = 0); static bool write(QObject *, const QQmlPropertyData &, const QVariant &, @@ -116,12 +122,8 @@ public: static void removeBinding(QQmlAbstractBinding *b); static QQmlAbstractBinding *binding(QObject *, QQmlPropertyIndex index); - static QQmlPropertyData saveValueType(const QQmlPropertyData &, - const QMetaObject *, int, - QQmlEngine *); - static QQmlProperty restore(QObject *, - const QQmlPropertyData &, - QQmlContextData *); + static QQmlProperty restore(QObject *, const QQmlPropertyData &, const QQmlPropertyData *, + QQmlContextData *); int signalIndex() const; @@ -139,8 +141,6 @@ public: QQmlBoundSignalExpression *); static bool write(const QQmlProperty &that, const QVariant &, QQmlPropertyData::WriteFlags); static QQmlPropertyIndex propertyIndex(const QQmlProperty &that); - static int bindingIndex(const QQmlProperty &that); - static int bindingIndex(const QQmlPropertyData &that); static QMetaMethod findSignalByName(const QMetaObject *mo, const QByteArray &); static bool connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index dfe0d02dcd..cc3ad62706 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -95,8 +95,7 @@ public: QJSValueType = 6, // Property type is a QScriptValue V4HandleType = 7, // Property type is a QQmlV4Handle VarPropertyType = 8, // Property type is a "var" property of VMEMO - ValueTypeVirtualType = 9, // Property is a value type "virtual" property - QVariantType = 10 // Property is a QVariant + QVariantType = 9 // Property is a QVariant }; // Can apply to all properties, except IsFunction @@ -154,8 +153,6 @@ public: bool isQJSValue() const { return flags.type == Flags::QJSValueType; } bool isV4Handle() const { return flags.type == Flags::V4HandleType; } bool isVarProperty() const { return flags.type == Flags::VarPropertyType; } - bool isValueTypeVirtual() const { return flags.type == Flags::ValueTypeVirtualType; } - void setAsValueTypeVirtual() { flags.type = Flags::ValueTypeVirtualType; } bool isQVariant() const { return flags.type == Flags::QVariantType; } bool isVMEFunction() const { return flags.isVMEFunction; } bool hasArguments() const { return flags.hasArguments; } @@ -168,20 +165,12 @@ public: bool isCloned() const { return flags.isCloned; } bool isConstructor() const { return flags.isConstructor; } - bool hasOverride() const { return flags.type != Flags::ValueTypeVirtualType && - !(flags.hasAccessors) && + bool hasOverride() const { return !(flags.hasAccessors) && overrideIndex >= 0; } bool hasRevision() const { return !(flags.hasAccessors) && revision != 0; } bool isFullyResolved() const { return !flags.notFullyResolved; } - // Returns -1 if not a value type virtual property - inline int getValueTypeCoreIndex() const; - - // Returns the "encoded" index for use with bindings. Encoding is: - // coreIndex | ((valueTypeCoreIndex + 1) << 16) - inline QQmlPropertyIndex encodedIndex() const; - union { int propType; // When !NotFullyResolved const char *propTypeName; // When NotFullyResolved @@ -198,18 +187,9 @@ public: qint16 revision; qint16 metaObjectOffset; - union { - struct { // When IsValueTypeVirtual - quint16 valueTypePropType; // The QVariant::Type of access property on the value - // type proxy object - quint16 valueTypeCoreIndex; // The prop index of the access property on the value - // type proxy object - }; - - struct { // When !IsValueTypeVirtual - uint overrideIndexIsProperty : 1; - signed int overrideIndex : 31; - }; + struct { // When !IsValueTypeVirtual + uint overrideIndexIsProperty : 1; + signed int overrideIndex : 31; }; }; struct { // When HasAccessors @@ -218,8 +198,6 @@ public: }; int coreIndex; - Flags valueTypeFlags; // flags of the access property on the value type proxy - // object private: friend class QQmlPropertyData; friend class QQmlPropertyCache; @@ -619,21 +597,7 @@ bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other) propType == other.propType && coreIndex == other.coreIndex && notifyIndex == other.notifyIndex && - revision == other.revision && - (!isValueTypeVirtual() || - (valueTypeCoreIndex == other.valueTypeCoreIndex && - valueTypePropType == other.valueTypePropType)); -} - -int QQmlPropertyRawData::getValueTypeCoreIndex() const -{ - return isValueTypeVirtual()?valueTypeCoreIndex:-1; -} - -QQmlPropertyIndex QQmlPropertyRawData::encodedIndex() const -{ - return isValueTypeVirtual() ? QQmlPropertyIndex(coreIndex, valueTypeCoreIndex) - : QQmlPropertyIndex(coreIndex); + revision == other.revision; } inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index a1bf692f19..3ad53aa3fc 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -431,8 +431,6 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) const QQmlPropertyData *pd = r->d()->propertyCache->property(name, 0, 0); if (!pd) return; - QMetaProperty property = metaObject->property(pd->coreIndex); - Q_ASSERT(property.isValid()); if (reference) { QV4::ScopedFunctionObject f(scope, value); @@ -449,25 +447,23 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QQmlPropertyData cacheData; cacheData.setWritable(true); - cacheData.setAsValueTypeVirtual(); cacheData.propType = writeBackPropertyType; cacheData.coreIndex = reference->d()->property; - cacheData.valueTypeCoreIndex = pd->coreIndex; - cacheData.valueTypePropType = property.userType(); QV4::Scoped bindingFunction(scope, (const Value &)f); bindingFunction->initBindingLocation(); QQmlBinding *newBinding = QQmlBinding::create(&cacheData, value, reference->d()->object, context); - newBinding->setTarget(reference->d()->object, cacheData); + newBinding->setTarget(reference->d()->object, cacheData, pd); QQmlPropertyPrivate::setBinding(newBinding); return; } else { QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex)); - } } + QMetaProperty property = metaObject->property(pd->coreIndex); + Q_ASSERT(property.isValid()); QVariant v = v4->toVariant(value, property.userType()); -- cgit v1.2.3 From b6f393259ca014fa8c6419f6d7e608e279fa7913 Mon Sep 17 00:00:00 2001 From: Maurice Kalinowski Date: Tue, 9 Aug 2016 09:32:38 +0200 Subject: Fix static build When compiling statically QSG_LOG_INFO is exported in QtQuick as well as the plugin causing problems when linking eg qmleasing. Change-Id: I9f86c0140c39f129e859b6b0e9eed43a9b7ce928 Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index e32ecdc138..87215d5b54 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -75,7 +75,7 @@ DECLARE_DEBUG_VAR(buffer) DECLARE_DEBUG_VAR(texture) // Except for system info on startup. -Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.general") +Q_LOGGING_CATEGORY(QSG_LOG_INFO_GENERAL, "qt.scenegraph.general") // Any changes to the defaults below must be reflected in adaptations.qdoc as @@ -197,7 +197,7 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte DXGI_ADAPTER_DESC1 desc; adapter->GetDesc1(&desc); const QString name = QString::fromUtf16((char16_t *) desc.Description); - qCDebug(QSG_LOG_INFO, "Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags); + qCDebug(QSG_LOG_INFO_GENERAL, "Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags); } if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX")) { @@ -207,7 +207,7 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte const QString name = QString::fromUtf16((char16_t *) desc.Description); HRESULT hr = D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr); if (SUCCEEDED(hr)) { - qCDebug(QSG_LOG_INFO, "Using requested adapter '%s'", qPrintable(name)); + qCDebug(QSG_LOG_INFO_GENERAL, "Using requested adapter '%s'", qPrintable(name)); *outAdapter = adapter.Detach(); return; } else { @@ -223,7 +223,7 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) { const QString name = QString::fromUtf16((char16_t *) desc.Description); - qCDebug(QSG_LOG_INFO, "Using adapter '%s'", qPrintable(name)); + qCDebug(QSG_LOG_INFO_GENERAL, "Using adapter '%s'", qPrintable(name)); break; } } @@ -287,7 +287,7 @@ void QSGD3D12DeviceManager::ensureCreated() } if (warp) { - qCDebug(QSG_LOG_INFO, "Using WARP"); + qCDebug(QSG_LOG_INFO_GENERAL, "Using WARP"); m_factory->EnumWarpAdapter(IID_PPV_ARGS(&adapter)); HRESULT hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)); if (FAILED(hr)) { @@ -300,12 +300,12 @@ void QSGD3D12DeviceManager::ensureCreated() if (SUCCEEDED(adapter.As(&adapter3))) { DXGI_QUERY_VIDEO_MEMORY_INFO vidMemInfo; if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &vidMemInfo))) { - qCDebug(QSG_LOG_INFO, "Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", + qCDebug(QSG_LOG_INFO_GENERAL, "Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); } if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &vidMemInfo))) { - qCDebug(QSG_LOG_INFO, "Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", + qCDebug(QSG_LOG_INFO_GENERAL, "Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); } @@ -726,16 +726,16 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int waitableSwapChainMaxLatency = qBound(0, qEnvironmentVariableIntValue(latReqEnvVar), 16); if (qEnvironmentVariableIsSet("QSG_INFO")) - const_cast(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true); + const_cast(QSG_LOG_INFO_GENERAL()).setEnabled(QtDebugMsg, true); - qCDebug(QSG_LOG_INFO, "d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d", + qCDebug(QSG_LOG_INFO_GENERAL, "d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d", swapChainBufferCount, frameInFlightCount); if (waitableSwapChainMaxLatency) - qCDebug(QSG_LOG_INFO, "Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency); + qCDebug(QSG_LOG_INFO_GENERAL, "Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency); const bool debugLayer = qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0; if (debugLayer) { - qCDebug(QSG_LOG_INFO, "Enabling debug layer"); + qCDebug(QSG_LOG_INFO_GENERAL, "Enabling debug layer"); ComPtr debugController; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) debugController->EnableDebugLayer(); -- cgit v1.2.3 From 29c0b01831c1f174a2355714ec9601af52dc9443 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 8 Aug 2016 14:40:26 +0200 Subject: Fix String(-0) According to the spec, this should give "0", not "-0". Change-Id: I7c1e2a777d41cdf13807f314c2adcc5e7ed1c757 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4runtime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index a69874cacb..f3d166d695 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -259,7 +259,7 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->append(QString(decpt - result->length(), zero)); } - if (sign) + if (sign && num) result->prepend(QLatin1Char('-')); return; -- cgit v1.2.3 From 5795e059dd40c0feeb3e408d47fa2ff64d56c791 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 9 Aug 2016 10:50:45 +0200 Subject: Fix signatures of FunctionObject::construct and call Fixup for 702c4247d74ffb7e4fb1aaca96d70f4591203ba2. Fixes the test failures in section 15.3.4 of the JS test suite. Change-Id: Id44886e0301b560f2975104757467bb0c22aed60 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4functionobject.cpp | 8 ++++---- src/qml/jsruntime/qv4functionobject_p.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 276a069a77..e1a6bda811 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -166,14 +166,14 @@ ReturnedValue FunctionObject::name() const return get(scope()->engine->id_name()); } -ReturnedValue FunctionObject::construct(const Managed *that, CallData *) +void FunctionObject::construct(const Managed *that, Scope &scope, CallData *) { - return static_cast(that)->engine()->throwTypeError(); + scope.result = static_cast(that)->engine()->throwTypeError(); } -ReturnedValue FunctionObject::call(const Managed *, CallData *) +void FunctionObject::call(const Managed *, Scope &scope, CallData *) { - return Encode::undefined(); + scope.result = Encode::undefined(); } void FunctionObject::markObjects(Heap::Base *that, ExecutionEngine *e) diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index be80b87873..182b762606 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -147,8 +147,8 @@ struct Q_QML_EXPORT FunctionObject: Object { using Object::construct; using Object::call; - static ReturnedValue construct(const Managed *that, CallData *); - static ReturnedValue call(const Managed *that, CallData *d); + static void construct(const Managed *that, Scope &scope, CallData *); + static void call(const Managed *that, Scope &scope, CallData *d); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function, bool createProto = true); static Heap::FunctionObject *createQmlFunction(QQmlContextData *qmlContext, QObject *scopeObject, QV4::Function *runtimeFunction, -- cgit v1.2.3 From 4c47c32c925f0725b57c61875bbb50864032bdd7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 9 Aug 2016 10:52:08 +0200 Subject: Fix SimpleScriptFunction::construct Do the same thing as in ScriptFunction::construct. If the return value of the call is not an object, return the this object passed into the function. If an exception got thrown return undefined. Fixes the test failures in section 15.3.5 in the JS test suite. Change-Id: I7a640f3eb0eb7d207ccfa1ff927a46031a6d2c06 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4functionobject.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index e1a6bda811..b08ad24fbe 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -548,8 +548,11 @@ void SimpleScriptFunction::construct(const Managed *that, Scope &scope, CallData if (f->function()->compiledFunction->hasQmlDependencies()) QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope); - if (!scope.result.isManaged() || !scope.result.managed()) + if (v4->hasException) { + scope.result = Encode::undefined(); + } else if (!scope.result.isObject()) { scope.result = callData->thisObject; + } } void SimpleScriptFunction::call(const Managed *that, Scope &scope, CallData *callData) -- cgit v1.2.3 From 3cba637ea6b4109171e5cab678f5ccecaed40809 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Aug 2016 10:30:26 +0200 Subject: Separate header verification and mmap handling of CompiledData::Unit ...into a separate class. This will make it easier to specialize the handling for Posix and Windows platforms. Change-Id: I995538fa8239eac8586c96812086e50b4c19257f Reviewed-by: Lars Knoll --- src/qml/compiler/compiler.pri | 6 +- src/qml/compiler/qv4compilationunitmapper.cpp | 114 ++++++++++++++++++++++++++ src/qml/compiler/qv4compilationunitmapper_p.h | 82 ++++++++++++++++++ src/qml/compiler/qv4compileddata.cpp | 50 ++--------- src/qml/compiler/qv4compileddata_p.h | 3 +- 5 files changed, 207 insertions(+), 48 deletions(-) create mode 100644 src/qml/compiler/qv4compilationunitmapper.cpp create mode 100644 src/qml/compiler/qv4compilationunitmapper_p.h diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index ad73c26b15..02288a8ad7 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -28,7 +28,8 @@ HEADERS += \ $$PWD/qv4isel_moth_p.h \ $$PWD/qv4instr_moth_p.h \ $$PWD/qqmlpropertycachecreator_p.h \ - $$PWD/qqmlpropertyvalidator_p.h + $$PWD/qqmlpropertyvalidator_p.h \ + $$PWD/qv4compilationunitmapper_p.h SOURCES += \ @@ -36,6 +37,7 @@ SOURCES += \ $$PWD/qv4instr_moth.cpp \ $$PWD/qv4isel_moth.cpp \ $$PWD/qqmlpropertycachecreator.cpp \ - $$PWD/qqmlpropertyvalidator.cpp + $$PWD/qqmlpropertyvalidator.cpp \ + $$PWD/qv4compilationunitmapper.cpp } diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp new file mode 100644 index 0000000000..1d8be036c9 --- /dev/null +++ b/src/qml/compiler/qv4compilationunitmapper.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qv4compilationunitmapper_p.h" + +#include "qv4compileddata_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +CompilationUnitMapper::CompilationUnitMapper() + : dataPtr(nullptr) +{ + +} + +CompiledData::Unit *CompilationUnitMapper::open(const QString &sourcePath, QString *errorString) +{ + close(); + + f.setFileName(sourcePath + QLatin1Char('c')); + if (!f.open(QIODevice::ReadOnly)) { + *errorString = f.errorString(); + return nullptr; + } + + CompiledData::Unit header; + qint64 bytesRead = f.read(reinterpret_cast(&header), sizeof(header)); + + if (bytesRead != sizeof(header)) { + *errorString = QStringLiteral("File too small for the header fields"); + return nullptr; + } + + if (strncmp(header.magic, CompiledData::magic_str, sizeof(header.magic))) { + *errorString = QStringLiteral("Magic bytes in the header do not match"); + return nullptr; + } + + if (header.version != quint32(QV4_DATA_STRUCTURE_VERSION)) { + *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header.version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); + return nullptr; + } + + if (header.qtVersion != quint32(QT_VERSION)) { + *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header.qtVersion, 0, 16).arg(QT_VERSION, 0, 16); + return nullptr; + } + + { + QFileInfo sourceCode(sourcePath); + if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header.sourceTimeStamp) { + *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + return nullptr; + } + } + + // Data structure and qt version matched, so now we can access the rest of the file safely. + + dataPtr = f.map(/*offset*/0, f.size()); + if (!dataPtr) { + *errorString = f.errorString(); + return nullptr; + } + + return reinterpret_cast(dataPtr); +} + +void CompilationUnitMapper::close() +{ + f.close(); + dataPtr = nullptr; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h new file mode 100644 index 0000000000..06ca3d0e02 --- /dev/null +++ b/src/qml/compiler/qv4compilationunitmapper_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QV4COMPILATIONUNITMAPPER_H +#define QV4COMPILATIONUNITMAPPER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace CompiledData { +struct Unit; +} + +class CompilationUnitMapper +{ +public: + CompilationUnitMapper(); + + CompiledData::Unit *open(const QString &sourcePath, QString *errorString); + void close(); + +private: + QFile f; + uchar *dataPtr; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4COMPILATIONUNITMAPPER_H diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 98eb7188cc..cc1f8ae261 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -49,6 +49,7 @@ #include #include #include +#include "qv4compilationunitmapper_p.h" #include #include #include @@ -376,56 +377,15 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory } const QString sourcePath = url.toLocalFile(); - QScopedPointer cacheFile(new QFile(sourcePath + QLatin1Char('c'))); + QScopedPointer cacheFile(new CompilationUnitMapper()); - if (!cacheFile->open(QIODevice::ReadOnly)) { - *errorString = cacheFile->errorString(); - return false; - } - - { - CompiledData::Unit header; - qint64 bytesRead = cacheFile->read(reinterpret_cast(&header), sizeof(header)); - - if (bytesRead != sizeof(header)) { - *errorString = QStringLiteral("File too small for the header fields"); - return false; - } - - if (strncmp(header.magic, CompiledData::magic_str, sizeof(header.magic))) { - *errorString = QStringLiteral("Magic bytes in the header do not match"); - return false; - } - - if (header.version != quint32(QV4_DATA_STRUCTURE_VERSION)) { - *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header.version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); - return false; - } - - if (header.qtVersion != quint32(QT_VERSION)) { - *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header.qtVersion, 0, 16).arg(QT_VERSION, 0, 16); - return false; - } - - { - QFileInfo sourceCode(sourcePath); - if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header.sourceTimeStamp) { - *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); - return false; - } - } - - } - // Data structure and qt version matched, so now we can access the rest of the file safely. - - uchar *cacheData = cacheFile->map(/*offset*/0, cacheFile->size()); - if (!cacheData) { - *errorString = cacheFile->errorString(); + CompiledData::Unit *mappedUnit = cacheFile->open(sourcePath, errorString); + if (!mappedUnit) { return false; } const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; - QScopedValueRollback dataPtrChange(data, reinterpret_cast(cacheData)); + QScopedValueRollback dataPtrChange(data, mappedUnit); { const QString foundArchitecture = stringAt(data->architectureIndex); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 0903654e51..a6ca1594a4 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -92,6 +92,7 @@ struct Function; struct Function; class EvalISelFactory; +class CompilationUnitMapper; namespace CompiledData { @@ -874,7 +875,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount int listMetaTypeId; bool isRegisteredWithEngine; - QScopedPointer backingFile; + QScopedPointer backingFile; // --- interface for QQmlPropertyCacheCreator typedef Object CompiledObject; -- cgit v1.2.3 From 186887aa56b68a2c0d9049759415db7307123137 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Aug 2016 11:09:24 +0200 Subject: Reduce file descriptor pressure on Unix platforms When opening cached compilation units, we can close the file descriptor after mmap'ing the contents, on Unix platforms. Change-Id: Ifb797a69743ebdc5e55db0b2e52b5cd66d071ca3 Reviewed-by: Lars Knoll --- src/qml/compiler/compiler.pri | 2 + src/qml/compiler/qv4compilationunitmapper.cpp | 57 +++++++---- src/qml/compiler/qv4compilationunitmapper_p.h | 9 +- src/qml/compiler/qv4compilationunitmapper_unix.cpp | 114 +++++++++++++++++++++ 4 files changed, 160 insertions(+), 22 deletions(-) create mode 100644 src/qml/compiler/qv4compilationunitmapper_unix.cpp diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 02288a8ad7..e80c0236a3 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -40,4 +40,6 @@ SOURCES += \ $$PWD/qqmlpropertyvalidator.cpp \ $$PWD/qv4compilationunitmapper.cpp +unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp + } diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp index 1d8be036c9..084137f17f 100644 --- a/src/qml/compiler/qv4compilationunitmapper.cpp +++ b/src/qml/compiler/qv4compilationunitmapper.cpp @@ -53,6 +53,40 @@ CompilationUnitMapper::CompilationUnitMapper() } +CompilationUnitMapper::~CompilationUnitMapper() +{ + close(); +} + +bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const QString &sourcePath, QString *errorString) +{ + if (strncmp(header->magic, CompiledData::magic_str, sizeof(header->magic))) { + *errorString = QStringLiteral("Magic bytes in the header do not match"); + return false; + } + + if (header->version != quint32(QV4_DATA_STRUCTURE_VERSION)) { + *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); + return false; + } + + if (header->qtVersion != quint32(QT_VERSION)) { + *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header->qtVersion, 0, 16).arg(QT_VERSION, 0, 16); + return false; + } + + { + QFileInfo sourceCode(sourcePath); + if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header->sourceTimeStamp) { + *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + return false; + } + } + + return true; +} + +#if !defined(Q_OS_UNIX) CompiledData::Unit *CompilationUnitMapper::open(const QString &sourcePath, QString *errorString) { close(); @@ -71,28 +105,8 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &sourcePath, QStri return nullptr; } - if (strncmp(header.magic, CompiledData::magic_str, sizeof(header.magic))) { - *errorString = QStringLiteral("Magic bytes in the header do not match"); + if (!verifyHeader(&header, sourcePath, errorString)) return nullptr; - } - - if (header.version != quint32(QV4_DATA_STRUCTURE_VERSION)) { - *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header.version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); - return nullptr; - } - - if (header.qtVersion != quint32(QT_VERSION)) { - *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header.qtVersion, 0, 16).arg(QT_VERSION, 0, 16); - return nullptr; - } - - { - QFileInfo sourceCode(sourcePath); - if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header.sourceTimeStamp) { - *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); - return nullptr; - } - } // Data structure and qt version matched, so now we can access the rest of the file safely. @@ -110,5 +124,6 @@ void CompilationUnitMapper::close() f.close(); dataPtr = nullptr; } +#endif // !defined(Q_OS_UNIX) QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h index 06ca3d0e02..119111ccd6 100644 --- a/src/qml/compiler/qv4compilationunitmapper_p.h +++ b/src/qml/compiler/qv4compilationunitmapper_p.h @@ -66,13 +66,20 @@ class CompilationUnitMapper { public: CompilationUnitMapper(); + ~CompilationUnitMapper(); CompiledData::Unit *open(const QString &sourcePath, QString *errorString); void close(); private: + static bool verifyHeader(const QV4::CompiledData::Unit *header, const QString &sourcePath, QString *errorString); + +#if defined(Q_OS_UNIX) + size_t length; +#else QFile f; - uchar *dataPtr; +#endif + void *dataPtr; }; } diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp new file mode 100644 index 0000000000..257a7616b7 --- /dev/null +++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qv4compilationunitmapper_p.h" + +#include +#include +#include + +#include "qv4compileddata_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +namespace { + struct Defer + { + std::function callback; + template + Defer(Callback &&cb) + : callback(cb) + {} + ~Defer() { callback(); } + Defer(const Defer &) = delete; + Defer &operator=(const Defer &) = delete; + }; +} + +CompiledData::Unit *CompilationUnitMapper::open(const QString &sourcePath, QString *errorString) +{ + close(); + + QByteArray cacheFileName = QFile::encodeName(sourcePath); + cacheFileName.append('c'); + int fd = qt_safe_open(cacheFileName.constData(), O_RDONLY); + if (fd == -1) { + *errorString = qt_error_string(errno); + return nullptr; + } + + Defer cleanup([fd]{ + qt_safe_close(fd) ; + }); + + CompiledData::Unit header; + qint64 bytesRead = qt_safe_read(fd, reinterpret_cast(&header), sizeof(header)); + + if (bytesRead != sizeof(header)) { + *errorString = QStringLiteral("File too small for the header fields"); + return nullptr; + } + + if (!verifyHeader(&header, sourcePath, errorString)) + return nullptr; + + // Data structure and qt version matched, so now we can access the rest of the file safely. + + length = static_cast(lseek(fd, 0, SEEK_END)); + + void *ptr = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, /*offset*/0); + if (ptr == MAP_FAILED) { + *errorString = qt_error_string(errno); + return nullptr; + } + dataPtr = ptr; + + return reinterpret_cast(dataPtr); +} + +void CompilationUnitMapper::close() +{ + if (dataPtr != nullptr) + munmap(dataPtr, length); + dataPtr = nullptr; +} + +QT_END_NAMESPACE -- cgit v1.2.3 From 89cfde8a024769c40e57c807f9b3bde5e91d3ffe Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 22 Jul 2016 16:13:54 +0300 Subject: QQmlImports: optimize excludeBaseUrl Don't allocate memory. Change-Id: I50542bd77c9b5432d452fec2a87b6858b5c4f9c7 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlimport.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 9393f74a8d..6396ad0bab 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -399,9 +399,7 @@ bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QSt if (baseUrl.startsWith(importUrl)) { - QString typeUrl(importUrl); - typeUrl.append(fileName); - if (typeUrl == baseUrl) + if (fileName == baseUrl.midRef(importUrl.size())) return false; } -- cgit v1.2.3 From 35597f301480ffc59f598be4de2c2074543725be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Str=C3=B8mme?= Date: Thu, 2 Jun 2016 12:08:29 +0200 Subject: Fix char conversions in QML This is a partial revert of 90b06e2773842, as it had unwanted side effects. The original intention was to make assignment from char to string possible, or more specifically, we wanted a solution where a QChar could be assigned to a QString, as a character and not a string representation of its value. While this behavior is desirable for QChar, we most likely want the opposite for the regular character types. Task-number: QTBUG-49232 Change-Id: I82d5f72b900fe984c4db1478fd52a9eb69ad2ee6 Reviewed-by: Michael Brasser Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4engine.cpp | 13 +++++--- src/qml/qml/qqmlproperty.cpp | 25 ++------------ tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp | 29 ++++++++++++++++ tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp | 42 ++---------------------- 4 files changed, 43 insertions(+), 66 deletions(-) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 5dc3e6151f..557b678ef8 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1143,8 +1143,13 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return value.integerValue(); if (value.isNumber()) return value.asDouble(); - if (value.isString()) - return value.stringValue()->toQString(); + if (value.isString()) { + const QString &str = value.toQString(); + // QChars are stored as a strings + if (typeHint == QVariant::Char && str.size() == 1) + return str.at(0); + return str; + } if (const QV4::QQmlLocaleData *ld = value.as()) return ld->d()->locale; if (const QV4::DateObject *d = value.as()) @@ -1284,9 +1289,9 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) case QMetaType::UShort: return QV4::Encode((int)*reinterpret_cast(ptr)); case QMetaType::Char: - return newString(QChar::fromLatin1(*reinterpret_cast(ptr)))->asReturnedValue(); + return QV4::Encode((int)*reinterpret_cast(ptr)); case QMetaType::UChar: - return newString(QChar::fromLatin1(*reinterpret_cast(ptr)))->asReturnedValue(); + return QV4::Encode((int)*reinterpret_cast(ptr)); case QMetaType::QChar: return newString(*reinterpret_cast(ptr))->asReturnedValue(); case QMetaType::QDateTime: diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 1b78ada698..1eaff6b600 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1327,29 +1327,8 @@ bool QQmlPropertyPrivate::write(QObject *object, bool ok = false; QVariant v; - if (variantType == QVariant::String) { - const QString &str = value.toString(); - const bool targetIsChar = (propertyType == qMetaTypeId() - || propertyType == qMetaTypeId() - || propertyType == qMetaTypeId()); - // If the string contains only one character and the target is a char, try converting it. - if (targetIsChar) { - if (str.size() != 1) - return false; // We can only convert if the string contains exactly one character. - - const QChar &qChar = str.at(0); - if (propertyType == qMetaTypeId()) { - v = qChar; - ok = true; - } else if (propertyType == qMetaTypeId() || propertyType == qMetaTypeId()) { - const char c = qChar.toLatin1(); - v = c; - ok = (qChar == c); - } - } else { - v = QQmlStringConverters::variantFromString(str, propertyType, &ok); - } - } + if (variantType == QVariant::String) + v = QQmlStringConverters::variantFromString(value.toString(), propertyType, &ok); if (!ok) { v = value; diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index d338e6f5ad..739af3782e 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -66,6 +66,7 @@ private slots: void qtbug_22535(); void evalAfterInvalidate(); void qobjectDerived(); + void qtbug_49232(); private: QQmlEngine engine; @@ -205,6 +206,8 @@ class TestObject : public QObject Q_PROPERTY(int a READ a NOTIFY aChanged) Q_PROPERTY(int b READ b NOTIFY bChanged) Q_PROPERTY(int c READ c NOTIFY cChanged) + Q_PROPERTY(char d READ d NOTIFY dChanged) + Q_PROPERTY(uchar e READ e NOTIFY eChanged) public: TestObject() : _a(10), _b(10), _c(10) {} @@ -218,15 +221,25 @@ public: int c() const { return _c; } void setC(int c) { _c = c; emit cChanged(); } + char d() const { return _d; } + void setD(char d) { _d = d; emit dChanged(); } + + uchar e() const { return _e; } + void setE(uchar e) { _e = e; emit eChanged(); } + signals: void aChanged(); void bChanged(); void cChanged(); + void dChanged(); + void eChanged(); private: int _a; int _b; int _c; + char _d; + uchar _e; }; #define TEST_CONTEXT_PROPERTY(ctxt, name, value) \ @@ -699,6 +712,22 @@ void tst_qqmlcontext::qobjectDerived() QCOMPARE(command.count, 2); } +void tst_qqmlcontext::qtbug_49232() +{ + TestObject testObject; + testObject.setD('a'); + testObject.setE(97); + + QQmlEngine engine; + engine.rootContext()->setContextProperty("TestObject", &testObject); + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0; QtObject { property int valueOne: TestObject.d; property int valueTwo: TestObject.e }", QUrl()); + QScopedPointer obj(component.create()); + + QCOMPARE(obj->property("valueOne"), QVariant('a')); + QCOMPARE(obj->property("valueTwo"), QVariant(97)); +} + QTEST_MAIN(tst_qqmlcontext) #include "tst_qqmlcontext.moc" diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 6ada14ce79..86c13065fa 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -329,11 +329,8 @@ class PropertyObject : public QObject Q_PROPERTY(MyQmlObject *qmlObject READ qmlObject) Q_PROPERTY(MyQObject *qObject READ qObject WRITE setQObject NOTIFY qObjectChanged) Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty) - Q_PROPERTY(char charProperty READ charProperty WRITE setCharProperty) Q_PROPERTY(QChar qcharProperty READ qcharProperty WRITE setQcharProperty) Q_PROPERTY(QChar constQChar READ constQChar STORED false CONSTANT FINAL) - Q_PROPERTY(char constChar READ constChar STORED false CONSTANT FINAL) - Q_PROPERTY(int constInt READ constInt STORED false CONSTANT FINAL) Q_CLASSINFO("DefaultProperty", "defaultProperty") public: @@ -370,15 +367,11 @@ public: } QString stringProperty() const { return m_stringProperty;} - char charProperty() const { return m_charProperty; } QChar qcharProperty() const { return m_qcharProperty; } QChar constQChar() const { return 0x25cf; /* Unicode: black circle */ } - char constChar() const { return 'A'; } - int constInt() const { return 123456; } void setStringProperty(QString arg) { m_stringProperty = arg; } - void setCharProperty(char arg) { m_charProperty = arg; } void setQcharProperty(QChar arg) { m_qcharProperty = arg; } signals: @@ -395,7 +388,6 @@ private: MyQmlObject m_qmlObject; MyQObject *m_qObject; QString m_stringProperty; - char m_charProperty; QChar m_qcharProperty; }; @@ -1408,23 +1400,14 @@ void tst_qqmlproperty::write() // Char/string-property { PropertyObject o; - QQmlProperty charProperty(&o, "charProperty"); QQmlProperty qcharProperty(&o, "qcharProperty"); QQmlProperty stringProperty(&o, "stringProperty"); const int black_circle = 0x25cf; - QCOMPARE(charProperty.write(QString("foo")), false); - QCOMPARE(charProperty.write('Q'), true); - QCOMPARE(charProperty.read(), QVariant('Q')); - QCOMPARE(charProperty.write(QString("t")), true); - QCOMPARE(charProperty.read(), QVariant('t')); - QCOMPARE(qcharProperty.write(QString("foo")), false); QCOMPARE(qcharProperty.write('Q'), true); QCOMPARE(qcharProperty.read(), QVariant('Q')); - QCOMPARE(qcharProperty.write(QString("t")), true); - QCOMPARE(qcharProperty.read(), QVariant('t')); QCOMPARE(qcharProperty.write(QChar(black_circle)), true); QCOMPARE(qcharProperty.read(), QVariant(QChar(black_circle))); @@ -1433,19 +1416,10 @@ void tst_qqmlproperty::write() QCOMPARE(o.stringProperty(), QString("bar")); QCOMPARE(stringProperty.write(QVariant(1234)), true); QCOMPARE(stringProperty.read().toString(), QString::number(1234)); + QCOMPARE(stringProperty.write('A'), true); + QCOMPARE(stringProperty.read().toString(), QString::number('A')); QCOMPARE(stringProperty.write(QChar(black_circle)), true); - QCOMPARE(stringProperty.read(), QVariant(QString(QChar(black_circle)))); - - { // char -> QString - QQmlComponent component(&engine); - component.setData("import Test 1.0\nPropertyObject { stringProperty: constChar }", QUrl()); - PropertyObject *obj = qobject_cast(component.create()); - QVERIFY(obj != 0); - if (obj) { - QQmlProperty stringProperty(obj, "stringProperty"); - QCOMPARE(stringProperty.read(), QVariant(QString(obj->constChar()))); - } - } + QCOMPARE(stringProperty.read(), QVariant(QChar(black_circle))); { // QChar -> QString QQmlComponent component(&engine); @@ -1458,16 +1432,6 @@ void tst_qqmlproperty::write() } } - { // int -> QString - QQmlComponent component(&engine); - component.setData("import Test 1.0\nPropertyObject { stringProperty: constInt }", QUrl()); - PropertyObject *obj = qobject_cast(component.create()); - QVERIFY(obj != 0); - if (obj) { - QQmlProperty stringProperty(obj, "stringProperty"); - QCOMPARE(stringProperty.read(), QVariant(QString::number(obj->constInt()))); - } - } } // VariantMap-property -- cgit v1.2.3 From 4493524ec24afb946eba3942f48d9fc1ff3192c1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 9 Aug 2016 10:49:22 +0200 Subject: V4: Align stack on 16 byte boundaries in the YarrJIT This is the required alignment for Aarch64, and a number of other ABIs prefer this size too when calling into system libraries. Change-Id: Ie38cabb77cf83543b915553e69c5c5728a67503b Reviewed-by: Simon Hausmann --- src/3rdparty/masm/yarr/YarrJIT.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/3rdparty/masm/yarr/YarrJIT.cpp b/src/3rdparty/masm/yarr/YarrJIT.cpp index 5664c585b9..d8211ec4b2 100644 --- a/src/3rdparty/masm/yarr/YarrJIT.cpp +++ b/src/3rdparty/masm/yarr/YarrJIT.cpp @@ -338,17 +338,31 @@ class YarrGenerator : private MacroAssembler { jump(Address(stackPointerRegister, frameLocation * sizeof(void*))); } + unsigned alignCallFrameSizeInBytes(unsigned callFrameSize) + { + callFrameSize *= sizeof(void*); + if (callFrameSize / sizeof(void*) != m_pattern.m_body->m_callFrameSize) + CRASH(); + // Originally, the code was: +// callFrameSize = (callFrameSize + 0x3f) & ~0x3f; + // However, 64 bytes is a bit surprising. The biggest "alignment" requirement is on Aarch64, where: + // "SP mod 16 = 0. The stack must be quad-word aligned." (IHI0055B_aapcs64.pdf) + callFrameSize = (callFrameSize + 0xf) & ~0xf; + if (!callFrameSize) + CRASH(); + return callFrameSize; + } void initCallFrame() { unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; if (callFrameSize) - subPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + subPtr(Imm32(alignCallFrameSizeInBytes(callFrameSize)), stackPointerRegister); } void removeCallFrame() { unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; if (callFrameSize) - addPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + addPtr(Imm32(alignCallFrameSizeInBytes(callFrameSize)), stackPointerRegister); } // Used to record subpatters, should only be called if compileMode is IncludeSubpatterns. @@ -2565,6 +2579,10 @@ class YarrGenerator : private MacroAssembler { if (compileMode == IncludeSubpatterns) loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output); #endif +#elif CPU(ARM64) + // The ABI doesn't guarantee the upper bits are zero on unsigned arguments, so clear them ourselves. + zeroExtend32ToPtr(index, index); + zeroExtend32ToPtr(length, length); #elif CPU(ARM) push(ARMRegisters::r4); push(ARMRegisters::r5); -- cgit v1.2.3 From 5e7720d3fba1865439bf21fd564152b8b9a70f13 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 22 Jul 2016 09:25:06 +0200 Subject: qmlplugindump: fix regexp for plugins.qmltypes The latest plugins.qmltypes files contain whitespace after "dependencies:". Furthermore, just for the sake of correctness, escape dots in the QtQuick.tooling module import URI and version. Change-Id: I54fa288bc530f7852747c75b4d656e789d519cf2 Reviewed-by: Mitch Curtis Reviewed-by: Thomas Hartmann Reviewed-by: Marco Benelli --- tools/qmlplugindump/qmltypereader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/qmlplugindump/qmltypereader.cpp b/tools/qmlplugindump/qmltypereader.cpp index 67ba415388..9dfb6fc1e0 100644 --- a/tools/qmlplugindump/qmltypereader.cpp +++ b/tools/qmlplugindump/qmltypereader.cpp @@ -40,7 +40,7 @@ #include QStringList readQmlTypes(const QString &filename) { - QRegularExpression re("import QtQuick.tooling 1.2.*Module {\\s*dependencies:\\[([^\\]]*)\\](.*)}", + QRegularExpression re("import QtQuick\\.tooling 1\\.2.*Module {\\s*dependencies:\\s*\\[([^\\]]*)\\](.*)}", QRegularExpression::DotMatchesEverythingOption); if (!QFileInfo(filename).exists()) { std::cerr << "Non existing file: " << filename.toStdString() << std::endl; -- cgit v1.2.3 From 28a055ce33a72651ae8a55bc673a26a6999c30ff Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 9 Aug 2016 15:03:16 +0300 Subject: QQmlXMLHttpRequest: mark header() and headers() as const These methods do not modify the object. Change-Id: Idd8f6f36912a76eb3afca48b6070a1077e586b44 Reviewed-by: Ulf Hermann --- src/qml/qml/qqmlxmlhttprequest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 3f56cc24e2..14c12c2542 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1016,8 +1016,8 @@ public: ReturnedValue abort(Object *thisObject, QQmlContextData *context); void addHeader(const QString &, const QString &); - QString header(const QString &name); - QString headers(); + QString header(const QString &name) const; + QString headers() const; QString responseBody(); const QByteArray & rawResponseBody() const; @@ -1144,7 +1144,7 @@ void QQmlXMLHttpRequest::addHeader(const QString &name, const QString &value) } } -QString QQmlXMLHttpRequest::header(const QString &name) +QString QQmlXMLHttpRequest::header(const QString &name) const { QByteArray utfname = name.toLower().toUtf8(); @@ -1155,7 +1155,7 @@ QString QQmlXMLHttpRequest::header(const QString &name) return QString(); } -QString QQmlXMLHttpRequest::headers() +QString QQmlXMLHttpRequest::headers() const { QString ret; -- cgit v1.2.3 From fa99390154844307448dd21dc3d56543349d3891 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Aug 2016 15:26:41 +0200 Subject: Centralized deferred cleanup handling Move the lambda cleanup code into a shared header file for re-use. Change-Id: Ib9fb7dce98200bcad2bb688740f9b6c2a1e4aae7 Reviewed-by: Ulf Hermann --- src/qml/compiler/qv4compilationunitmapper_unix.cpp | 17 +---- src/qml/qml/ftw/ftw.pri | 1 + src/qml/qml/ftw/qdeferredcleanup_p.h | 74 ++++++++++++++++++++++ src/qml/qml/qqmltypeloader.cpp | 15 +---- 4 files changed, 79 insertions(+), 28 deletions(-) create mode 100644 src/qml/qml/ftw/qdeferredcleanup_p.h diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp index 257a7616b7..7119acc80e 100644 --- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include "qv4compileddata_p.h" @@ -49,20 +50,6 @@ QT_BEGIN_NAMESPACE using namespace QV4; -namespace { - struct Defer - { - std::function callback; - template - Defer(Callback &&cb) - : callback(cb) - {} - ~Defer() { callback(); } - Defer(const Defer &) = delete; - Defer &operator=(const Defer &) = delete; - }; -} - CompiledData::Unit *CompilationUnitMapper::open(const QString &sourcePath, QString *errorString) { close(); @@ -75,7 +62,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &sourcePath, QStri return nullptr; } - Defer cleanup([fd]{ + QDeferredCleanup cleanup([fd]{ qt_safe_close(fd) ; }); diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri index addf1d9ff8..2d4a82e2f4 100644 --- a/src/qml/qml/ftw/ftw.pri +++ b/src/qml/qml/ftw/ftw.pri @@ -13,6 +13,7 @@ HEADERS += \ $$PWD/qflagpointer_p.h \ $$PWD/qlazilyallocated_p.h \ $$PWD/qqmlnullablevalue_p.h \ + $$PWD/qdeferredcleanup_p.h \ SOURCES += \ $$PWD/qintrusivelist.cpp \ diff --git a/src/qml/qml/ftw/qdeferredcleanup_p.h b/src/qml/qml/ftw/qdeferredcleanup_p.h new file mode 100644 index 0000000000..6b59f04a77 --- /dev/null +++ b/src/qml/qml/ftw/qdeferredcleanup_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QDEFERREDCLEANUP_P_H +#define QDEFERREDCLEANUP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include + +QT_BEGIN_NAMESPACE + +struct QDeferredCleanup +{ + std::function callback; + template + QDeferredCleanup(Callback &&cb) + : callback(cb) + {} + ~QDeferredCleanup() { callback(); } + QDeferredCleanup(const QDeferredCleanup &) = delete; + QDeferredCleanup &operator=(const QDeferredCleanup &) = delete; +}; + +QT_END_NAMESPACE + +#endif // QDEFERREDCLEANUP_P_H diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 75bc98f278..b7154625ee 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -130,18 +131,6 @@ namespace { LockHolder(LockType *l) : lock(*l) { lock.lock(); } ~LockHolder() { lock.unlock(); } }; - - struct Defer - { - std::function callback; - template - Defer(Callback &&cb) - : callback(cb) - {} - ~Defer() { callback(); } - Defer(const Defer &) = delete; - Defer &operator=(const Defer &) = delete; - }; } #ifndef QT_NO_NETWORK @@ -2172,7 +2161,7 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer Date: Tue, 9 Aug 2016 09:42:25 +0200 Subject: Remove dead code We don't use C++ exceptions anymore :) Change-Id: Iedab648ec7ab6ee490dbe992d32d6a0017f26a41 Reviewed-by: Lars Knoll --- tools/fdegen/fdegen.pro | 8 -- tools/fdegen/main.cpp | 362 ------------------------------------------------ 2 files changed, 370 deletions(-) delete mode 100644 tools/fdegen/fdegen.pro delete mode 100644 tools/fdegen/main.cpp diff --git a/tools/fdegen/fdegen.pro b/tools/fdegen/fdegen.pro deleted file mode 100644 index a52533280e..0000000000 --- a/tools/fdegen/fdegen.pro +++ /dev/null @@ -1,8 +0,0 @@ -TEMPLATE = app -TARGET = fdegen -INCLUDEPATH += . - -# Input -SOURCES += main.cpp - -LIBS += -ldwarf -lelf diff --git a/tools/fdegen/main.cpp b/tools/fdegen/main.cpp deleted file mode 100644 index 53ee9dec2a..0000000000 --- a/tools/fdegen/main.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the V4VM module 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 - -#define DEBUG - -#ifdef DEBUG -#include -#endif - -#include - -enum DwarfRegs { -#if defined(Q_PROCESSOR_X86_64) - // X86-64 - RAX = 0, - RDX = 1, - RCX = 2, - RBX = 3, - RSI = 4, - RDI = 5, - RBP = 6, - RSP = 7, - R8 = 8, - R9 = 9, - R10 = 10, - R11 = 11, - R12 = 12, - R13 = 13, - R14 = 14, - R15 = 15, - RIP = 16, - - InstructionPointerRegister = RIP, - StackPointerRegister = RSP, - StackFrameRegister = RBP -#elif defined(Q_PROCESSOR_X86) - // x86 - EAX = 0, - EDX = 1, - ECX = 2, - EBX = 3, - ESP = 4, - EBP = 5, - ESI = 6, - EDI = 7, - EIP = 8, - - InstructionPointerRegister = EIP, - StackPointerRegister = ESP, - StackFrameRegister = EBP -#else -#error Not ported yet -#endif -}; - -static const DwarfRegs calleeSavedRegisters[] = { -#if defined(Q_PROCESSOR_X86_64) - R12, - R14 -#elif defined(Q_PROCESSOR_X86) - ESI, - EDI -#endif -}; -static const int calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); - -#if QT_POINTER_SIZE == 8 -#define Elf_Ehdr Elf64_Ehdr -#define elf_newehdr elf64_newehdr -#define Elf_Shdr Elf64_Shdr -#define elf_getshdr elf64_getshdr -#else -#define Elf_Ehdr Elf32_Ehdr -#define elf_newehdr elf32_newehdr -#define Elf_Shdr Elf32_Shdr -#define elf_getshdr elf32_getshdr -#endif - -static void die(const char *msg) -{ - fprintf(stderr, "error: %s\n", msg); - exit(1); -} - -static int createSectionCallback( - char *name, - int size, - Dwarf_Unsigned /*type*/, - Dwarf_Unsigned /*flags*/, - Dwarf_Unsigned /*link*/, - Dwarf_Unsigned /*info*/, - Dwarf_Unsigned* /*sect_name_index*/, - void * /*user_data*/, - int* /*error*/) -{ - if (strcmp(name, ".debug_frame")) - return 0; - fprintf(stderr, "createsection called with %s and size %d\n", name, size); - return 1; -} - -static unsigned char cie_init_instructions[] = { - DW_CFA_def_cfa, StackPointerRegister, /*offset in bytes */sizeof(void*), - DW_CFA_offset | InstructionPointerRegister, 1, -}; - -int main() -{ - Dwarf_Error error = 0; - Dwarf_P_Debug dw = dwarf_producer_init_c(DW_DLC_WRITE | DW_DLC_SIZE_64, - createSectionCallback, - /* error handler */0, - /* error arg */0, - /* user data */0, - &error); - if (error != 0) - die("dwarf_producer_init_c failed"); - - Dwarf_Unsigned cie = dwarf_add_frame_cie(dw, - "", - /* code alignment factor */sizeof(void*), - /* data alignment factor */-sizeof(void*), - /* return address reg*/InstructionPointerRegister, - cie_init_instructions, - sizeof(cie_init_instructions), - &error); - if (error != 0) - die("dwarf_add_frame_cie failed"); - - Dwarf_P_Fde fde = dwarf_new_fde(dw, &error); - if (error != 0) - die("dwarf_new_fde failed"); - - /* New entry in state machine for code offset 1 after push rbp instruction */ - dwarf_add_fde_inst(fde, - DW_CFA_advance_loc, - /*offset in code alignment units*/ 1, - /* unused*/ 0, - &error); - - /* After "push rbp" the offset to the CFA is now 2 instead of 1 */ - dwarf_add_fde_inst(fde, - DW_CFA_def_cfa_offset_sf, - /*offset in code alignment units*/ -2, - /* unused*/ 0, - &error); - - /* After "push rbp" the value of rbp is now stored at offset 1 from CFA */ - dwarf_add_fde_inst(fde, - DW_CFA_offset, - StackFrameRegister, - 2, - &error); - - /* New entry in state machine for code offset 3 for mov rbp, rsp instruction */ - dwarf_add_fde_inst(fde, - DW_CFA_advance_loc, - /*offset in code alignment units*/ 3, - /* unused */ 0, - &error); - - /* After "mov rbp, rsp" the cfa is reachable via rbp */ - dwarf_add_fde_inst(fde, - DW_CFA_def_cfa_register, - StackFrameRegister, - /* unused */0, - &error); - - /* Callee saved registers */ - for (int i = 0; i < calleeSavedRegisterCount; ++i) { - dwarf_add_fde_inst(fde, - DW_CFA_offset, - calleeSavedRegisters[i], - i + 3, - &error); - } - - dwarf_add_frame_fde(dw, fde, - /* die */0, - cie, - /*virt addr */0, - /* length of code */0, - /* symbol index */0, - &error); - if (error != 0) - die("dwarf_add_frame_fde failed"); - - dwarf_transform_to_disk_form(dw, &error); - if (error != 0) - die("dwarf_transform_to_disk_form failed"); - - Dwarf_Unsigned len = 0; - Dwarf_Signed elfIdx = 0; - unsigned char *bytes = (unsigned char *)dwarf_get_section_bytes(dw, /* section */1, - &elfIdx, &len, &error); - if (error != 0) - die("dwarf_get_section_bytes failed"); - - // libdwarf doesn't add a terminating FDE with zero length, so let's add one - // ourselves. - unsigned char *newBytes = (unsigned char *)alloca(len + 4); - memcpy(newBytes, bytes, len); - newBytes[len] = 0; - newBytes[len + 1] = 0; - newBytes[len + 2] = 0; - newBytes[len + 3] = 0; - newBytes[len + 4] = 0; - bytes = newBytes; - len += 4; - - // Reset CIE-ID back to 0 as expected for .eh_frames - bytes[4] = 0; - bytes[5] = 0; - bytes[6] = 0; - bytes[7] = 0; - - unsigned fde_offset = bytes[0] + 4; - - bytes[fde_offset + 4] = fde_offset + 4; - - printf("static const unsigned char cie_fde_data[] = {\n"); - int i = 0; - while (i < len) { - printf(" "); - for (int j = 0; i < len && j < 8; ++j, ++i) - printf("0x%x, ", bytes[i]); - printf("\n"); - } - printf("};\n"); - - printf("static const int fde_offset = %d;\n", fde_offset); - printf("static const int initial_location_offset = %d;\n", fde_offset + 8); - printf("static const int address_range_offset = %d;\n", fde_offset + 8 + sizeof(void*)); - -#ifdef DEBUG - { - if (elf_version(EV_CURRENT) == EV_NONE) - die("wrong elf version"); - int fd = open("debug.o", O_WRONLY | O_CREAT, 0777); - if (fd < 0) - die("cannot create debug.o"); - - Elf *e = elf_begin(fd, ELF_C_WRITE, 0); - if (!e) - die("elf_begin failed"); - - Elf_Ehdr *ehdr = elf_newehdr(e); - if (!ehdr) - die(elf_errmsg(-1)); - - ehdr->e_ident[EI_DATA] = ELFDATA2LSB; -#if defined(Q_PROCESSOR_X86_64) - ehdr->e_machine = EM_X86_64; -#elif defined(Q_PROCESSOR_X86) - ehdr->e_machine = EM_386; -#else -#error port me :) -#endif - ehdr->e_type = ET_EXEC; - ehdr->e_version = EV_CURRENT; - - Elf_Scn *section = elf_newscn(e); - if (!section) - die("elf_newscn failed"); - - Elf_Data *data = elf_newdata(section); - if (!data) - die(elf_errmsg(-1)); - data->d_align = 4; - data->d_off = 0; - data->d_buf = bytes; - data->d_size = len; - data->d_type = ELF_T_BYTE; - data->d_version = EV_CURRENT; - - Elf_Shdr *shdr = elf_getshdr(section); - if (!shdr) - die(elf_errmsg(-1)); - - shdr->sh_name = 1; - shdr->sh_type = SHT_PROGBITS; - shdr->sh_entsize = 0; - - char stringTable[] = { - 0, - '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0, - '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0 - }; - - section = elf_newscn(e); - if (!section) - die("elf_newscn failed"); - - data = elf_newdata(section); - if (!data) - die(elf_errmsg(-1)); - data->d_align = 1; - data->d_off = 0; - data->d_buf = stringTable; - data->d_size = sizeof(stringTable); - data->d_type = ELF_T_BYTE; - data->d_version = EV_CURRENT; - - shdr = elf_getshdr(section); - if (!shdr) - die(elf_errmsg(-1)); - - shdr->sh_name = 11; - shdr->sh_type = SHT_STRTAB; - shdr->sh_flags = SHF_STRINGS | SHF_ALLOC; - shdr->sh_entsize = 0; - - ehdr->e_shstrndx = elf_ndxscn(section); - - if (elf_update(e, ELF_C_WRITE) < 0) - die(elf_errmsg(-1)); - - elf_end(e); - close(fd); - } -#endif - - dwarf_producer_finish(dw, &error); - if (error != 0) - die("dwarf_producer_finish failed"); - return 0; -} -- cgit v1.2.3 From 4fbfbf7fe8e6f8f31b5704a0f8f55415a7284523 Mon Sep 17 00:00:00 2001 From: Cavit Sina Dogru Date: Wed, 3 Aug 2016 17:54:26 +0300 Subject: Qt object: Add exit(int) This is similar to the Qt.quit() function but also specifies the return code that the event loop will return. [ChangeLog][QtQml] Added exit(int retCode) method to the Qt global object. An application can call Qt.exit to specify a return code of the engine. Task-number: QTBUG-54360 Change-Id: Iaa319e6dc4d6b99dc3a5c01845e87b936fd2cab0 Reviewed-by: Shawn Rutledge --- src/qml/qml/qqmlapplicationengine.cpp | 1 + src/qml/qml/qqmlengine.cpp | 19 +++++++++++++++++++ src/qml/qml/qqmlengine.h | 1 + src/qml/qml/qqmlengine_p.h | 1 + src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 25 +++++++++++++++++++++++++ src/qml/qml/v8/qqmlbuiltinfunctions_p.h | 1 + tests/auto/qml/qqmlqt/data/exit.qml | 7 +++++++ tests/auto/qml/qqmlqt/tst_qqmlqt.cpp | 15 +++++++++++++++ tools/qml/main.cpp | 17 ++++++++++++++--- tools/qmlscene/main.cpp | 1 + 10 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 tests/auto/qml/qqmlqt/data/exit.qml diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 30fc186391..fef2da753b 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -69,6 +69,7 @@ void QQmlApplicationEnginePrivate::init() q->connect(&statusMapper, SIGNAL(mapped(QObject*)), q, SLOT(_q_finishLoad(QObject*))); q->connect(q, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); + q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit); #ifndef QT_NO_TRANSLATION QTranslator* qtTranslator = new QTranslator; if (qtTranslator->load(QLatin1String("qt_") + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 2b49eae4de..cdaa9e83e9 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -982,8 +982,19 @@ QQmlEngine::~QQmlEngine() /*! \fn void QQmlEngine::quit() This signal is emitted when the QML loaded by the engine would like to quit. + + \sa exit() */ +/*! \fn void QQmlEngine::exit(int retCode) + This signal is emitted when the QML loaded by the engine would like to exit + from the event loop with the specified return code. + + \since 5.8 + \sa quit() + */ + + /*! \fn void QQmlEngine::warnings(const QList &warnings) This signal is emitted when \a warnings messages are generated by QML. */ @@ -1837,6 +1848,14 @@ void QQmlEnginePrivate::sendQuit() } } +void QQmlEnginePrivate::sendExit(int retCode) +{ + Q_Q(QQmlEngine); + if (q->receivers(SIGNAL(exit(int))) == 0) + qWarning("Signal QQmlEngine::exit() emitted, but no receivers connected to handle it."); + emit q->exit(retCode); +} + static void dumpwarning(const QQmlError &error) { QMessageLogger(error.url().toString().toLatin1().constData(), diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 132af78f80..bbb9c36ce9 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -155,6 +155,7 @@ protected: Q_SIGNALS: void quit(); + void exit(int retCode); void warnings(const QList &warnings); private: diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 949060f395..713b03dbf3 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -227,6 +227,7 @@ public: bool isScriptLoaded(const QUrl &url) const; void sendQuit(); + void sendExit(int retCode = 0); void warning(const QQmlError &); void warning(const QList &); void warning(QQmlDelayedError *); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 76a900c846..db0439ab9e 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -137,6 +137,7 @@ Heap::QtObject::QtObject(QQmlEngine *qmlEngine) o->defineDefaultProperty(QStringLiteral("darker"), QV4::QtObject::method_darker); o->defineDefaultProperty(QStringLiteral("tint"), QV4::QtObject::method_tint); o->defineDefaultProperty(QStringLiteral("quit"), QV4::QtObject::method_quit); + o->defineDefaultProperty(QStringLiteral("exit"), QV4::QtObject::method_exit); o->defineDefaultProperty(QStringLiteral("createQmlObject"), QV4::QtObject::method_createQmlObject); o->defineDefaultProperty(QStringLiteral("createComponent"), QV4::QtObject::method_createComponent); } @@ -992,6 +993,8 @@ This function causes the QQmlEngine::quit() signal to be emitted. Within the \l {Prototyping with qmlscene}, this causes the launcher application to exit; to quit a C++ application when this method is called, connect the QQmlEngine::quit() signal to the QCoreApplication::quit() slot. + +\sa exit() */ ReturnedValue QtObject::method_quit(CallContext *ctx) { @@ -999,6 +1002,28 @@ ReturnedValue QtObject::method_quit(CallContext *ctx) return QV4::Encode::undefined(); } +/*! + \qmlmethod Qt::exit(int retCode) + + This function causes the QQmlEngine::exit(int) signal to be emitted. + Within the \l {Prototyping with qmlscene}, this causes the launcher application to exit + the specified return code. To exit from the event loop with a specified return code when this + method is called, a C++ application can connect the QQmlEngine::exit(int) signal + to the QCoreApplication::exit(int) slot. + + \sa quit() +*/ +ReturnedValue QtObject::method_exit(CallContext *ctx) +{ + if (ctx->argc() != 1) + V4THROW_ERROR("Qt.exit(): Invalid arguments"); + + int retCode = ctx->args()[0].toNumber(); + + QQmlEnginePrivate::get(ctx->engine()->qmlEngine())->sendExit(retCode); + return QV4::Encode::undefined(); +} + /*! \qmlmethod object Qt::createQmlObject(string qml, object parent, string filepath) diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index d29983c476..8c0759679a 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -122,6 +122,7 @@ struct QtObject : Object static ReturnedValue method_btoa(CallContext *ctx); static ReturnedValue method_atob(CallContext *ctx); static ReturnedValue method_quit(CallContext *ctx); + static ReturnedValue method_exit(CallContext *ctx); static ReturnedValue method_resolvedUrl(CallContext *ctx); static ReturnedValue method_createQmlObject(CallContext *ctx); static ReturnedValue method_createComponent(CallContext *ctx); diff --git a/tests/auto/qml/qqmlqt/data/exit.qml b/tests/auto/qml/qqmlqt/data/exit.qml new file mode 100644 index 0000000000..12727d9f04 --- /dev/null +++ b/tests/auto/qml/qqmlqt/data/exit.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +QtObject { + property int returnCode: -1 + Component.onCompleted: Qt.exit(returnCode) +} + diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index 8150241e4a..69791085c5 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -87,6 +87,7 @@ private slots: void atob(); void fontFamilies(); void quit(); + void exit(); void resolvedUrl(); void later_data(); void later(); @@ -980,6 +981,20 @@ void tst_qqmlqt::quit() delete object; } +void tst_qqmlqt::exit() +{ + QQmlComponent component(&engine, testFileUrl("exit.qml")); + + QSignalSpy spy(&engine, &QQmlEngine::exit); + QObject *object = component.create(); + QVERIFY(object != Q_NULLPTR); + QCOMPARE(spy.count(), 1); + QList arguments = spy.takeFirst(); + QVERIFY(arguments.at(0).toInt() == object->property("returnCode").toInt()); + + delete object; +} + void tst_qqmlqt::resolvedUrl() { QQmlComponent component(&engine, testFileUrl("resolvedUrl.qml")); diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp index a795144984..d718067616 100644 --- a/tools/qml/main.cpp +++ b/tools/qml/main.cpp @@ -160,18 +160,23 @@ public: LoadWatcher(QQmlApplicationEngine *e, int expected) : QObject(e) , earlyExit(false) + , returnCode(0) , expect(expected) , haveOne(false) { connect(e, SIGNAL(objectCreated(QObject*,QUrl)), this, SLOT(checkFinished(QObject*))); // QQmlApplicationEngine also connects quit() to QCoreApplication::quit - // but if called before exec() then QCoreApplication::quit does nothing + // and exit() to QCoreApplication::exit but if called before exec() + // then QCoreApplication::quit or QCoreApplication::exit does nothing connect(e, SIGNAL(quit()), this, SLOT(quit())); + connect(e, &QQmlEngine::exit, + this, &LoadWatcher::exit); } bool earlyExit; + int returnCode; private: void contain(QObject *o, const QUrl &containPath); @@ -196,14 +201,20 @@ public Q_SLOTS: if (! --expect) { printf("qml: Did not load any objects, exiting.\n"); - exit(2);//Different return code from qFatal + std::exit(2);//Different return code from qFatal } } void quit() { //Will be checked before calling exec() earlyExit = true; + returnCode = 0; } + void exit(int retCode) { + earlyExit = true; + returnCode = retCode; + } + #if defined(QT_GUI_LIB) && !defined(QT_NO_OPENGL) void onOpenGlContextCreated(QOpenGLContext *context); #endif @@ -582,7 +593,7 @@ int main(int argc, char *argv[]) } if (lw->earlyExit) - return 0; + return lw->returnCode; return app->exec(); } diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp index 1185a8e7ae..0e542ab0c8 100644 --- a/tools/qmlscene/main.cpp +++ b/tools/qmlscene/main.cpp @@ -564,6 +564,7 @@ int main(int argc, char ** argv) loadDummyDataFiles(engine, fi.path()); } QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); + QObject::connect(&engine, &QQmlEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit); component->loadUrl(options.url); while (component->isLoading()) QCoreApplication::processEvents(); -- cgit v1.2.3 From b11421394b4745253a046461234906feb9c811dc Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Wed, 20 Jul 2016 14:23:59 +0300 Subject: Qml: optimize string usage Use QStringBuilder more. Use QString::asprintf instead of arg()'s chain. Use += operator to reserve extra capacity for possible free following append/prepend/+= call. Change-Id: Ib65398b91566994339d2c4bbfaf94e49806b7471 Reviewed-by: Shawn Rutledge --- src/qml/qml/qqmlbinding.cpp | 14 ++++++-------- src/qml/qml/qqmlboundsignal.cpp | 4 ++-- src/qml/qml/qqmlerror.cpp | 4 ++-- src/qml/qml/qqmlimport.cpp | 23 ++++++++--------------- src/qml/qml/qqmlproperty.cpp | 6 +----- src/qml/qml/qqmlpropertycache.cpp | 4 +++- src/qml/qml/qqmltypeloader.cpp | 13 +++++++------ src/qml/qml/qqmlvaluetype.cpp | 6 +++--- src/qml/qml/qqmlvaluetypewrapper.cpp | 3 ++- src/qml/qml/qqmlvmemetaobject.cpp | 8 ++++---- src/qml/qml/qqmlxmlhttprequest.cpp | 4 ++-- src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 12 ++++++------ 12 files changed, 46 insertions(+), 55 deletions(-) diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 72c7347a68..3b3e23c891 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -378,12 +378,11 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, QJSValue(QV8Engine::getV4(v8engine), result.asReturnedValue())), context(), flags); } else if (isUndefined) { - QString errorStr = QLatin1String("Unable to assign [undefined] to "); - if (!QMetaType::typeName(type)) - errorStr += QLatin1String("[unknown property type]"); - else - errorStr += QLatin1String(QMetaType::typeName(type)); - delayedError()->setErrorDescription(errorStr); + const QLatin1String typeName(QMetaType::typeName(type) + ? QMetaType::typeName(type) + : "[unknown property type]"); + delayedError()->setErrorDescription(QLatin1String("Unable to assign [undefined] to ") + + typeName); return false; } else if (const QV4::FunctionObject *f = result.as()) { if (f->isBinding()) @@ -458,8 +457,7 @@ QString QQmlBinding::expressionIdentifier() QString url = function->sourceFile(); quint16 lineNumber = function->compiledFunction->location.line; quint16 columnNumber = function->compiledFunction->location.column; - - return url + QLatin1Char(':') + QString::number(lineNumber) + QLatin1Char(':') + QString::number(columnNumber); + return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber)); } void QQmlBinding::expressionChanged() diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index ef837183db..4e63790290 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -80,8 +80,8 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, // Add some leading whitespace to account for the binding's column offset. // It's 2 off because a, we start counting at 1 and b, the '(' below is not counted. - function.fill(QChar(QChar::Space), qMax(column, (quint16)2) - 2); - function += QLatin1String("(function ") + handlerName + QLatin1Char('('); + function += QString(qMax(column, (quint16)2) - 2, QChar(QChar::Space)) + + QLatin1String("(function ") + handlerName + QLatin1Char('('); if (parameterString.isEmpty()) { QString error; diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp index 74ceeabeb4..b309550ca8 100644 --- a/src/qml/qml/qqmlerror.cpp +++ b/src/qml/qml/qqmlerror.cpp @@ -249,9 +249,9 @@ QString QQmlError::toString() const int l(line()); if (u.isEmpty() || (u.isLocalFile() && u.path().isEmpty())) - rv = QLatin1String(""); + rv += QLatin1String(""); else - rv = u.toString(); + rv += u.toString(); if (l != -1) { rv += QLatin1Char(':') + QString::number(l); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 6396ad0bab..63e8da3aa8 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -538,10 +538,10 @@ QString QQmlImports::versionString(int vmaj, int vmin, ImportVersion version) { if (version == QQmlImports::FullyVersioned) { // extension with fully encoded version number (eg. MyModule.3.2) - return QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin); + return QString::asprintf(".%d.%d", vmaj, vmin); } else if (version == QQmlImports::PartiallyVersioned) { // extension with encoded version major (eg. MyModule.3) - return QString(QLatin1String(".%1")).arg(vmaj); + return QString::asprintf(".%d", vmaj); } // else extension without version number (eg. MyModule) return QString(); } @@ -1393,15 +1393,9 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix // The uri for this import. For library imports this is the same as uri // specified by the user, but it may be different in the case of file imports. QString importUri = uri; - - QString qmldirPath = importUri; - if (importUri.endsWith(Slash)) - qmldirPath += String_qmldir; - else - qmldirPath += Slash_qmldir; - - QString qmldirUrl = resolveLocalUrl(base, qmldirPath); - + QString qmldirUrl = resolveLocalUrl(base, importUri + (importUri.endsWith(Slash) + ? String_qmldir + : Slash_qmldir)); QString qmldirIdentifier; if (QQmlFile::isLocalFile(qmldirUrl)) { @@ -1699,10 +1693,9 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, if (!resolvedPath.endsWith(Slash)) resolvedPath += Slash; + resolvedPath += prefix + baseName; foreach (const QString &suffix, suffixes) { - QString pluginFileName = prefix + baseName + suffix; - - QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName); + const QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + suffix); if (!absolutePath.isEmpty()) return absolutePath; } @@ -1955,7 +1948,7 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba #ifndef QT_NO_LIBRARY // Dynamic plugins are differentiated by their filepath. For static plugins we // don't have that information so we use their address as key instead. - QString uniquePluginID = QString().sprintf("%p", instance); + const QString uniquePluginID = QString::asprintf("%p", instance); StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); QMutexLocker lock(&plugins->mutex); diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index ffa9bfa3a7..e287b68385 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -644,15 +644,11 @@ QString QQmlProperty::name() const // ### if (!d->object) { } else if (d->isValueType()) { - QString rv = d->core.name(d->object) + QLatin1Char('.'); - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType); Q_ASSERT(valueTypeMetaObject); const char *vtName = valueTypeMetaObject->property(d->valueTypeData.coreIndex).name(); - rv += QString::fromUtf8(vtName); - - d->nameCache = rv; + d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(vtName); } else if (type() & SignalProperty) { QString name = QLatin1String("on") + d->core.name(d->object); name[2] = name.at(2).toUpper(); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 71b3fd8309..38fddc8253 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -1195,7 +1195,9 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) if (data->propType != 0) returnType = QMetaType::typeName(data->propType); - QByteArray signature = methods.at(ii).first.toUtf8() + '('; + QByteArray signature; + // '+=' reserves extra capacity. Follow-up appending will be probably free. + signature += methods.at(ii).first.toUtf8() + '('; QQmlPropertyCacheMethodArguments *arguments = 0; if (data->hasArguments()) { diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index b7154625ee..a151f50a00 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2534,13 +2534,13 @@ void QQmlTypeData::resolveTypes() ScriptReference ref; //ref.location = ... - ref.qualifier = script.nameSpace; if (!script.qualifier.isEmpty()) { - ref.qualifier.prepend(script.qualifier + QLatin1Char('.')); - + ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace; // Add a reference to the enclosing namespace m_namespaces.insert(script.qualifier); + } else { + ref.qualifier = script.nameSpace; } ref.script = blob; @@ -2550,12 +2550,13 @@ void QQmlTypeData::resolveTypes() // Lets handle resolved composite singleton types foreach (const QQmlImports::CompositeSingletonReference &csRef, m_importCache.resolvedCompositeSingletons()) { TypeReference ref; - QString typeName = csRef.typeName; - + QString typeName; if (!csRef.prefix.isEmpty()) { - typeName.prepend(csRef.prefix + QLatin1Char('.')); + typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName; // Add a reference to the enclosing namespace m_namespaces.insert(csRef.prefix); + } else { + typeName = csRef.typeName; } int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1; diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index eefe7a5fb8..bcefad0ee3 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -260,7 +260,7 @@ int QQmlValueType::metaCall(QObject *, QMetaObject::Call type, int _id, void **a QString QQmlPointFValueType::toString() const { - return QString(QLatin1String("QPointF(%1, %2)")).arg(v.x()).arg(v.y()); + return QString::asprintf("QPointF(%g, %g)", v.x(), v.y()); } qreal QQmlPointFValueType::x() const @@ -307,7 +307,7 @@ void QQmlPointValueType::setY(int y) QString QQmlSizeFValueType::toString() const { - return QString(QLatin1String("QSizeF(%1, %2)")).arg(v.width()).arg(v.height()); + return QString::asprintf("QSizeF(%g, %g)", v.width(), v.height()); } qreal QQmlSizeFValueType::width() const @@ -353,7 +353,7 @@ void QQmlSizeValueType::setHeight(int h) QString QQmlRectFValueType::toString() const { - return QString(QLatin1String("QRectF(%1, %2, %3, %4)")).arg(v.x()).arg(v.y()).arg(v.width()).arg(v.height()); + return QString::asprintf("QRectF(%g, %g, %g, %g)", v.x(), v.y(), v.width(), v.height()); } qreal QQmlRectFValueType::x() const diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 3ad53aa3fc..c4008d2a05 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -332,7 +332,8 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(CallContext *ctx) if (QMetaType::convert(w->d()->gadgetPtr, w->d()->valueType->typeId, &convertResult, QMetaType::QString)) { result = convertResult; } else { - result = QString::fromUtf8(QMetaType::typeName(w->d()->valueType->typeId)) + QLatin1Char('('); + result += QString::fromUtf8(QMetaType::typeName(w->d()->valueType->typeId)) + + QLatin1Char('('); const QMetaObject *mo = w->d()->propertyCache->metaObject(); const int propCount = mo->propertyCount(); for (int i = 0; i < propCount; ++i) { diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 81bc6846e6..47c125355b 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -932,10 +932,10 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * // are not rewritten correctly but this bug is deemed out-of-scope to fix for // performance reasons; see QTBUG-24064) and thus compilation will have failed. QQmlError e; - e.setDescription(QString::fromLatin1("Exception occurred during compilation of " - "function: %1") - .arg(QString::fromUtf8(QMetaObject::method(_id) - .methodSignature()))); + e.setDescription(QLatin1String("Exception occurred during compilation of " + "function: ") + + QString::fromUtf8(QMetaObject::method(_id) + .methodSignature())); ep->warning(e); return -1; // The dynamic method with that id is not available. } diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 14c12c2542..58ffab9ec9 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1162,8 +1162,8 @@ QString QQmlXMLHttpRequest::headers() const foreach (const HeaderPair &header, m_headersList) { if (ret.length()) ret.append(QLatin1String("\r\n")); - ret = ret % QString::fromUtf8(header.first) % QLatin1String(": ") - % QString::fromUtf8(header.second); + ret += QString::fromUtf8(header.first) + QLatin1String(": ") + + QString::fromUtf8(header.second); } return ret; } diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index db0439ab9e..fd36f76d0e 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1057,7 +1057,9 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) struct Error { static ReturnedValue create(QV4::ExecutionEngine *v4, const QList &errors) { Scope scope(v4); - QString errorstr = QLatin1String("Qt.createQmlObject(): failed to create object: "); + QString errorstr; + // '+=' reserves extra capacity. Follow-up appending will be probably free. + errorstr += QLatin1String("Qt.createQmlObject(): failed to create object: "); QV4::ScopedArrayObject qmlerrors(scope, v4->newArrayObject()); QV4::ScopedObject qmlerror(scope); @@ -1497,15 +1499,13 @@ static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *c result.append(QLatin1Char(' ')); if (ctx->args()[i].as()) - result.append(QLatin1Char('[') + ctx->args()[i].toQStringNoThrow() + QLatin1Char(']')); + result += QLatin1Char('[') + ctx->args()[i].toQStringNoThrow() + QLatin1Char(']'); else result.append(ctx->args()[i].toQStringNoThrow()); } - if (printStack) { - result.append(QLatin1Char('\n')); - result.append(jsStack(v4)); - } + if (printStack) + result += QLatin1Char('\n') + jsStack(v4); static QLoggingCategory qmlLoggingCategory("qml"); static QLoggingCategory jsLoggingCategory("js"); -- cgit v1.2.3 From 3d618b58b4f138717dffc81c9c421fe4398dd30c Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 9 Aug 2016 15:08:57 +0300 Subject: QQmlXMLHttpRequest: replace remaining 'foreach' with 'range for' Change-Id: I6724e6d9a70f6bf3cf7e08e33eb101f9a85c791c Reviewed-by: Ulf Hermann --- src/qml/qml/qqmlxmlhttprequest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 58ffab9ec9..0310d42fa4 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1148,7 +1148,7 @@ QString QQmlXMLHttpRequest::header(const QString &name) const { QByteArray utfname = name.toLower().toUtf8(); - foreach (const HeaderPair &header, m_headersList) { + for (const HeaderPair &header : m_headersList) { if (header.first == utfname) return QString::fromUtf8(header.second); } @@ -1159,7 +1159,7 @@ QString QQmlXMLHttpRequest::headers() const { QString ret; - foreach (const HeaderPair &header, m_headersList) { + for (const HeaderPair &header : m_headersList) { if (ret.length()) ret.append(QLatin1String("\r\n")); ret += QString::fromUtf8(header.first) + QLatin1String(": ") -- cgit v1.2.3 From 9425f832cdc036818cb08d1bd1328345fcb6f2ff Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Aug 2016 15:46:38 +0200 Subject: Enable disk caching on Windows In order to enable the disk cache we need to replace the QFile::map usage with direct win32 file API calls in order to create executable file mappings. The files opened with QFile lack GENERIC_EXECUTE in the open flags. The code remains disabled on WinRT for now. Change-Id: I7d12267755a9de0344ac087b2ff67140531d9df0 Reviewed-by: Maurice Kalinowski --- src/qml/compiler/compiler.pri | 1 + src/qml/compiler/qv4compilationunitmapper.cpp | 40 ------- src/qml/compiler/qv4compilationunitmapper_p.h | 2 - src/qml/compiler/qv4compilationunitmapper_win.cpp | 129 ++++++++++++++++++++++ src/qml/qml/qqmltypeloader.cpp | 12 +- 5 files changed, 131 insertions(+), 53 deletions(-) create mode 100644 src/qml/compiler/qv4compilationunitmapper_win.cpp diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index e80c0236a3..e49f5c40a5 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -41,5 +41,6 @@ SOURCES += \ $$PWD/qv4compilationunitmapper.cpp unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp +else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp } diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp index 084137f17f..b53b7cf784 100644 --- a/src/qml/compiler/qv4compilationunitmapper.cpp +++ b/src/qml/compiler/qv4compilationunitmapper.cpp @@ -86,44 +86,4 @@ bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const return true; } -#if !defined(Q_OS_UNIX) -CompiledData::Unit *CompilationUnitMapper::open(const QString &sourcePath, QString *errorString) -{ - close(); - - f.setFileName(sourcePath + QLatin1Char('c')); - if (!f.open(QIODevice::ReadOnly)) { - *errorString = f.errorString(); - return nullptr; - } - - CompiledData::Unit header; - qint64 bytesRead = f.read(reinterpret_cast(&header), sizeof(header)); - - if (bytesRead != sizeof(header)) { - *errorString = QStringLiteral("File too small for the header fields"); - return nullptr; - } - - if (!verifyHeader(&header, sourcePath, errorString)) - return nullptr; - - // Data structure and qt version matched, so now we can access the rest of the file safely. - - dataPtr = f.map(/*offset*/0, f.size()); - if (!dataPtr) { - *errorString = f.errorString(); - return nullptr; - } - - return reinterpret_cast(dataPtr); -} - -void CompilationUnitMapper::close() -{ - f.close(); - dataPtr = nullptr; -} -#endif // !defined(Q_OS_UNIX) - QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h index 119111ccd6..69007f4618 100644 --- a/src/qml/compiler/qv4compilationunitmapper_p.h +++ b/src/qml/compiler/qv4compilationunitmapper_p.h @@ -76,8 +76,6 @@ private: #if defined(Q_OS_UNIX) size_t length; -#else - QFile f; #endif void *dataPtr; }; diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp new file mode 100644 index 0000000000..d58c46c090 --- /dev/null +++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qv4compilationunitmapper_p.h" + +#include "qv4compileddata_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +CompiledData::Unit *CompilationUnitMapper::open(const QString &sourcePath, QString *errorString) +{ + close(); + + // ### TODO: fix up file encoding/normalization/unc handling once QFileSystemEntry + // is exported from QtCore. + const QString cacheFileName = sourcePath + QLatin1Char('c'); + HANDLE handle = +#if defined(Q_OS_WINRT) + CreateFile2(reinterpret_cast(cacheFileName.constData()), + GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, + OPEN_EXISTING, nullptr); +#else + CreateFile(reinterpret_cast(cacheFileName.constData()), + GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + nullptr); +#endif + if (handle == INVALID_HANDLE_VALUE) { + *errorString = qt_error_string(GetLastError()); + return nullptr; + } + + QDeferredCleanup fileHandleCleanup([handle]{ + CloseHandle(handle); + }); + +#if defined(Q_OS_WINRT) + *errorString = QStringLiteral("Compilation unit mapping not supported on WinRT yet"); + return nullptr; +#else + CompiledData::Unit header; + DWORD bytesRead; + if (!ReadFile(handle, reinterpret_cast(&header), sizeof(header), &bytesRead, nullptr)) { + *errorString = qt_error_string(GetLastError()); + return false; + } + + if (bytesRead != sizeof(header)) { + *errorString = QStringLiteral("File too small for the header fields"); + return nullptr; + } + + if (!verifyHeader(&header, sourcePath, errorString)) + return nullptr; + + // Data structure and qt version matched, so now we can access the rest of the file safely. + + HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_EXECUTE_READ, 0, 0, 0); + if (!fileMappingHandle) { + *errorString = qt_error_string(GetLastError()); + return false; + } + + QDeferredCleanup mappingCleanup([fileMappingHandle]{ + CloseHandle(fileMappingHandle); + }); + + dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ | FILE_MAP_EXECUTE, 0, 0, 0); + if (!dataPtr) { + *errorString = qt_error_string(GetLastError()); + return nullptr; + } + + return reinterpret_cast(dataPtr); +#endif +} + +void CompilationUnitMapper::close() +{ +#if !defined(Q_OS_WINRT) + if (dataPtr != nullptr) + UnmapViewOfFile(dataPtr); +#endif + dataPtr = nullptr; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index a151f50a00..4fa69d22bb 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -104,22 +104,12 @@ #endif DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); -DEFINE_BOOL_CONFIG_OPTION(_disableDiskCache, QML_DISABLE_DISK_CACHE); +DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE); DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE); Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache") -static bool disableDiskCache() -{ - return _disableDiskCache() - // ### FIXME: Fix crashes on Windows with mmap'ed code. -#if defined(Q_OS_WIN) - || true -#endif - ; -} - QT_BEGIN_NAMESPACE namespace { -- cgit v1.2.3 From 6fe7ccf59b917e9383c07c1e7a71631200590e3a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 10 Aug 2016 11:45:24 +0200 Subject: V4: Fix JavaScript finally-block execution After moving all runtime functions into the Runtime class and doing indirect function calls, the code generation would always emit code to check for an exception after a call. This is problematic for methods that do not throw, but might be called when an exception is thrown. I.e. in a finally block. This is especially problematic for methods like popScope, the very first runtime method that is called in a finally block. The result was that after popScope, execution was passed over to the exception handler block for that finally block (meaning: the body of the finally block was never executed). The fix is to declare an enumerator in an anonymous enum for each runtime method that indicates if an exception check is needed. The existing ExceptionCheck templates are used to set the value. Change-Id: I5bd8bcf2a92acabf2a33b3764447de6cc364bba9 Reviewed-by: Lars Knoll --- src/qml/jit/qv4assembler_p.h | 54 +++++++++---------------------------- src/qml/jit/qv4binop.cpp | 14 +++++----- src/qml/jit/qv4binop_p.h | 1 + src/qml/jit/qv4isel_masm.cpp | 37 +++++++++++++++---------- src/qml/jit/qv4isel_masm_p.h | 4 +-- src/qml/jit/qv4unop.cpp | 8 ++++-- src/qml/jsruntime/qv4runtimeapi_p.h | 33 +++++++++++++++++++++++ 7 files changed, 84 insertions(+), 67 deletions(-) diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index e29b165c2d..2ef0db78c0 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -108,36 +108,6 @@ struct RuntimeCall { bool isValid() const { return addr.offset >= 0; } }; -template -struct ExceptionCheck { - enum { NeedsCheck = 1 }; -}; -// push_catch and pop context methods shouldn't check for exceptions -template <> -struct ExceptionCheck { - enum { NeedsCheck = 0 }; -}; -template -struct ExceptionCheck { - enum { NeedsCheck = 0 }; -}; -template <> -struct ExceptionCheck { - enum { NeedsCheck = 0 }; -}; -template -struct ExceptionCheck { - enum { NeedsCheck = 0 }; -}; -template -struct ExceptionCheck { - enum { NeedsCheck = 0 }; -}; -template -struct ExceptionCheck { - enum { NeedsCheck = 0 }; -}; - class Assembler : public JSC::MacroAssembler, public TargetPlatform { Q_DISABLE_COPY(Assembler) @@ -809,7 +779,7 @@ public: }; template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size + SizeOnStack<1, Arg2>::Size @@ -852,7 +822,7 @@ public: if (stackSpaceNeeded) addPtr(TrustedImm32(stackSpaceNeeded), StackPointerRegister); - if (ExceptionCheck::NeedsCheck) { + if (needsExceptionCheck) { checkException(); } @@ -861,33 +831,33 @@ public: } template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); + generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); } template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType(), VoidType()); + generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, VoidType(), VoidType()); } template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType(), VoidType()); + generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType(), VoidType()); } template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) { - generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType(), VoidType()); + generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType(), VoidType()); } template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1) + void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1) { - generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType(), VoidType()); + generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType(), VoidType()); } Pointer toAddress(RegisterID tmpReg, IR::Expr *e, int offset) diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index 45cc9259c3..9c535bb0bb 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -45,17 +45,17 @@ using namespace QV4; using namespace JIT; #define OP(op) \ - { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, 0, 0 } + { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define OPCONTEXT(op) \ - { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), 0, 0 } + { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define INLINE_OP(op, memOp, immOp) \ - { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, memOp, immOp } + { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define INLINE_OPCONTEXT(op, memOp, immOp) \ - { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), memOp, immOp } + { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define NULL_OP \ - { 0, 0, 0, 0, 0 } + { 0, 0, 0, 0, 0, false } const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { NULL_OP, // OpInvalid @@ -128,11 +128,11 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) RuntimeCall fallBack(info.fallbackImplementation); RuntimeCall context(info.contextImplementation); if (fallBack.isValid()) { - as->generateFunctionCallImp(target, info.name, fallBack, + as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, fallBack, Assembler::PointerToValue(lhs), Assembler::PointerToValue(rhs)); } else if (context.isValid()) { - as->generateFunctionCallImp(target, info.name, context, + as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, context, Assembler::EngineRegister, Assembler::PointerToValue(lhs), Assembler::PointerToValue(rhs)); diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h index c246ee43b0..37601f54ba 100644 --- a/src/qml/jit/qv4binop_p.h +++ b/src/qml/jit/qv4binop_p.h @@ -81,6 +81,7 @@ struct Binop { int contextImplementation; // offsetOf(Runtime,...) MemRegOp inlineMemRegOp; ImmRegOp inlineImmRegOp; + bool needsExceptionCheck; }; static const OpInfo operations[IR::LastAluOp + 1]; diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index da28df817d..c1c42f876c 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -970,9 +970,15 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) } #define setOp(op, opName, operation) \ - do { op = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); } while (0) + do { \ + op = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ + } while (0) #define setOpContext(op, opName, operation) \ - do { opContext = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); } while (0) + do { \ + opContext = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ + } while (0) void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) { @@ -1446,18 +1452,19 @@ void InstructionSelection::visitCJump(IR::CJump *s) RuntimeCall op; RuntimeCall opContext; const char *opName = 0; + bool needsExceptionCheck; switch (b->op) { default: Q_UNREACHABLE(); Q_ASSERT(!"todo"); break; - case IR::OpGt: setOp(op, opName, Runtime::compareGreaterThan); break; - case IR::OpLt: setOp(op, opName, Runtime::compareLessThan); break; - case IR::OpGe: setOp(op, opName, Runtime::compareGreaterEqual); break; - case IR::OpLe: setOp(op, opName, Runtime::compareLessEqual); break; - case IR::OpEqual: setOp(op, opName, Runtime::compareEqual); break; - case IR::OpNotEqual: setOp(op, opName, Runtime::compareNotEqual); break; - case IR::OpStrictEqual: setOp(op, opName, Runtime::compareStrictEqual); break; - case IR::OpStrictNotEqual: setOp(op, opName, Runtime::compareStrictNotEqual); break; - case IR::OpInstanceof: setOpContext(op, opName, Runtime::compareInstanceof); break; - case IR::OpIn: setOpContext(op, opName, Runtime::compareIn); break; + case IR::OpGt: setOp(op, opName, compareGreaterThan); break; + case IR::OpLt: setOp(op, opName, compareLessThan); break; + case IR::OpGe: setOp(op, opName, compareGreaterEqual); break; + case IR::OpLe: setOp(op, opName, compareLessEqual); break; + case IR::OpEqual: setOp(op, opName, compareEqual); break; + case IR::OpNotEqual: setOp(op, opName, compareNotEqual); break; + case IR::OpStrictEqual: setOp(op, opName, compareStrictEqual); break; + case IR::OpStrictNotEqual: setOp(op, opName, compareStrictNotEqual); break; + case IR::OpInstanceof: setOpContext(op, opName, compareInstanceof); break; + case IR::OpIn: setOpContext(op, opName, compareIn); break; } // switch // TODO: in SSA optimization, do constant expression evaluation. @@ -1466,12 +1473,14 @@ void InstructionSelection::visitCJump(IR::CJump *s) // Of course, after folding the CJUMP to a JUMP, dead-code (dead-basic-block) // elimination (which isn't there either) would remove the whole else block. if (opContext.isValid()) - _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, opContext, + _as->generateFunctionCallImp(needsExceptionCheck, + Assembler::ReturnValueRegister, opName, opContext, Assembler::EngineRegister, Assembler::PointerToValue(b->left), Assembler::PointerToValue(b->right)); else - _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, + _as->generateFunctionCallImp(needsExceptionCheck, + Assembler::ReturnValueRegister, opName, op, Assembler::PointerToValue(b->left), Assembler::PointerToValue(b->right)); diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 93453f71be..5bca879a77 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -244,7 +244,7 @@ private: #define isel_stringIfy(s) isel_stringIfyx(s) #define generateRuntimeCall(t, function, ...) \ - _as->generateFunctionCallImp(t, "Runtime::" isel_stringIfy(function), RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) + _as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) int prepareVariableArguments(IR::ExprList* args); int prepareCallData(IR::ExprList* args, IR::Expr *thisObject); @@ -260,7 +260,7 @@ private: // address. Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup)); - _as->generateFunctionCallImp(retval, "lookup getter/setter", + _as->generateFunctionCallImp(true, retval, "lookup getter/setter", LookupCall(lookupAddr, getterSetterOffset), lookupAddr, arg1, arg2, arg3); } diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp index 6a32069ac4..799103849b 100644 --- a/src/qml/jit/qv4unop.cpp +++ b/src/qml/jit/qv4unop.cpp @@ -47,10 +47,14 @@ using namespace JIT; #define stringIfyx(s) #s #define stringIfy(s) stringIfyx(s) #define setOp(operation) \ - do { call = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); } while (0) + do { \ + call = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); \ + needsExceptionCheck = Runtime::Method_##operation##_NeedsExceptionCheck; \ + } while (0) void Unop::generate(IR::Expr *source, IR::Expr *target) { + bool needsExceptionCheck; RuntimeCall call; const char *name = 0; switch (op) { @@ -71,7 +75,7 @@ void Unop::generate(IR::Expr *source, IR::Expr *target) } // switch Q_ASSERT(call.isValid()); - _as->generateFunctionCallImp(target, name, call, Assembler::PointerToValue(source)); + _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, Assembler::PointerToValue(source)); } void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index cbc7a2ddc1..582cdcf4e9 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -58,8 +58,41 @@ namespace QV4 { struct NoThrowEngine; +namespace { +template +struct ExceptionCheck { + enum { NeedsCheck = 1 }; +}; +// push_catch and pop context methods shouldn't check for exceptions +template <> +struct ExceptionCheck { + enum { NeedsCheck = 0 }; +}; +template +struct ExceptionCheck { + enum { NeedsCheck = 0 }; +}; +template <> +struct ExceptionCheck { + enum { NeedsCheck = 0 }; +}; +template +struct ExceptionCheck { + enum { NeedsCheck = 0 }; +}; +template +struct ExceptionCheck { + enum { NeedsCheck = 0 }; +}; +template +struct ExceptionCheck { + enum { NeedsCheck = 0 }; +}; +} // anonymous namespace + #define RUNTIME_METHOD(returnvalue, name, args) \ typedef returnvalue (*Method_##name)args; \ + enum { Method_##name##_NeedsExceptionCheck = ExceptionCheck::NeedsCheck }; \ static returnvalue method_##name args; \ const Method_##name name -- cgit v1.2.3 From 7ec1d316f4eefc12b7fe040e467f799a67ce0a11 Mon Sep 17 00:00:00 2001 From: Arnaud Vrac Date: Thu, 28 Jul 2016 20:27:56 +0200 Subject: Fix import with qualifier of remote directory with qmldir Do not skip qmldir loading when using an import qualifier and the resolved uri is remote. This makes the import behavior the same in all cases. Task-number: QTBUG-55002 Change-Id: I99d68be02ddd062e387d36946e730df076e80a8d Reviewed-by: Simon Hausmann --- src/qml/qml/qqmltypeloader.cpp | 11 ++++------- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 4 +++- tests/auto/quick/qquickloader/data/qmldir | 1 - 3 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 tests/auto/quick/qquickloader/data/qmldir diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 4fa69d22bb..f7846f333b 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1452,13 +1452,10 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL bool incomplete = false; - QUrl qmldirUrl; - if (importQualifier.isEmpty()) { - qmldirUrl = finalUrl().resolved(QUrl(importUri + QLatin1String("/qmldir"))); - if (!QQmlImports::isLocal(qmldirUrl)) { - // This is a remote file; the import is currently incomplete - incomplete = true; - } + QUrl qmldirUrl = finalUrl().resolved(QUrl(importUri + QLatin1String("/qmldir"))); + if (!QQmlImports::isLocal(qmldirUrl)) { + // This is a remote file; the import is currently incomplete + incomplete = true; } if (!m_importCache.addFileImport(importDatabase, importUri, importQualifier, import->majorVersion, diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index f32051a26a..8a60b04494 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2601,7 +2601,7 @@ void tst_qqmllanguage::basicRemote_data() QTest::newRow("no need for qmldir") << QUrl(serverdir+"Test.qml") << "" << ""; QTest::newRow("absent qmldir") << QUrl(serverdir+"/noqmldir/Test.qml") << "" << ""; - QTest::newRow("need qmldir") << QUrl(serverdir+"TestLocal.qml") << "" << ""; + QTest::newRow("need qmldir") << QUrl(serverdir+"TestNamed.qml") << "" << ""; } void tst_qqmllanguage::basicRemote() @@ -2641,6 +2641,8 @@ void tst_qqmllanguage::importsRemote_data() << ""; QTest::newRow("remote import with local") << "import \""+serverdir+"\"\nTestLocal {}" << "QQuickImage" << ""; + QTest::newRow("remote import with qualifier") << "import \""+serverdir+"\" as NS\nNS.NamedLocal {}" << "QQuickImage" + << ""; QTest::newRow("wrong remote import with undeclared local") << "import \""+serverdir+"\"\nWrongTestLocal {}" << "" << "WrongTestLocal is not a type"; QTest::newRow("wrong remote import of internal local") << "import \""+serverdir+"\"\nLocalInternal {}" << "" diff --git a/tests/auto/quick/qquickloader/data/qmldir b/tests/auto/quick/qquickloader/data/qmldir deleted file mode 100644 index bf42b507c0..0000000000 --- a/tests/auto/quick/qquickloader/data/qmldir +++ /dev/null @@ -1 +0,0 @@ -# For tst_QDeclarativeLoader::networkRequestUrl; no types needed though. -- cgit v1.2.3 From 02b7e700b6393e17680cffb561f28dcdc90ffea6 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 9 Aug 2016 14:51:50 +0200 Subject: V4: Correctly enable the arm64/aarch64 disassembler Change-Id: Icdc82f831ee630de301e19313893d0f70654948c Reviewed-by: Simon Hausmann --- src/3rdparty/masm/masm-defs.pri | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri index b5480babc6..fa0d3d3c55 100644 --- a/src/3rdparty/masm/masm-defs.pri +++ b/src/3rdparty/masm/masm-defs.pri @@ -24,7 +24,8 @@ INCLUDEPATH += $$PWD disassembler { if(isEqual(QT_ARCH, "i386")|isEqual(QT_ARCH, "x86_64")): DEFINES += WTF_USE_UDIS86=1 - if(isEqual(QT_ARCH, "arm")): DEFINES += WTF_USE_ARMV7_DISASSEMBLER=1 WTF_USE_ARM64_DISASSEMBLER=1 + if(isEqual(QT_ARCH, "arm")): DEFINES += WTF_USE_ARMV7_DISASSEMBLER=1 + if(isEqual(QT_ARCH, "arm64")): DEFINES += WTF_USE_ARM64_DISASSEMBLER=1 if(isEqual(QT_ARCH, "mips")): DEFINES += WTF_USE_MIPS32_DISASSEMBLER=1 } else { DEFINES += WTF_USE_UDIS86=0 -- cgit v1.2.3 From 1351ac74078038cdf2f80640c8d4ba605a0ea16b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 10 Aug 2016 13:19:47 +0200 Subject: QSG: Fix inconsistent use of 'override' qsgsoftwarerenderablenode.cpp:221:11: warning: 'scissorRect' overrides a member function but is not marked 'override' [-Winconsistent-missing-override] (plus 4 more) Change-Id: Ie6b85d2eba9e3fbda8cfdc03abfece98df1bb28f Reviewed-by: Simon Hausmann --- .../adaptations/software/qsgsoftwarerenderablenode.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index d900688173..7b9e749532 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -218,11 +218,11 @@ void QSGSoftwareRenderableNode::update() struct RenderNodeState : public QSGRenderNode::RenderState { const QMatrix4x4 *projectionMatrix() const override { return &ident; } - QRect scissorRect() const { return QRect(); } - bool scissorEnabled() const { return false; } - int stencilValue() const { return 0; } - bool stencilEnabled() const { return false; } - const QRegion *clipRegion() const { return &cr; } + QRect scissorRect() const override { return QRect(); } + bool scissorEnabled() const override { return false; } + int stencilValue() const override { return 0; } + bool stencilEnabled() const override { return false; } + const QRegion *clipRegion() const override { return &cr; } QMatrix4x4 ident; QRegion cr; }; -- cgit v1.2.3 From 86a55cdb8cb850066e1dcc288d2dddf600652994 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 4 Aug 2016 12:38:43 +0200 Subject: QML: Make all fields in QQmlPropertyRawData private And add accessors. This makes it easier later on to change the storage of the fields. Change-Id: I21163668ac83a7d52f398981baf3c27ef161c177 Reviewed-by: Simon Hausmann --- .../qmldbg_debugger/qqmlenginedebugservice.cpp | 6 +- src/qml/compiler/qqmlirbuilder.cpp | 8 +- src/qml/compiler/qqmlpropertycachecreator_p.h | 10 +- src/qml/compiler/qqmlpropertyvalidator.cpp | 46 ++--- src/qml/compiler/qqmltypecompiler.cpp | 18 +- src/qml/compiler/qv4isel_p.cpp | 18 +- src/qml/compiler/qv4jsir.cpp | 4 +- src/qml/jsruntime/qv4qobjectwrapper.cpp | 150 +++++++------- src/qml/qml/qqmlbinding.cpp | 30 +-- src/qml/qml/qqmlcontext.cpp | 2 +- src/qml/qml/qqmllist.cpp | 6 +- src/qml/qml/qqmlobjectcreator.cpp | 88 ++++----- src/qml/qml/qqmlproperty.cpp | 86 ++++----- src/qml/qml/qqmlproperty_p.h | 2 +- src/qml/qml/qqmlpropertycache.cpp | 215 ++++++++++----------- src/qml/qml/qqmlpropertycache_p.h | 198 ++++++++++++------- src/qml/qml/qqmlvaluetypewrapper.cpp | 18 +- src/qml/qml/qqmlvmemetaobject.cpp | 8 +- src/quick/items/qquickopenglshadereffect.cpp | 6 +- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 2 +- .../qqmlpropertycache/tst_qqmlpropertycache.cpp | 72 +++---- 21 files changed, 520 insertions(+), 473 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index 12ed987ca0..f72b8a51f9 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -753,7 +753,7 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth if (!prop || !prop->isVMEFunction()) return false; - QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex); + QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex()); QList paramNames = metaMethod.parameterNames(); QString paramStr; @@ -772,12 +772,12 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth QV4::Scope scope(v4); int lineNumber = 0; - QV4::ScopedFunctionObject oldMethod(scope, vmeMetaObject->vmeMethod(prop->coreIndex)); + QV4::ScopedFunctionObject oldMethod(scope, vmeMetaObject->vmeMethod(prop->coreIndex())); if (oldMethod && oldMethod->d()->function) { lineNumber = oldMethod->d()->function->compiledFunction->location.line; } QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber)); - vmeMetaObject->setVmeMethod(prop->coreIndex, v); + vmeMetaObject->setVmeMethod(prop->coreIndex(), v); return true; } diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 1bc2b30087..ef8ffa8620 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1845,20 +1845,20 @@ static QV4::IR::DiscoveredType resolveMetaObjectProperty( if (property->isEnum()) return QV4::IR::VarType; - switch (property->propType) { + switch (property->propType()) { case QMetaType::Bool: result = QV4::IR::BoolType; break; case QMetaType::Int: result = QV4::IR::SInt32Type; break; case QMetaType::Double: result = QV4::IR::DoubleType; break; case QMetaType::QString: result = QV4::IR::StringType; break; default: if (property->isQObject()) { - if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType)) { + if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType())) { auto newResolver = resolver->owner->New(); newResolver->owner = resolver->owner; initMetaObjectResolver(newResolver, cache); return QV4::IR::DiscoveredType(newResolver); } - } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType)) { + } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType())) { if (QQmlPropertyCache *cache = qmlEngine->cache(valueTypeMetaObject)) { auto newResolver = resolver->owner->New(); newResolver->owner = resolver->owner; @@ -2053,7 +2053,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis d = property(propName, notInRevision); if (d) - return cache->signal(d->notifyIndex); + return cache->signal(d->notifyIndex()); } return 0; diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index a1b0a14890..10bcd1dbc1 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -124,7 +124,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObje // group properties and value type group properties. For the former the base type is derived from // the property that references us, for the latter we only need a meta-object on the referencing object // because interceptors can't go to the shared value type instances. - if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType)) { + if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType())) { if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex); auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex); @@ -180,8 +180,8 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCac { if (context.instantiatingProperty) { if (context.instantiatingProperty->isQObject()) { - return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType); - } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType)) { + return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType()); + } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType())) { return enginePrivate->cache(vtmo); } } else if (obj->inheritedTypeNameIndex != 0) { @@ -664,7 +664,7 @@ inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias QQmlPropertyData *targetProperty = targetCache->property(coreIndex); Q_ASSERT(targetProperty); - *type = targetProperty->propType; + *type = targetProperty->propType(); writable = targetProperty->isWritable(); resettable = targetProperty->isResettable(); @@ -680,7 +680,7 @@ inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias *type = QVariant::Int; } else { // Copy type flags - propertyFlags->copyPropertyTypeFlags(targetProperty->getFlags()); + propertyFlags->copyPropertyTypeFlags(targetProperty->flags()); if (targetProperty->isVarProperty()) propertyFlags->type = QQmlPropertyData::Flags::QVariantType; diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index c644330e97..45379d5155 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -206,7 +206,7 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, } if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { - const QVector subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)); + const QVector subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())); if (!subObjectValidatorErrors.isEmpty()) return subObjectValidatorErrors; } @@ -240,7 +240,7 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { QString error; - if (pd->propType == qMetaTypeId()) + if (pd->propType() == qMetaTypeId()) error = tr( "Cannot assign multiple values to a script property"); else error = tr( "Cannot assign multiple values to a singular property"); @@ -255,7 +255,7 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, if (loc < (*assignedGroupProperty)->valueLocation) loc = (*assignedGroupProperty)->valueLocation; - if (pd && QQmlValueTypeFactory::isValueType(pd->propType)) + if (pd && QQmlValueTypeFactory::isValueType(pd->propType())) return recordError(loc, tr("Property has already been assigned a value")); return recordError(loc, tr("Cannot assign a value directly to a grouped property")); } @@ -269,8 +269,8 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, if (bindingError.isSet()) return recordError(bindingError); } else if (binding->isGroupProperty()) { - if (QQmlValueTypeFactory::isValueType(pd->propType)) { - if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)) { + if (QQmlValueTypeFactory::isValueType(pd->propType())) { + if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())) { if (!pd->isWritable()) { return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); } @@ -278,7 +278,7 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, return recordError(binding->location, tr("Invalid grouped property access")); } } else { - if (!enginePrivate->propertyCacheForType(pd->propType)) { + if (!enginePrivate->propertyCacheForType(pd->propType())) { return recordError(binding->location, tr("Invalid grouped property access")); } } @@ -334,7 +334,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache return noError; QString value = binding->valueAsString(qmlUnit); - QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex); + QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex()); bool ok; if (p.isFlagType()) { p.enumerator().keysToValue(value.toUtf8().constData(), &ok); @@ -347,7 +347,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache return noError; } - switch (property->propType) { + switch (property->propType()) { case QMetaType::QVariant: break; case QVariant::String: { @@ -541,12 +541,12 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); default: { // generate single literal value assignment to a list property if required - if (property->propType == qMetaTypeId >()) { + if (property->propType() == qMetaTypeId >()) { if (binding->type != QV4::CompiledData::Binding::Type_Number) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected")); } break; - } else if (property->propType == qMetaTypeId >()) { + } else if (property->propType() == qMetaTypeId >()) { bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); if (ok) { double n = binding->valueAsNumber(); @@ -556,31 +556,31 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache if (!ok) return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected")); break; - } else if (property->propType == qMetaTypeId >()) { + } else if (property->propType() == qMetaTypeId >()) { if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected")); } break; - } else if (property->propType == qMetaTypeId >()) { + } else if (property->propType() == qMetaTypeId >()) { if (binding->type != QV4::CompiledData::Binding::Type_String) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected")); } break; - } else if (property->propType == qMetaTypeId >()) { + } else if (property->propType() == qMetaTypeId >()) { if (!binding->evaluatesToString()) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected")); } break; - } else if (property->propType == qMetaTypeId()) { + } else if (property->propType() == qMetaTypeId()) { break; - } else if (property->propType == qMetaTypeId()) { + } else if (property->propType() == qMetaTypeId()) { break; } // otherwise, try a custom type assignment - QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); if (!converter) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType())))); } } break; @@ -652,15 +652,15 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * return noError; } - if (QQmlMetaType::isInterface(property->propType)) { + if (QQmlMetaType::isInterface(property->propType())) { // Can only check at instantiation time if the created sub-object successfully casts to the // target interface. return noError; - } else if (property->propType == QMetaType::QVariant) { + } else if (property->propType() == QMetaType::QVariant) { // We can convert everything to QVariant :) return noError; } else if (property->isQList()) { - const int listType = enginePrivate->listType(property->propType); + const int listType = enginePrivate->listType(property->propType()); if (!QQmlMetaType::isInterface(listType)) { QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); if (!canCoerce(listType, source)) { @@ -672,15 +672,15 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * return noError; } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { return noError; - } else if (QQmlValueTypeFactory::isValueType(property->propType)) { + } else if (QQmlValueTypeFactory::isValueType(property->propType())) { return QQmlCompileError(binding->location, tr("Unexpected object assignment")); - } else if (property->propType == qMetaTypeId()) { + } else if (property->propType() == qMetaTypeId()) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); } else { // We want to raw metaObject here as the raw metaobject is the // actual property type before we applied any extensions that might // effect the properties on the type, but don't effect assignability - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType); + QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType()); // Will be true if the assgned type inherits propertyMetaObject bool isAssignable = false; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index e1166286c2..4b33363d09 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -390,7 +390,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio bool notInRevision = false; QQmlPropertyData *signal = resolver.signal(propertyName, ¬InRevision); if (signal) { - int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex); + int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex()); sigIndex = propertyCache->originalClone(sigIndex); bool unnamedParameter = false; @@ -547,7 +547,7 @@ bool QQmlEnumTypeResolver::resolveEnumBindings() if (!pd) continue; - if (!pd->isEnum() && pd->propType != QMetaType::Int) + if (!pd->isEnum() && pd->propType() != QMetaType::Int) continue; if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding)) @@ -577,7 +577,7 @@ bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QS bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding) { - bool isIntProp = (prop->propType == QMetaType::Int) && !prop->isEnum(); + bool isIntProp = (prop->propType() == QMetaType::Int) && !prop->isEnum(); if (!prop->isEnum() && !isIntProp) return true; @@ -621,7 +621,7 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, auto *tr = resolvedTypes->value(obj->inheritedTypeNameIndex); if (type && tr && tr->type == type) { - QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex); + QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex()); // When these two match, we can short cut the search if (mprop.isFlagType()) { @@ -758,7 +758,7 @@ void QQmlScriptStringScanner::scan() continue; bool notInRevision = false; QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; - if (!pd || pd->propType != scriptStringMetaType) + if (!pd || pd->propType() != scriptStringMetaType) continue; QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex); @@ -815,7 +815,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI if (!pd || !pd->isQObject()) continue; - QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType); + QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType()); const QMetaObject *mo = pc->firstCppMetaObject(); while (mo) { if (mo == &QQmlComponent::staticMetaObject) @@ -1116,15 +1116,15 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv } } - if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) { + if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) { *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); break; } - propIdx = QQmlPropertyIndex(targetProperty->coreIndex); + propIdx = QQmlPropertyIndex(targetProperty->coreIndex()); if (!subProperty.isEmpty()) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType); + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType()); if (!valueTypeMetaObject) { *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); break; diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index d97eec5e1d..72e6c276a9 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -149,18 +149,18 @@ void IRDecoder::visitMove(IR::Move *s) if (_function && attachedPropertiesId == 0 && !m->property->isConstant()) { if (m->kind == IR::Member::MemberOfQmlContextObject) { - _function->contextObjectPropertyDependencies.insert(m->property->coreIndex, m->property->notifyIndex); + _function->contextObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex()); captureRequired = false; } else if (m->kind == IR::Member::MemberOfQmlScopeObject) { - _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex, m->property->notifyIndex); + _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex()); captureRequired = false; } } if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { - getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex, s->target); + getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex(), s->target); return; } - getQObjectProperty(m->base, m->property->coreIndex, captureRequired, isSingletonProperty, attachedPropertiesId, s->target); + getQObjectProperty(m->base, m->property->coreIndex(), captureRequired, isSingletonProperty, attachedPropertiesId, s->target); #endif // V4_BOOTSTRAP return; } else if (m->kind == IR::Member::MemberOfIdObjectsArray) { @@ -187,7 +187,7 @@ void IRDecoder::visitMove(IR::Move *s) #ifndef V4_BOOTSTRAP Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { - callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex, c->args, s->target); + callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, s->target); return; } #endif @@ -216,10 +216,10 @@ void IRDecoder::visitMove(IR::Move *s) Q_UNIMPLEMENTED(); #else if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { - setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex); + setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex()); return; } - setQObjectProperty(s->source, m->base, m->property->coreIndex); + setQObjectProperty(s->source, m->base, m->property->coreIndex()); #endif return; } else { @@ -263,7 +263,7 @@ void IRDecoder::visitExp(IR::Exp *s) #ifndef V4_BOOTSTRAP Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { - callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex, c->args, 0); + callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, 0); return; } #endif @@ -295,7 +295,7 @@ void IRDecoder::callBuiltin(IR::Call *call, Expr *result) if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { callBuiltinTypeofQmlContextProperty(member->base, IR::Member::MemberKind(member->kind), - member->property->coreIndex, result); + member->property->coreIndex(), result); return; } #endif diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index b6c5226894..5687834b00 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -911,8 +911,8 @@ void IRPrinter::visitMember(Member *e) *out << '.' << *e->name; #ifndef V4_BOOTSTRAP if (e->property) - *out << " (meta-property " << e->property->coreIndex - << " <" << QMetaType::typeName(e->property->propType) + *out << " (meta-property " << e->property->coreIndex() + << " <" << QMetaType::typeName(e->property->propType()) << ">)"; else if (e->kind == Member::MemberOfIdObjectsArray) *out << "(id object " << e->idIndex << ")"; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 8fef45891a..624c37a694 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -124,7 +124,7 @@ struct ReadAccessor { Q_UNUSED(n); void *args[] = { output, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); + QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex(), args); } static inline void Direct(QObject *object, const QQmlPropertyData &property, @@ -134,16 +134,16 @@ struct ReadAccessor { Q_UNUSED(n); void *args[] = { output, 0 }; - object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); + object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex(), args); } static inline void Accessor(QObject *object, const QQmlPropertyData &property, void *output, QQmlNotifier **n) { - Q_ASSERT(property.accessors); + Q_ASSERT(property.accessors()); - property.accessors->read(object, output); - if (n) property.accessors->notifier(object, n); + property.accessors()->read(object, output); + if (n) property.accessors()->notifier(object, n); } }; @@ -162,32 +162,32 @@ static QV4::ReturnedValue LoadProperty(QV4::ExecutionEngine *v4, QObject *object ReadFunction(object, property, &rv, notifier); return QV4::QObjectWrapper::wrap(v4, rv); } else if (property.isQList()) { - return QmlListWrapper::create(v4, object, property.coreIndex, property.propType); - } else if (property.propType == QMetaType::QReal) { + return QmlListWrapper::create(v4, object, property.coreIndex(), property.propType()); + } else if (property.propType() == QMetaType::QReal) { qreal v = 0; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); - } else if (property.propType == QMetaType::Int || property.isEnum()) { + } else if (property.propType() == QMetaType::Int || property.isEnum()) { int v = 0; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); - } else if (property.propType == QMetaType::Bool) { + } else if (property.propType() == QMetaType::Bool) { bool v = false; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); - } else if (property.propType == QMetaType::QString) { + } else if (property.propType() == QMetaType::QString) { QString v; ReadFunction(object, property, &v, notifier); return v4->newString(v)->asReturnedValue(); - } else if (property.propType == QMetaType::UInt) { + } else if (property.propType() == QMetaType::UInt) { uint v = 0; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); - } else if (property.propType == QMetaType::Float) { + } else if (property.propType() == QMetaType::Float) { float v = 0; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); - } else if (property.propType == QMetaType::Double) { + } else if (property.propType() == QMetaType::Double) { double v = 0; ReadFunction(object, property, &v, notifier); return QV4::Encode(v); @@ -195,7 +195,7 @@ static QV4::ReturnedValue LoadProperty(QV4::ExecutionEngine *v4, QObject *object QQmlV4Handle handle; ReadFunction(object, property, &handle, notifier); return handle; - } else if (property.propType == qMetaTypeId()) { + } else if (property.propType() == qMetaTypeId()) { QJSValue v; ReadFunction(object, property, &v, notifier); return QJSValuePrivate::convertedToValue(v4, v); @@ -205,32 +205,32 @@ static QV4::ReturnedValue LoadProperty(QV4::ExecutionEngine *v4, QObject *object if (QQmlValueTypeFactory::isValueType(v.userType())) { if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(v.userType())) - return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex, valueTypeMetaObject, v.userType()); // VariantReference value-type. + return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, v.userType()); // VariantReference value-type. } return scope.engine->fromVariant(v); - } else if (QQmlValueTypeFactory::isValueType(property.propType)) { + } else if (QQmlValueTypeFactory::isValueType(property.propType())) { Q_ASSERT(notifier == 0); - if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType)) - return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex, valueTypeMetaObject, property.propType); + if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType())) + return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType()); } else { Q_ASSERT(notifier == 0); // see if it's a sequence type bool succeeded = false; - QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType, object, property.coreIndex, &succeeded)); + QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), &succeeded)); if (succeeded) return retn->asReturnedValue(); } - if (property.propType == QMetaType::UnknownType) { - QMetaProperty p = object->metaObject()->property(property.coreIndex); + if (property.propType() == QMetaType::UnknownType) { + QMetaProperty p = object->metaObject()->property(property.coreIndex()); qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property " "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name()); return QV4::Encode::undefined(); } else { - QVariant v(property.propType, (void *)0); + QVariant v(property.propType(), (void *)0); ReadFunction(object, property, v.data(), notifier); return scope.engine->fromVariant(v); } @@ -328,25 +328,25 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired) { - QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex)); + QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex())); if (property->isFunction() && !property->isVarProperty()) { if (property->isVMEFunction()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); - return vmemo->vmeMethod(property->coreIndex); + return vmemo->vmeMethod(property->coreIndex()); } else if (property->isV4Function()) { Scope scope(engine); ScopedContext global(scope, engine->qmlContext()); if (!global) global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, property->coreIndex); + return QV4::QObjectMethod::create(global, object, property->coreIndex()); } else if (property->isSignalHandler()) { QmlSignalHandler::initProto(engine); - return engine->memoryManager->allocObject(object, property->coreIndex)->asReturnedValue(); + return engine->memoryManager->allocObject(object, property->coreIndex())->asReturnedValue(); } else { ExecutionContext *global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, property->coreIndex); + return QV4::QObjectMethod::create(global, object, property->coreIndex()); } } @@ -356,19 +356,19 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje QQmlNotifier *n = 0; QQmlNotifier **nptr = 0; - if (ep && ep->propertyCapture && property->accessors->notifier) + if (ep && ep->propertyCapture && property->accessors()->notifier) nptr = &n; Scope scope(engine); QV4::ScopedValue rv(scope, LoadProperty(engine, object, *property, nptr)); if (captureRequired && !property->isConstant()) { - if (property->accessors->notifier) { + if (property->accessors()->notifier) { if (n && ep->propertyCapture) ep->propertyCapture->captureProperty(n); } else { if (ep->propertyCapture) - ep->propertyCapture->captureProperty(object, property->coreIndex, property->notifyIndex); + ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex()); } } @@ -376,12 +376,12 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje } if (captureRequired && ep && ep->propertyCapture && !property->isConstant()) - ep->propertyCapture->captureProperty(object, property->coreIndex, property->notifyIndex); + ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex()); if (property->isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); - return vmemo->vmeProperty(property->coreIndex); + return vmemo->vmeProperty(property->coreIndex()); } else if (property->isDirect()) { return LoadProperty(engine, object, *property, 0); } else { @@ -448,13 +448,13 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP QV4::ScopedFunctionObject f(scope, value); if (f) { if (!f->isBinding()) { - if (!property->isVarProperty() && property->propType != qMetaTypeId()) { + if (!property->isVarProperty() && property->propType() != qMetaTypeId()) { // assigning a JS function to a non var or QJSValue property or is not allowed. QString error = QLatin1String("Cannot assign JavaScript function to "); - if (!QMetaType::typeName(property->propType)) + if (!QMetaType::typeName(property->propType())) error += QLatin1String("[unknown property type]"); else - error += QLatin1String(QMetaType::typeName(property->propType)); + error += QLatin1String(QMetaType::typeName(property->propType())); scope.engine->throwError(error); return; } @@ -473,13 +473,13 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP if (newBinding) QQmlPropertyPrivate::setBinding(newBinding); else - QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex)); + QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex())); if (!newBinding && property->isVarProperty()) { // allow assignment of "special" values (null, undefined, function) to var properties QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); - vmemo->setVMEProperty(property->coreIndex, value); + vmemo->setVMEProperty(property->coreIndex(), value); return; } @@ -488,44 +488,44 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP int status = -1; \ int flags = 0; \ void *argv[] = { &o, 0, &status, &flags }; \ - QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv); + QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv); if (value.isNull() && property->isQObject()) { PROPERTY_STORE(QObject*, 0); } else if (value.isUndefined() && property->isResettable()) { void *a[] = { 0 }; - QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a); - } else if (value.isUndefined() && property->propType == qMetaTypeId()) { + QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex(), a); + } else if (value.isUndefined() && property->propType() == qMetaTypeId()) { PROPERTY_STORE(QVariant, QVariant()); - } else if (value.isUndefined() && property->propType == QMetaType::QJsonValue) { + } else if (value.isUndefined() && property->propType() == QMetaType::QJsonValue) { PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); - } else if (!newBinding && property->propType == qMetaTypeId()) { + } else if (!newBinding && property->propType() == qMetaTypeId()) { PROPERTY_STORE(QJSValue, QJSValue(scope.engine, value.asReturnedValue())); - } else if (value.isUndefined() && property->propType != qMetaTypeId()) { + } else if (value.isUndefined() && property->propType() != qMetaTypeId()) { QString error = QLatin1String("Cannot assign [undefined] to "); - if (!QMetaType::typeName(property->propType)) + if (!QMetaType::typeName(property->propType())) error += QLatin1String("[unknown property type]"); else - error += QLatin1String(QMetaType::typeName(property->propType)); + error += QLatin1String(QMetaType::typeName(property->propType())); scope.engine->throwError(error); return; } else if (value.as()) { // this is handled by the binding creation above - } else if (property->propType == QMetaType::Int && value.isNumber()) { + } else if (property->propType() == QMetaType::Int && value.isNumber()) { PROPERTY_STORE(int, value.asDouble()); - } else if (property->propType == QMetaType::QReal && value.isNumber()) { + } else if (property->propType() == QMetaType::QReal && value.isNumber()) { PROPERTY_STORE(qreal, qreal(value.asDouble())); - } else if (property->propType == QMetaType::Float && value.isNumber()) { + } else if (property->propType() == QMetaType::Float && value.isNumber()) { PROPERTY_STORE(float, float(value.asDouble())); - } else if (property->propType == QMetaType::Double && value.isNumber()) { + } else if (property->propType() == QMetaType::Double && value.isNumber()) { PROPERTY_STORE(double, double(value.asDouble())); - } else if (property->propType == QMetaType::QString && value.isString()) { + } else if (property->propType() == QMetaType::QString && value.isString()) { PROPERTY_STORE(QString, value.toQStringNoThrow()); } else if (property->isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); - vmemo->setVMEProperty(property->coreIndex, value); - } else if (property->propType == qMetaTypeId() && (value.isUndefined() || value.isPrimitive())) { + vmemo->setVMEProperty(property->coreIndex(), value); + } else if (property->propType() == qMetaTypeId() && (value.isUndefined() || value.isPrimitive())) { QQmlScriptString ss(value.toQStringNoThrow(), 0 /* context */, object); if (value.isNumber()) { ss.d->numberValue = value.toNumber(); @@ -540,7 +540,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP if (property->isQList()) v = scope.engine->toVariant(value, qMetaTypeId >()); else - v = scope.engine->toVariant(value, property->propType); + v = scope.engine->toVariant(value, property->propType()); QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) { @@ -548,7 +548,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP if (v.userType() == QVariant::Invalid) valueType = "null"; else valueType = QMetaType::typeName(v.userType()); - const char *targetTypeName = QMetaType::typeName(property->propType); + const char *targetTypeName = QMetaType::typeName(property->propType()); if (!targetTypeName) targetTypeName = "an unregistered type"; @@ -1323,34 +1323,34 @@ static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object, if (!current->isOverload()) return 0; - Q_ASSERT(!current->overrideIndexIsProperty); + Q_ASSERT(!current->overrideIndexIsProperty()); if (propertyCache) { - return propertyCache->method(current->overrideIndex); + return propertyCache->method(current->overrideIndex()); } else { const QMetaObject *mo = object.metaObject(); int methodOffset = mo->methodCount() - QMetaObject_methods(mo); - while (methodOffset > current->overrideIndex) { + while (methodOffset > current->overrideIndex()) { mo = mo->superClass(); methodOffset -= QMetaObject_methods(mo); } // If we've been called before with the same override index, then // we can't go any further... - if (&dummy == current && dummy.coreIndex == current->overrideIndex) + if (&dummy == current && dummy.coreIndex() == current->overrideIndex()) return 0; - QMetaMethod method = mo->method(current->overrideIndex); + QMetaMethod method = mo->method(current->overrideIndex()); dummy.load(method); // Look for overloaded methods QByteArray methodName = method.name(); - for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) { + for (int ii = current->overrideIndex() - 1; ii >= methodOffset; --ii) { if (methodName == mo->method(ii).name()) { dummy.setOverload(true); - dummy.overrideIndexIsProperty = 0; - dummy.overrideIndex = ii; + dummy.setOverrideIndexIsProperty(0); + dummy.setOverrideIndex(ii); return &dummy; } } @@ -1380,9 +1380,9 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ if (data.isConstructor()) args = static_cast(object).constructorParameterTypes( - data.coreIndex, &storage, &unknownTypeError); + data.coreIndex(), &storage, &unknownTypeError); else - args = object.methodParameterTypes(data.coreIndex, &storage, &unknownTypeError); + args = object.methodParameterTypes(data.coreIndex(), &storage, &unknownTypeError); if (!args) { QString typeName = QString::fromLatin1(unknownTypeError); @@ -1395,11 +1395,11 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ return engine->throwError(error); } - return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs, callType); + return CallMethod(object, data.coreIndex(), returnType, args[0], args + 1, engine, callArgs, callType); } else { - return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs, callType); + return CallMethod(object, data.coreIndex(), returnType, 0, 0, engine, callArgs, callType); } } @@ -1438,7 +1438,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const int methodArgumentCount = 0; int *methodArgTypes = 0; if (attempt->hasArguments()) { - int *args = object.methodParameterTypes(attempt->coreIndex, &storage, 0); + int *args = object.methodParameterTypes(attempt->coreIndex(), &storage, 0); if (!args) // Must be an unknown argument continue; @@ -1475,7 +1475,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const const QQmlPropertyData *candidate = &data; while (candidate) { error += QLatin1String("\n ") + - QString::fromUtf8(object.metaObject()->method(candidate->coreIndex) + QString::fromUtf8(object.metaObject()->method(candidate->coreIndex()) .methodSignature()); candidate = RelatedMethod(object, candidate, dummy, propertyCache); } @@ -1859,7 +1859,7 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const const QMetaMethod moMethod = mo->method(d()->index); method.load(moMethod); - if (method.coreIndex == -1) { + if (method.coreIndex() == -1) { scope.result = QV4::Encode::undefined(); return; } @@ -1870,8 +1870,8 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const for (int ii = d()->index - 1; ii >= methodOffset; --ii) { if (methodName == mo->method(ii).name()) { method.setOverload(true); - method.overrideIndexIsProperty = 0; - method.overrideIndex = ii; + method.setOverrideIndexIsProperty(0); + method.setOverrideIndex(ii); break; } } @@ -1883,7 +1883,7 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const QQmlV4Function *funcptr = &func; void *args[] = { 0, &funcptr }; - object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex, args); + object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex(), args); return; } @@ -1923,7 +1923,7 @@ void Heap::QMetaObjectWrapper::ensureConstructorsCache() { QMetaMethod method = metaObject->constructor(i); QQmlPropertyData d; d.load(method); - d.coreIndex = i; + d.setCoreIndex(i); constructors << d; } } @@ -2010,7 +2010,7 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine int *methodArgTypes = 0; if (attempt.hasArguments()) { QQmlMetaObject::ArgTypeStorage storage; - int *args = object.constructorParameterTypes(attempt.coreIndex, &storage, 0); + int *args = object.constructorParameterTypes(attempt.coreIndex(), &storage, 0); if (!args) // Must be an unknown argument continue; @@ -2046,7 +2046,7 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine for (int i = 0; i < numberOfConstructors; i++) { const QQmlPropertyData & candidate = d()->constructors.at(i); error += QLatin1String("\n ") + - QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex) + QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex()) .methodSignature()); } diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 3b3e23c891..8ed7641610 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -275,7 +275,7 @@ protected: int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded. if (propertyType == QMetaType::UnknownType) - propertyType = pd->propType; + propertyType = pd->propType(); if (Q_LIKELY(!isUndefined && !vpd.isValid())) { switch (propertyType) { @@ -304,8 +304,8 @@ protected: break; default: if (const QV4::QQmlValueTypeWrapper *vtw = result.as()) { - if (vtw->d()->valueType->typeId == pd->propType) { - return vtw->write(m_target.data(), pd->coreIndex); + if (vtw->d()->valueType->typeId == pd->propType()) { + return vtw->write(m_target.data(), pd->coreIndex()); } } break; @@ -331,7 +331,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, QQmlEngine *engine = context()->engine; QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); - int type = valueTypeData.isValid() ? valueTypeData.propType : core.propType; + int type = valueTypeData.isValid() ? valueTypeData.propType() : core.propType(); QQmlJavaScriptExpression::DeleteWatcher watcher(this); @@ -343,7 +343,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, value = QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId >()); } else if (result.isNull() && core.isQObject()) { value = QVariant::fromValue((QObject *)0); - } else if (core.propType == qMetaTypeId >()) { + } else if (core.propType() == qMetaTypeId >()) { value = QQmlPropertyPrivate::resolvedUrlSequence(QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId >()), context()); } else if (!isVarProperty && type != qMetaTypeId()) { value = QV8Engine::getV4(v8engine)->toVariant(result, type); @@ -362,10 +362,10 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_target.data()); Q_ASSERT(vmemo); - vmemo->setVMEProperty(core.coreIndex, result); + vmemo->setVMEProperty(core.coreIndex(), result); } else if (isUndefined && core.isResettable()) { void *args[] = { 0 }; - QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex, args); + QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex(), args); } else if (isUndefined && type == qMetaTypeId()) { QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant(), context(), flags); } else if (type == qMetaTypeId()) { @@ -507,8 +507,8 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const return; } - int coreIndex = core.coreIndex; - int valueTypeIndex = valueType ? valueType->coreIndex : -1; + int coreIndex = core.coreIndex(); + int valueTypeIndex = valueType ? valueType->coreIndex() : -1; for (bool isAlias = core.isAlias(); isAlias; ) { QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); @@ -532,7 +532,7 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const m_target = object; isAlias = propertyData->isAlias(); - coreIndex = propertyData->coreIndex; + coreIndex = propertyData->coreIndex(); } m_targetIndex = QQmlPropertyIndex(coreIndex, valueTypeIndex); @@ -554,12 +554,12 @@ void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyD Q_ASSERT(*propertyData); if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType((*propertyData)->propType); + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType((*propertyData)->propType()); Q_ASSERT(valueTypeMetaObject); QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex()); valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp)); - valueTypeData->propType = vtProp.userType(); - valueTypeData->coreIndex = m_targetIndex.valueTypeIndex(); + valueTypeData->setPropType(vtProp.userType()); + valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex()); } } @@ -625,9 +625,9 @@ protected: QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property) { if (property && property->isQObject()) - return new QObjectPointerBinding(engine, property->propType); + return new QObjectPointerBinding(engine, property->propType()); - const int type = (property && property->isFullyResolved()) ? property->propType : QMetaType::UnknownType; + const int type = (property && property->isFullyResolved()) ? property->propType() : QMetaType::UnknownType; if (type == qMetaTypeId()) { return new QQmlBindingBinding; diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 6621f48e20..018841b9b1 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -383,7 +383,7 @@ QVariant QQmlContext::contextProperty(const QString &name) const QQmlPropertyData *property = QQmlPropertyCache::property(data->engine, obj, name, data, local); - if (property) value = obj->metaObject()->property(property->coreIndex).read(obj); + if (property) value = obj->metaObject()->property(property->coreIndex()).read(obj); } if (!value.isValid() && parentContext()) value = parentContext()->contextProperty(name); diff --git a/src/qml/qml/qqmllist.cpp b/src/qml/qml/qqmllist.cpp index d0b7fb4853..a719956483 100644 --- a/src/qml/qml/qqmllist.cpp +++ b/src/qml/qml/qqmllist.cpp @@ -143,16 +143,16 @@ QQmlListReference::QQmlListReference(QObject *object, const char *property, QQml QQmlEnginePrivate *p = engine?QQmlEnginePrivate::get(engine):0; - int listType = p?p->listType(data->propType):QQmlMetaType::listType(data->propType); + int listType = p?p->listType(data->propType()):QQmlMetaType::listType(data->propType()); if (listType == -1) return; d = new QQmlListReferencePrivate; d->object = object; d->elementType = p?p->rawMetaObjectForType(listType):QQmlMetaType::qmlType(listType)->baseMetaObject(); - d->propertyType = data->propType; + d->propertyType = data->propType(); void *args[] = { &d->property, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, data->coreIndex, args); + QMetaObject::metacall(object, QMetaObject::ReadProperty, data->coreIndex(), args); } /*! \internal */ diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 993331b0a0..f9794ec26a 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -281,7 +281,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | QQmlPropertyData::RemoveBindingOnAliasWrite; QV4::Scope scope(v4); - int propertyType = property->propType; + int propertyType = property->propType(); if (property->isEnum()) { if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) { @@ -302,7 +302,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const double n = binding->valueAsNumber(); if (double(int(n)) == n) { if (property->isVarProperty()) { - _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromInt32(int(n))); + _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromInt32(int(n))); } else { int i = int(n); QVariant value(i); @@ -310,7 +310,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } } else { if (property->isVarProperty()) { - _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromDouble(n)); + _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromDouble(n)); } else { QVariant value(n); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -318,7 +318,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { if (property->isVarProperty()) { - _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromBoolean(binding->valueAsBoolean())); + _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromBoolean(binding->valueAsBoolean())); } else { QVariant value(binding->valueAsBoolean()); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -327,7 +327,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QString stringValue = binding->valueAsString(qmlUnit); if (property->isVarProperty()) { QV4::ScopedString s(scope, v4->newString(stringValue)); - _vmeMetaObject->setVMEProperty(property->coreIndex, s); + _vmeMetaObject->setVMEProperty(property->coreIndex(), s); } else { QVariant value = QQmlStringConverters::variantFromString(stringValue); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -398,7 +398,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); struct { void *data[4]; } buffer; - if (QQml_valueTypeProvider()->storeValueType(property->propType, &colorValue, &buffer, sizeof(buffer))) { + if (QQml_valueTypeProvider()->storeValueType(property->propType(), &colorValue, &buffer, sizeof(buffer))) { property->writeProperty(_qobject, &buffer, propertyWriteFlags); } } @@ -534,26 +534,26 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; default: { // generate single literal value assignment to a list property if required - if (property->propType == qMetaTypeId >()) { + if (property->propType() == qMetaTypeId >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); QList value; value.append(binding->valueAsNumber()); property->writeProperty(_qobject, &value, propertyWriteFlags); break; - } else if (property->propType == qMetaTypeId >()) { + } else if (property->propType() == qMetaTypeId >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double n = binding->valueAsNumber(); QList value; value.append(int(n)); property->writeProperty(_qobject, &value, propertyWriteFlags); break; - } else if (property->propType == qMetaTypeId >()) { + } else if (property->propType() == qMetaTypeId >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean); QList value; value.append(binding->valueAsBoolean()); property->writeProperty(_qobject, &value, propertyWriteFlags); break; - } else if (property->propType == qMetaTypeId >()) { + } else if (property->propType() == qMetaTypeId >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); QString urlString = binding->valueAsString(qmlUnit); QUrl u = urlString.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(urlString)); @@ -561,13 +561,13 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const value.append(u); property->writeProperty(_qobject, &value, propertyWriteFlags); break; - } else if (property->propType == qMetaTypeId >()) { + } else if (property->propType() == qMetaTypeId >()) { Q_ASSERT(binding->evaluatesToString()); QList value; value.append(binding->valueAsString(qmlUnit)); property->writeProperty(_qobject, &value, propertyWriteFlags); break; - } else if (property->propType == qMetaTypeId()) { + } else if (property->propType() == qMetaTypeId()) { QJSValue value; if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { value = QJSValue(binding->valueAsBoolean()); @@ -586,12 +586,12 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const // otherwise, try a custom type assignment QString stringValue = binding->valueAsString(qmlUnit); - QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); Q_ASSERT(converter); QVariant value = (*converter)(stringValue); - QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex); - if (value.isNull() || ((int)metaProperty.type() != property->propType && metaProperty.userType() != property->propType)) { + QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex()); + if (value.isNull() || ((int)metaProperty.type() != property->propType() && metaProperty.userType() != property->propType())) { recordError(binding->location, tr("Cannot assign value %1 to property %2").arg(stringValue).arg(QString::fromUtf8(metaProperty.name()))); break; } @@ -623,7 +623,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) if (_compiledObject->idNameIndex) { const QQmlPropertyData *idProperty = propertyData.last(); Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QLatin1String("id")); - if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType == QMetaType::QString) { + if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType() == QMetaType::QString) { QV4::CompiledData::Binding idBinding; idBinding.propertyNameIndex = 0; // Not used idBinding.flags = 0; @@ -636,10 +636,10 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) // ### this is best done through type-compile-time binding skip lists. if (_valueTypeProperty) { - QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex)); + QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex())); if (binding && !binding->isValueTypeProxy()) { - QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex)); + QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex())); } else if (binding) { QQmlValueTypeProxyBinding *proxy = static_cast(binding); @@ -652,7 +652,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { QQmlPropertyData *property = binding->propertyNameIndex != 0 ? _propertyCache->property(stringAt(binding->propertyNameIndex), _qobject, context) : defaultProperty; if (property) - bindingSkipList |= (1 << property->coreIndex); + bindingSkipList |= (1 << property->coreIndex()); } proxy->removeBindings(bindingSkipList); @@ -678,10 +678,10 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) const QQmlPropertyData *property = propertyData.at(i); if (property && property->isQList()) { - if (property->coreIndex != currentListPropertyIndex) { + if (property->coreIndex() != currentListPropertyIndex) { void *argv[1] = { (void*)&_currentList }; - QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex, argv); - currentListPropertyIndex = property->coreIndex; + QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv); + currentListPropertyIndex = property->coreIndex(); } } else if (_currentList.object) { _currentList = QQmlListProperty(); @@ -717,7 +717,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } // ### resolve this at compile time - if (property && property->propType == qMetaTypeId()) { + if (property && property->propType() == qMetaTypeId()) { QQmlScriptString ss(binding->valueAsScriptString(qmlUnit), context->asQQmlContext(), _scopeObject); ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line; @@ -730,7 +730,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyData::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; void *argv[] = { &ss, 0, &propertyWriteStatus, &propertyWriteFlags }; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); return true; } @@ -753,20 +753,20 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con const QQmlPropertyData *valueTypeProperty = 0; QObject *bindingTarget = _bindingTarget; - if (QQmlValueTypeFactory::isValueType(property->propType)) { - valueType = QQmlValueTypeFactory::valueType(property->propType); + if (QQmlValueTypeFactory::isValueType(property->propType())) { + valueType = QQmlValueTypeFactory::valueType(property->propType()); if (!valueType) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; } - valueType->read(_qobject, property->coreIndex); + valueType->read(_qobject, property->coreIndex()); groupObject = valueType; valueTypeProperty = property; } else { void *argv[1] = { &groupObject }; - QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex, argv); + QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv); if (!groupObject) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; @@ -779,16 +779,16 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; if (valueType) - valueType->write(_qobject, property->coreIndex, QQmlPropertyData::BypassInterceptor); + valueType->write(_qobject, property->coreIndex(), QQmlPropertyData::BypassInterceptor); return true; } } - if (_ddata->hasBindingBit(property->coreIndex) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + if (_ddata->hasBindingBit(property->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) && !_valueTypeProperty) - QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex)); + QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex())); if (binding->type == QV4::CompiledData::Binding::Type_Script) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; @@ -798,7 +798,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction, /*createProto*/ false)); if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { - int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex); + int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, context, _scopeObject, function); @@ -829,7 +829,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con if (!_valueTypeProperty) { QQmlData *targetDeclarativeData = QQmlData::get(_bindingTarget); Q_ASSERT(targetDeclarativeData); - targetDeclarativeData->setPendingBindingBit(_bindingTarget, property->coreIndex); + targetDeclarativeData->setPendingBindingBit(_bindingTarget, property->coreIndex()); } } } @@ -861,7 +861,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyIndex propertyIndex; if (property->isAlias()) { - QQmlPropertyIndex originalIndex(property->coreIndex, _valueTypeProperty ? _valueTypeProperty->coreIndex : -1); + QQmlPropertyIndex originalIndex(property->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); QQmlPropertyIndex propIndex; QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); QQmlData *data = QQmlData::get(target); @@ -906,7 +906,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex); + QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex()); if (!QMetaObject::checkConnectArgs(signalMethod, method)) { recordError(binding->valueLocation, tr("Cannot connect mismatched signal/slot %1 %vs. %2") @@ -915,7 +915,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QQmlPropertyPrivate::connect(_qobject, property->coreIndex, createdSubObject, method.methodIndex()); + QQmlPropertyPrivate::connect(_qobject, property->coreIndex(), createdSubObject, method.methodIndex()); return true; } @@ -924,24 +924,24 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con int propertyWriteStatus = -1; void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags }; - if (const char *iid = QQmlMetaType::interfaceIId(property->propType)) { + if (const char *iid = QQmlMetaType::interfaceIId(property->propType())) { void *ptr = createdSubObject->qt_metacast(iid); if (ptr) { argv[0] = &ptr; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); } else { recordError(binding->location, tr("Cannot assign object to interface property")); return false; } - } else if (property->propType == QMetaType::QVariant) { + } else if (property->propType() == QMetaType::QVariant) { if (property->isVarProperty()) { QV4::Scope scope(v4); QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(QV8Engine::getV4(engine), createdSubObject)); - _vmeMetaObject->setVMEProperty(property->coreIndex, wrappedObject); + _vmeMetaObject->setVMEProperty(property->coreIndex(), wrappedObject); } else { QVariant value = QVariant::fromValue(createdSubObject); argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); } } else if (property->isQList()) { Q_ASSERT(_currentList.object); @@ -949,7 +949,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con void *itemToAdd = createdSubObject; const char *iid = 0; - int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType); + int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType()); if (listItemType != -1) iid = QQmlMetaType::interfaceIId(listItemType); if (iid) @@ -965,7 +965,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else { // pointer compatibility was tested in QQmlPropertyValidator at type compile time argv[0] = &createdSubObject; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); } return true; } @@ -995,7 +995,7 @@ void QQmlObjectCreator::setupFunctions() continue; function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction); - _vmeMetaObject->setVmeMethod(property->coreIndex, function); + _vmeMetaObject->setVmeMethod(property->coreIndex(), function); } } diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index e287b68385..7b1e2ec4f6 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -291,9 +291,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) if (property->isFunction()) return; // Not an object property - if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType)) { + if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType())) { // We're now at a value type property - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType); + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType()); if (!valueTypeMetaObject) return; // Not a value type int idx = valueTypeMetaObject->indexOfProperty(path.last().toUtf8().constData()); @@ -307,8 +307,8 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) object = currentObject; core = *property; valueTypeData.setFlags(QQmlPropertyData::flagsForProperty(vtProp)); - valueTypeData.propType = vtProp.userType(); - valueTypeData.coreIndex = idx; + valueTypeData.setPropType(vtProp.userType()); + valueTypeData.setCoreIndex(idx); return; } else { @@ -355,9 +355,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) while (d && d->isFunction()) d = ddata->propertyCache->overrideData(d); - if (d && d->notifyIndex != -1) { + if (d && d->notifyIndex() != -1) { object = currentObject; - core = *ddata->propertyCache->signal(d->notifyIndex); + core = *ddata->propertyCache->signal(d->notifyIndex()); return; } } @@ -394,7 +394,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) int QQmlPropertyPrivate::signalIndex() const { Q_ASSERT(type() == QQmlProperty::SignalProperty); - QMetaMethod m = object->metaObject()->method(core.coreIndex); + QMetaMethod m = object->metaObject()->method(core.coreIndex()); return QMetaObjectPrivate::signalIndex(m); } @@ -470,11 +470,11 @@ const char *QQmlProperty::propertyTypeName() const if (!d) return 0; if (d->isValueType()) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType); + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType()); Q_ASSERT(valueTypeMetaObject); - return valueTypeMetaObject->property(d->valueTypeData.coreIndex).typeName(); + return valueTypeMetaObject->property(d->valueTypeData.coreIndex()).typeName(); } else if (d->object && type() & Property && d->core.isValid()) { - return d->object->metaObject()->property(d->core.coreIndex).typeName(); + return d->object->metaObject()->property(d->core.coreIndex()).typeName(); } else { return 0; } @@ -491,8 +491,8 @@ bool QQmlProperty::operator==(const QQmlProperty &other) const // category is intentially omitted here as it is generated // from the other members return d->object == other.d->object && - d->core.coreIndex == other.d->core.coreIndex && - d->valueTypeData.coreIndex == other.d->valueTypeData.coreIndex; + d->core.coreIndex() == other.d->core.coreIndex() && + d->valueTypeData.coreIndex() == other.d->valueTypeData.coreIndex(); } /*! @@ -513,9 +513,9 @@ int QQmlPropertyPrivate::propertyType() const { uint type = this->type(); if (isValueType()) { - return valueTypeData.propType; + return valueTypeData.propType(); } else if (type & QQmlProperty::Property) { - return core.propType; + return core.propType(); } else { return QVariant::Invalid; } @@ -604,7 +604,7 @@ bool QQmlProperty::isDesignable() const if (!d) return false; if (type() & Property && d->core.isValid() && d->object) - return d->object->metaObject()->property(d->core.coreIndex).isDesignable(); + return d->object->metaObject()->property(d->core.coreIndex()).isDesignable(); else return false; } @@ -644,10 +644,10 @@ QString QQmlProperty::name() const // ### if (!d->object) { } else if (d->isValueType()) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType); + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType()); Q_ASSERT(valueTypeMetaObject); - const char *vtName = valueTypeMetaObject->property(d->valueTypeData.coreIndex).name(); + const char *vtName = valueTypeMetaObject->property(d->valueTypeData.coreIndex()).name(); d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(vtName); } else if (type() & SignalProperty) { QString name = QLatin1String("on") + d->core.name(d->object); @@ -671,7 +671,7 @@ QMetaProperty QQmlProperty::property() const if (!d) return QMetaProperty(); if (type() & Property && d->core.isValid() && d->object) - return d->object->metaObject()->property(d->core.coreIndex); + return d->object->metaObject()->property(d->core.coreIndex()); else return QMetaProperty(); } @@ -685,7 +685,7 @@ QMetaMethod QQmlProperty::method() const if (!d) return QMetaMethod(); if (type() & SignalProperty && d->object) - return d->object->metaObject()->method(d->core.coreIndex); + return d->object->metaObject()->method(d->core.coreIndex()); else return QMetaMethod(); } @@ -700,7 +700,7 @@ QQmlPropertyPrivate::binding(const QQmlProperty &that) if (!that.d || !that.isProperty() || !that.d->object) return 0; - QQmlPropertyIndex thatIndex(that.d->core.coreIndex, that.d->valueTypeData.coreIndex); + QQmlPropertyIndex thatIndex(that.d->core.coreIndex(), that.d->valueTypeData.coreIndex()); return binding(that.d->object, thatIndex); } @@ -1020,16 +1020,16 @@ QVariant QQmlPropertyPrivate::readValueProperty() { if (isValueType()) { - QQmlValueType *valueType = QQmlValueTypeFactory::valueType(core.propType); + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(core.propType()); Q_ASSERT(valueType); - valueType->read(object, core.coreIndex); - return valueType->metaObject()->property(valueTypeData.coreIndex).read(valueType); + valueType->read(object, core.coreIndex()); + return valueType->metaObject()->property(valueTypeData.coreIndex()).read(valueType); } else if (core.isQList()) { QQmlListProperty prop; core.readProperty(object, &prop); - return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType, engine)); + return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType(), engine)); } else if (core.isQObject()) { @@ -1039,21 +1039,21 @@ QVariant QQmlPropertyPrivate::readValueProperty() } else { - if (!core.propType) // Unregistered type - return object->metaObject()->property(core.coreIndex).read(object); + if (!core.propType()) // Unregistered type + return object->metaObject()->property(core.coreIndex()).read(object); QVariant value; int status = -1; void *args[] = { 0, &value, &status }; - if (core.propType == QMetaType::QVariant) { + if (core.propType() == QMetaType::QVariant) { args[0] = &value; } else { - value = QVariant(core.propType, (void*)0); + value = QVariant(core.propType(), (void*)0); args[0] = value.data(); } core.readPropertyWithArgs(object, args); - if (core.propType != QMetaType::QVariant && args[0] != value.data()) - return QVariant((QVariant::Type)core.propType, args[0]); + if (core.propType() != QMetaType::QVariant && args[0] != value.data()) + return QVariant((QVariant::Type)core.propType(), args[0]); return value; } @@ -1157,10 +1157,10 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, bool rv = false; if (valueTypeData.isValid()) { - QQmlValueType *writeBack = QQmlValueTypeFactory::valueType(core.propType); - writeBack->read(object, core.coreIndex); + QQmlValueType *writeBack = QQmlValueTypeFactory::valueType(core.propType()); + writeBack->read(object, core.coreIndex()); rv = write(writeBack, valueTypeData, value, context, flags); - writeBack->write(object, core.coreIndex, flags); + writeBack->write(object, core.coreIndex(), flags); } else { rv = write(object, core, value, context, flags); } @@ -1173,11 +1173,11 @@ bool QQmlPropertyPrivate::write(QObject *object, const QVariant &value, QQmlContextData *context, QQmlPropertyData::WriteFlags flags) { - const int propertyType = property.propType; + const int propertyType = property.propType(); const int variantType = value.userType(); if (property.isEnum()) { - QMetaProperty prop = object->metaObject()->property(property.coreIndex); + QMetaProperty prop = object->metaObject()->property(property.coreIndex()); QVariant v = value; // Enum values come through the script engine as doubles if (variantType == QVariant::Double) { @@ -1186,7 +1186,7 @@ bool QQmlPropertyPrivate::write(QObject *object, if (qFuzzyIsNull(fractional)) v.convert(QVariant::Int); } - return writeEnumProperty(prop, property.coreIndex, object, v, flags); + return writeEnumProperty(prop, property.coreIndex(), object, v, flags); } QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context); @@ -1274,9 +1274,9 @@ bool QQmlPropertyPrivate::write(QObject *object, QQmlMetaObject listType; if (enginePriv) { - listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType)); + listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType())); } else { - QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType)); + QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType())); if (!type) return false; listType = type->baseMetaObject(); @@ -1491,7 +1491,7 @@ bool QQmlProperty::reset() const { if (isResettable()) { void *args[] = { 0 }; - QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex, args); + QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex(), args); return true; } else { return false; @@ -1516,7 +1516,7 @@ bool QQmlPropertyPrivate::write(const QQmlProperty &that, bool QQmlProperty::hasNotifySignal() const { if (type() & Property && d->object) { - return d->object->metaObject()->property(d->core.coreIndex).hasNotifySignal(); + return d->object->metaObject()->property(d->core.coreIndex()).hasNotifySignal(); } return false; } @@ -1546,7 +1546,7 @@ bool QQmlProperty::connectNotifySignal(QObject *dest, int method) const if (!(type() & Property) || !d->object) return false; - QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); + QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex()); if (prop.hasNotifySignal()) { return QQmlPropertyPrivate::connect(d->object, prop.notifySignalIndex(), dest, method, Qt::DirectConnection); } else { @@ -1567,7 +1567,7 @@ bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const if (!(type() & Property) || !d->object) return false; - QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); + QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex()); if (prop.hasNotifySignal()) { QByteArray signal('2' + prop.notifySignal().methodSignature()); return QObject::connect(d->object, signal.constData(), dest, slot); @@ -1581,7 +1581,7 @@ bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const */ int QQmlProperty::index() const { - return d ? d->core.coreIndex : -1; + return d ? d->core.coreIndex() : -1; } QQmlPropertyIndex QQmlPropertyPrivate::propertyIndex(const QQmlProperty &that) diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 16d2bfef64..2565ec0ce6 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -83,7 +83,7 @@ public: QQmlPropertyIndex encodedIndex() const { return encodedIndex(core, valueTypeData); } static QQmlPropertyIndex encodedIndex(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData) - { return QQmlPropertyIndex(core.coreIndex, valueTypeData.coreIndex); } + { return QQmlPropertyIndex(core.coreIndex(), valueTypeData.coreIndex()); } inline QQmlContextData *effectiveContext() const; diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 38fddc8253..af9b8cc045 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -145,102 +145,102 @@ QQmlPropertyData::flagsForProperty(const QMetaProperty &p, QQmlEngine *engine) void QQmlPropertyData::lazyLoad(const QMetaProperty &p) { - coreIndex = p.propertyIndex(); - notifyIndex = QMetaObjectPrivate::signalIndex(p.notifySignal()); + setCoreIndex(p.propertyIndex()); + setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); Q_ASSERT(p.revision() <= Q_INT16_MAX); - revision = p.revision(); + setRevision(p.revision()); - flags = fastFlagsForProperty(p); + _flags = fastFlagsForProperty(p); int type = static_cast(p.type()); if (type == QMetaType::QObjectStar) { - propType = type; - flags.type = Flags::QObjectDerivedType; + setPropType(type); + _flags.type = Flags::QObjectDerivedType; } else if (type == QMetaType::QVariant) { - propType = type; - flags.type = Flags::QVariantType; + setPropType(type); + _flags.type = Flags::QVariantType; } else if (type == QVariant::UserType || type == -1) { - propTypeName = p.typeName(); - flags.notFullyResolved = true; + _flags.notFullyResolved = true; + setPropTypeName(p.typeName()); } else { - propType = type; + setPropType(type); } } void QQmlPropertyData::load(const QMetaProperty &p, QQmlEngine *engine) { - propType = p.userType(); - coreIndex = p.propertyIndex(); - notifyIndex = QMetaObjectPrivate::signalIndex(p.notifySignal()); - flags = fastFlagsForProperty(p); - flagsForPropertyType(propType, engine, flags); + setPropType(p.userType()); + setCoreIndex(p.propertyIndex()); + setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); + _flags = fastFlagsForProperty(p); + flagsForPropertyType(propType(), engine, _flags); Q_ASSERT(p.revision() <= Q_INT16_MAX); - revision = p.revision(); + setRevision(p.revision()); } void QQmlPropertyData::load(const QMetaMethod &m) { - coreIndex = m.methodIndex(); - arguments = 0; + setCoreIndex(m.methodIndex()); + setArguments(nullptr); - propType = m.returnType(); + setPropType(m.returnType()); - flags.type = Flags::FunctionType; + _flags.type = Flags::FunctionType; if (m.methodType() == QMetaMethod::Signal) - flags.isSignal = true; + _flags.isSignal = true; else if (m.methodType() == QMetaMethod::Constructor) { - flags.isConstructor = true; - propType = QMetaType::QObjectStar; + _flags.isConstructor = true; + setPropType(QMetaType::QObjectStar); } if (m.parameterCount()) { - flags.hasArguments = true; - if ((m.parameterCount() == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) { - flags.isV4Function = true; + _flags.hasArguments = true; + if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { + _flags.isV4Function = true; } } if (m.attributes() & QMetaMethod::Cloned) - flags.isCloned = true; + _flags.isCloned = true; Q_ASSERT(m.revision() <= Q_INT16_MAX); - revision = m.revision(); + setRevision(m.revision()); } void QQmlPropertyData::lazyLoad(const QMetaMethod &m) { - coreIndex = m.methodIndex(); - propType = QMetaType::Void; - arguments = 0; - flags.type = Flags::FunctionType; + setCoreIndex(m.methodIndex()); + setPropType(QMetaType::Void); + setArguments(nullptr); + _flags.type = Flags::FunctionType; if (m.methodType() == QMetaMethod::Signal) - flags.isSignal = true; + _flags.isSignal = true; else if (m.methodType() == QMetaMethod::Constructor) { - flags.isConstructor = true; - propType = QMetaType::QObjectStar; + _flags.isConstructor = true; + setPropType(QMetaType::QObjectStar); } const char *returnType = m.typeName(); if (!returnType) returnType = "\0"; if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) { - propTypeName = returnType; - flags.notFullyResolved = true; + setPropTypeName(returnType); + _flags.notFullyResolved = true; } const int paramCount = m.parameterCount(); if (paramCount) { - flags.hasArguments = true; - if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) { - flags.isV4Function = true; + _flags.hasArguments = true; + if ((paramCount == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { + _flags.isV4Function = true; } } if (m.attributes() & QMetaMethod::Cloned) - flags.isCloned = true; + _flags.isCloned = true; Q_ASSERT(m.revision() <= Q_INT16_MAX); - revision = m.revision(); + setRevision(m.revision()); } /*! @@ -343,10 +343,10 @@ void QQmlPropertyCache::appendProperty(const QString &name, QQmlPropertyData::Fl int coreIndex, int propType, int notifyIndex) { QQmlPropertyData data; - data.propType = propType; - data.coreIndex = coreIndex; - data.notifyIndex = notifyIndex; - data.flags = flags; + data.setPropType(propType); + data.setCoreIndex(coreIndex); + data.setNotifyIndex(notifyIndex); + data._flags = flags; QQmlPropertyData *old = findNamedProperty(name); if (old) @@ -363,20 +363,20 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag const QList &names) { QQmlPropertyData data; - data.propType = QVariant::Invalid; - data.coreIndex = coreIndex; - data.flags = flags; - data.arguments = 0; + data.setPropType(QVariant::Invalid); + data.setCoreIndex(coreIndex); + data._flags = flags; + data.setArguments(nullptr); QQmlPropertyData handler = data; - handler.flags.isSignalHandler = true; + handler._flags.isSignalHandler = true; if (types) { int argumentCount = *types; QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names); ::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int)); args->argumentsValid = true; - data.arguments = args; + data.setArguments(args); } QQmlPropertyData *old = findNamedProperty(name); @@ -402,16 +402,16 @@ void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flag int argumentCount = names.count(); QQmlPropertyData data; - data.propType = QMetaType::QVariant; - data.coreIndex = coreIndex; + data.setPropType(QMetaType::QVariant); + data.setCoreIndex(coreIndex); QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names); for (int ii = 0; ii < argumentCount; ++ii) args->arguments[ii + 1] = QMetaType::QVariant; args->argumentsValid = true; - data.arguments = args; + data.setArguments(args); - data.flags = flags; + data._flags = flags; QQmlPropertyData *old = findNamedProperty(name); if (old) @@ -570,20 +570,20 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QQmlPropertyData *sigdata = 0; if (m.methodType() == QMetaMethod::Signal) - data->flags = signalFlags; + data->_flags = signalFlags; else - data->flags = methodFlags; + data->_flags = methodFlags; data->lazyLoad(m); - data->flags.isDirect = !dynamicMetaObject; + data->_flags.isDirect = !dynamicMetaObject; Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); - data->metaObjectOffset = allowedRevisionCache.count() - 1; + data->setMetaObjectOffset(allowedRevisionCache.count() - 1); if (data->isSignal()) { sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart]; *sigdata = *data; - sigdata->flags.isSignalHandler = true; + sigdata->_flags.isSignalHandler = true; } QQmlPropertyData *old = 0; @@ -624,8 +624,8 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, if (old) { // We only overload methods in the same class, exactly like C++ - if (old->isFunction() && old->coreIndex >= methodOffset) - data->flags.isOverload = true; + if (old->isFunction() && old->coreIndex() >= methodOffset) + data->_flags.isOverload = true; data->markAsOverrideOf(old); } @@ -652,13 +652,13 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart]; - data->flags = propertyFlags; + data->_flags = propertyFlags; data->lazyLoad(p); - data->flags.isDirect = !dynamicMetaObject; + data->_flags.isDirect = !dynamicMetaObject; Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); - data->metaObjectOffset = allowedRevisionCache.count() - 1; + data->setMetaObjectOffset(allowedRevisionCache.count() - 1); QQmlPropertyData *old = 0; @@ -677,11 +677,11 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QQmlAccessorProperties::Property *accessorProperty = accessorProperties.property(str); // Fast properties may not be overrides or revisioned - Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision == 0)); + Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision() == 0)); if (accessorProperty) { - data->flags.hasAccessors = true; - data->accessors = accessorProperty->accessors; + data->_flags.hasAccessors = true; + data->setAccessors(accessorProperty->accessors); } else if (old) { data->markAsOverrideOf(old); } @@ -692,10 +692,11 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const { Q_ASSERT(data->notFullyResolved()); - data->propType = QMetaType::type(data->propTypeName); + data->setPropType(QMetaType::type(data->propTypeName())); + data->_flags.notFullyResolved = false; if (!data->isFunction()) { - if (data->propType == QMetaType::UnknownType) { + if (data->propType() == QMetaType::UnknownType) { const QMetaObject *mo = _metaObject; QQmlPropertyCache *p = _parent; while (p && (!mo || _ownMetaObject)) { @@ -704,22 +705,20 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const } int propOffset = mo->propertyOffset(); - if (mo && data->coreIndex < propOffset + mo->propertyCount()) { - while (data->coreIndex < propOffset) { + if (mo && data->coreIndex() < propOffset + mo->propertyCount()) { + while (data->coreIndex() < propOffset) { mo = mo->superClass(); propOffset = mo->propertyOffset(); } int registerResult = -1; void *argv[] = { ®isterResult }; - mo->static_metacall(QMetaObject::RegisterPropertyMetaType, data->coreIndex - propOffset, argv); - data->propType = registerResult == -1 ? QMetaType::UnknownType : registerResult; + mo->static_metacall(QMetaObject::RegisterPropertyMetaType, data->coreIndex() - propOffset, argv); + data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult); } } - flagsForPropertyType(data->propType, engine->qmlEngine(), data->flags); + flagsForPropertyType(data->propType(), engine->qmlEngine(), data->_flags); } - - data->flags.notFullyResolved = false; } void QQmlPropertyCache::updateRecur(const QMetaObject *metaObject) @@ -873,25 +872,25 @@ QString QQmlPropertyData::name(QObject *object) const QString QQmlPropertyData::name(const QMetaObject *metaObject) const { - if (!metaObject || coreIndex == -1) + if (!metaObject || coreIndex() == -1) return QString(); if (isFunction()) { - QMetaMethod m = metaObject->method(coreIndex); + QMetaMethod m = metaObject->method(coreIndex()); return QString::fromUtf8(m.name().constData()); } else { - QMetaProperty p = metaObject->property(coreIndex); + QMetaProperty p = metaObject->property(coreIndex()); return QString::fromUtf8(p.name()); } } void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor) { - overrideIndexIsProperty = !predecessor->isFunction(); - overrideIndex = predecessor->coreIndex; + setOverrideIndexIsProperty(!predecessor->isFunction()); + setOverrideIndex(predecessor->coreIndex()); - predecessor->flags.isOverridden = true; + predecessor->_flags.isOverridden = true; } struct StaticQtMetaObject : public QObject @@ -1124,7 +1123,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) { struct Sort { static bool lt(const QPair &lhs, const QPair &rhs) { - return lhs.second->coreIndex < rhs.second->coreIndex; + return lhs.second->coreIndex() < rhs.second->coreIndex(); } }; struct Insert { static void in(QQmlPropertyCache *This, @@ -1135,7 +1134,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) return; if (data->isFunction()) { - if (data->coreIndex < This->methodIndexCacheStart) + if (data->coreIndex() < This->methodIndexCacheStart) return; QPair entry = qMakePair((QString)iter.key(), data); @@ -1145,7 +1144,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) data = This->overrideData(data); if (data && !data->isFunction()) Insert::in(This, properties, methods, iter, data); } else { - if (data->coreIndex < This->propertyIndexCacheStart) + if (data->coreIndex() < This->propertyIndexCacheStart) return; QPair entry = qMakePair((QString)iter.key(), data); @@ -1176,11 +1175,11 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) QQmlPropertyData *data = properties.at(ii).second; int notifierId = -1; - if (data->notifyIndex != -1) - notifierId = data->notifyIndex - signalHandlerIndexCacheStart; + if (data->notifyIndex() != -1) + notifierId = data->notifyIndex() - signalHandlerIndexCacheStart; QMetaPropertyBuilder property = builder.addProperty(properties.at(ii).first.toUtf8(), - QMetaType::typeName(data->propType), + QMetaType::typeName(data->propType()), notifierId); property.setReadable(true); @@ -1192,8 +1191,8 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) QQmlPropertyData *data = methods.at(ii).second; QByteArray returnType; - if (data->propType != 0) - returnType = QMetaType::typeName(data->propType); + if (data->propType() != 0) + returnType = QMetaType::typeName(data->propType()); QByteArray signature; // '+=' reserves extra capacity. Follow-up appending will be probably free. @@ -1201,7 +1200,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) QQmlPropertyCacheMethodArguments *arguments = 0; if (data->hasArguments()) { - arguments = (QQmlPropertyCacheMethodArguments *)data->arguments; + arguments = (QQmlPropertyCacheMethodArguments *)data->arguments(); Q_ASSERT(arguments->argumentsValid); for (int ii = 0; ii < arguments->arguments[0]; ++ii) { if (ii != 0) signature.append(','); @@ -1228,7 +1227,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) if (!_defaultPropertyName.isEmpty()) { QQmlPropertyData *dp = property(_defaultPropertyName, 0, 0); - if (dp && dp->coreIndex >= propertyIndexCacheStart) { + if (dp && dp->coreIndex() >= propertyIndexCacheStart) { Q_ASSERT(!dp->isFunction()); builder.addClassInfo("DefaultProperty", _defaultPropertyName.toUtf8()); } @@ -1452,7 +1451,7 @@ QList QQmlPropertyCache::signalParameterNames(int index) const { QQmlPropertyData *signalData = signal(index); if (signalData && signalData->hasArguments()) { - QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments; + QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments(); if (args && args->names) return *args->names; const QMetaMethod &method = QMetaObjectPrivate::signal(firstCppMetaObject(), index); @@ -1554,9 +1553,9 @@ QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const { - Q_ASSERT(!_m.isNull() && data.coreIndex >= 0); + Q_ASSERT(!_m.isNull() && data.coreIndex() >= 0); - int type = data.propType; + int type = data.propType(); const char *propTypeName = 0; @@ -1566,16 +1565,16 @@ int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *u if (_m.isT1()) { QQmlPropertyCache *c = _m.asT1(); - Q_ASSERT(data.coreIndex < c->methodIndexCacheStart + c->methodIndexCache.count()); + Q_ASSERT(data.coreIndex() < c->methodIndexCacheStart + c->methodIndexCache.count()); - while (data.coreIndex < c->methodIndexCacheStart) + while (data.coreIndex() < c->methodIndexCacheStart) c = c->_parent; const QMetaObject *metaObject = c->createMetaObject(); Q_ASSERT(metaObject); - m = metaObject->method(data.coreIndex); + m = metaObject->method(data.coreIndex()); } else { - m = _m.asT2()->method(data.coreIndex); + m = _m.asT2()->method(data.coreIndex()); } type = m.returnType(); @@ -1615,19 +1614,19 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, QQmlPropertyData *rv = const_cast(&c->methodIndexCache.at(index - c->methodIndexCacheStart)); - if (rv->arguments && static_cast(rv->arguments)->argumentsValid) - return static_cast(rv->arguments)->arguments; + if (rv->arguments() && static_cast(rv->arguments())->argumentsValid) + return static_cast(rv->arguments())->arguments; const QMetaObject *metaObject = c->createMetaObject(); Q_ASSERT(metaObject); QMetaMethod m = metaObject->method(index); int argc = m.parameterCount(); - if (!rv->arguments) { + if (!rv->arguments()) { A *args = c->createArgumentsObject(argc, m.parameterNames()); - rv->arguments = args; + rv->setArguments(args); } - A *args = static_cast(rv->arguments); + A *args = static_cast(rv->arguments()); QList argTypeNames; // Only loaded if needed @@ -1651,7 +1650,7 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, args->arguments[ii + 1] = type; } args->argumentsValid = true; - return static_cast(rv->arguments)->arguments; + return static_cast(rv->arguments())->arguments; } else { QMetaMethod m = _m.asT2()->method(index); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index cc3ad62706..79fd77dd45 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -64,6 +64,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QCryptographicHash; @@ -131,77 +133,123 @@ public: inline void copyPropertyTypeFlags(Flags from); }; - Flags getFlags() const { return flags; } - void setFlags(Flags f) { flags = f; } - - bool isValid() const { return coreIndex != -1; } - - bool isConstant() const { return flags.isConstant; } - bool isWritable() const { return flags.isWritable; } - void setWritable(bool onoff) { flags.isWritable = onoff; } - bool isResettable() const { return flags.isResettable; } - bool isAlias() const { return flags.isAlias; } - bool isFinal() const { return flags.isFinal; } - bool isOverridden() const { return flags.isOverridden; } - bool isDirect() const { return flags.isDirect; } - bool hasAccessors() const { return flags.hasAccessors; } - bool isFunction() const { return flags.type == Flags::FunctionType; } - bool isQObject() const { return flags.type == Flags::QObjectDerivedType; } - bool isEnum() const { return flags.type == Flags::EnumType; } - bool isQList() const { return flags.type == Flags::QListType; } - bool isQmlBinding() const { return flags.type == Flags::QmlBindingType; } - bool isQJSValue() const { return flags.type == Flags::QJSValueType; } - bool isV4Handle() const { return flags.type == Flags::V4HandleType; } - bool isVarProperty() const { return flags.type == Flags::VarPropertyType; } - bool isQVariant() const { return flags.type == Flags::QVariantType; } - bool isVMEFunction() const { return flags.isVMEFunction; } - bool hasArguments() const { return flags.hasArguments; } - bool isSignal() const { return flags.isSignal; } - bool isVMESignal() const { return flags.isVMESignal; } - bool isV4Function() const { return flags.isV4Function; } - bool isSignalHandler() const { return flags.isSignalHandler; } - bool isOverload() const { return flags.isOverload; } - void setOverload(bool onoff) { flags.isOverload = onoff; } - bool isCloned() const { return flags.isCloned; } - bool isConstructor() const { return flags.isConstructor; } - - bool hasOverride() const { return !(flags.hasAccessors) && - overrideIndex >= 0; } - bool hasRevision() const { return !(flags.hasAccessors) && revision != 0; } - - bool isFullyResolved() const { return !flags.notFullyResolved; } + Flags flags() const { return _flags; } + void setFlags(Flags f) { _flags = f; } + + bool isValid() const { return coreIndex() != -1; } + + bool isConstant() const { return _flags.isConstant; } + bool isWritable() const { return _flags.isWritable; } + void setWritable(bool onoff) { _flags.isWritable = onoff; } + bool isResettable() const { return _flags.isResettable; } + bool isAlias() const { return _flags.isAlias; } + bool isFinal() const { return _flags.isFinal; } + bool isOverridden() const { return _flags.isOverridden; } + bool isDirect() const { return _flags.isDirect; } + bool hasAccessors() const { return _flags.hasAccessors; } + bool isFunction() const { return _flags.type == Flags::FunctionType; } + bool isQObject() const { return _flags.type == Flags::QObjectDerivedType; } + bool isEnum() const { return _flags.type == Flags::EnumType; } + bool isQList() const { return _flags.type == Flags::QListType; } + bool isQmlBinding() const { return _flags.type == Flags::QmlBindingType; } + bool isQJSValue() const { return _flags.type == Flags::QJSValueType; } + bool isV4Handle() const { return _flags.type == Flags::V4HandleType; } + bool isVarProperty() const { return _flags.type == Flags::VarPropertyType; } + bool isQVariant() const { return _flags.type == Flags::QVariantType; } + bool isVMEFunction() const { return _flags.isVMEFunction; } + bool hasArguments() const { return _flags.hasArguments; } + bool isSignal() const { return _flags.isSignal; } + bool isVMESignal() const { return _flags.isVMESignal; } + bool isV4Function() const { return _flags.isV4Function; } + bool isSignalHandler() const { return _flags.isSignalHandler; } + bool isOverload() const { return _flags.isOverload; } + void setOverload(bool onoff) { _flags.isOverload = onoff; } + bool isCloned() const { return _flags.isCloned; } + bool isConstructor() const { return _flags.isConstructor; } + + bool hasOverride() const { return !(_flags.hasAccessors) && + overrideIndex() >= 0; } + bool hasRevision() const { return !(_flags.hasAccessors) && revision() != 0; } + + bool isFullyResolved() const { return !_flags.notFullyResolved; } + + int propType() const { Q_ASSERT(isFullyResolved()); return _propType; } + void setPropType(int pt) { _propType = pt; } + + const char *propTypeName() const { Q_ASSERT(!isFullyResolved()); return _propTypeName; } + void setPropTypeName(const char *ptn) { _propTypeName = ptn; } + + int notifyIndex() const { return _notifyIndex; } + void setNotifyIndex(int idx) { _notifyIndex = idx; } + + QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; } + void setArguments(QQmlPropertyCacheMethodArguments *args) { _arguments = args; } + + int revision() const { return _revision; } + void setRevision(int rev) + { + Q_ASSERT(rev >= std::numeric_limits::min()); + Q_ASSERT(rev <= std::numeric_limits::max()); + _revision = qint16(rev); + } + + int metaObjectOffset() const { return _metaObjectOffset; } + void setMetaObjectOffset(int off) + { + Q_ASSERT(off >= std::numeric_limits::min()); + Q_ASSERT(off <= std::numeric_limits::max()); + _metaObjectOffset = qint16(off); + } + bool overrideIndexIsProperty() const { return _overrideIndexIsProperty; } + void setOverrideIndexIsProperty(bool onoff) { _overrideIndexIsProperty = onoff; } + + int overrideIndex() const { return _overrideIndex; } + void setOverrideIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits::min()); + Q_ASSERT(idx <= std::numeric_limits::max()); + _overrideIndex = idx; + } + + QQmlAccessors *accessors() const { return _accessors; } + void setAccessors(QQmlAccessors *acc) { _accessors = acc; } + + int coreIndex() const { return _coreIndex; } + void setCoreIndex(int idx) { _coreIndex = idx; } + +private: union { - int propType; // When !NotFullyResolved - const char *propTypeName; // When NotFullyResolved + int _propType; // When !NotFullyResolved + const char *_propTypeName; // When NotFullyResolved }; union { // The notify index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). - int notifyIndex; // When !IsFunction - void *arguments; // When IsFunction && HasArguments + int _notifyIndex; // When !IsFunction + QQmlPropertyCacheMethodArguments *_arguments; // When IsFunction && HasArguments }; union { struct { // When !HasAccessors - qint16 revision; - qint16 metaObjectOffset; + qint16 _revision; + qint16 _metaObjectOffset; struct { // When !IsValueTypeVirtual - uint overrideIndexIsProperty : 1; - signed int overrideIndex : 31; + uint _overrideIndexIsProperty : 1; + signed int _overrideIndex : 31; }; }; struct { // When HasAccessors - QQmlAccessors *accessors; + QQmlAccessors *_accessors; }; }; - int coreIndex; -private: + int _coreIndex; + Flags _flags; + friend class QQmlPropertyData; friend class QQmlPropertyCache; - Flags flags; }; class QQmlPropertyData : public QQmlPropertyRawData @@ -236,20 +284,20 @@ public: inline void readPropertyWithArgs(QObject *target, void *args[]) const { if (hasAccessors()) { - accessors->read(target, args[0]); + accessors()->read(target, args[0]); } else { - QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, args); + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args); } } bool writeProperty(QObject *target, void *value, WriteFlags flags) const { - if (flags.testFlag(BypassInterceptor) && hasAccessors() && accessors->write) { - accessors->write(target, value); + if (flags.testFlag(BypassInterceptor) && hasAccessors() && accessors()->write) { + accessors()->write(target, value); } else { int status = -1; void *argv[] = { value, 0, &status, &flags }; - QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex, argv); + QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv); } return true; } @@ -275,7 +323,7 @@ private: friend class QQmlPropertyCache; void lazyLoad(const QMetaProperty &); void lazyLoad(const QMetaMethod &); - bool notFullyResolved() const { return flags.notFullyResolved; } + bool notFullyResolved() const { return _flags.notFullyResolved; } }; class QQmlPropertyCacheMethodArguments; @@ -577,13 +625,13 @@ void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flag QQmlPropertyData::QQmlPropertyData() { - propType = 0; - coreIndex = -1; - notifyIndex = -1; - overrideIndexIsProperty = false; - overrideIndex = -1; - revision = 0; - metaObjectOffset = -1; + setPropType(0); + setNotifyIndex(-1); + setOverrideIndexIsProperty(false); + setOverrideIndex(-1); + setRevision(0); + setMetaObjectOffset(-1); + setCoreIndex(-1); } QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d) @@ -593,11 +641,11 @@ QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d) bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other) { - return flags == other.flags && - propType == other.propType && - coreIndex == other.coreIndex && - notifyIndex == other.notifyIndex && - revision == other.revision; + return _flags == other._flags && + propType() == other.propType() && + coreIndex() == other.coreIndex() && + notifyIndex() == other.notifyIndex() && + revision() == other.revision(); } inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const @@ -660,7 +708,7 @@ inline QQmlPropertyData *QQmlPropertyCache::signal(int index) const return _parent->signal(index); QQmlPropertyData *rv = const_cast(&methodIndexCache.at(index - signalHandlerIndexCacheStart)); - Q_ASSERT(rv->isSignal() || rv->coreIndex == -1); + Q_ASSERT(rv->isSignal() || rv->coreIndex() == -1); return ensureResolved(rv); } @@ -692,16 +740,16 @@ QQmlPropertyCache::overrideData(QQmlPropertyData *data) const if (!data->hasOverride()) return 0; - if (data->overrideIndexIsProperty) - return property(data->overrideIndex); + if (data->overrideIndexIsProperty()) + return property(data->overrideIndex()); else - return method(data->overrideIndex); + return method(data->overrideIndex()); } bool QQmlPropertyCache::isAllowedInRevision(QQmlPropertyData *data) const { - return (data->hasAccessors() || (data->metaObjectOffset == -1 && data->revision == 0)) || - (allowedRevisionCache[data->metaObjectOffset] >= data->revision); + return (data->hasAccessors() || (data->metaObjectOffset() == -1 && data->revision() == 0)) || + (allowedRevisionCache[data->metaObjectOffset()] >= data->revision()); } int QQmlPropertyCache::propertyCount() const diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index c4008d2a05..2566ab06b5 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -370,10 +370,10 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha if (result->isFunction()) // calling a Q_INVOKABLE function of a value type - return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex); + return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex()); #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ - if (result->propType == metatype) { \ + if (result->propType() == metatype) { \ cpptype v; \ void *args[] = { &v, 0 }; \ metaObject->d.static_metacall(reinterpret_cast(gadget), QMetaObject::ReadProperty, index, args); \ @@ -382,7 +382,7 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha const QMetaObject *metaObject = r->d()->propertyCache->metaObject(); - int index = result->coreIndex; + int index = result->coreIndex(); QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index); void *gadget = r->d()->gadgetPtr; @@ -395,10 +395,10 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha QVariant v; void *args[] = { Q_NULLPTR, Q_NULLPTR }; - if (result->propType == QMetaType::QVariant) { + if (result->propType() == QMetaType::QVariant) { args[0] = &v; } else { - v = QVariant(result->propType, static_cast(Q_NULLPTR)); + v = QVariant(result->propType(), static_cast(Q_NULLPTR)); args[0] = v.data(); } metaObject->d.static_metacall(reinterpret_cast(gadget), QMetaObject::ReadProperty, index, args); @@ -448,8 +448,8 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QQmlPropertyData cacheData; cacheData.setWritable(true); - cacheData.propType = writeBackPropertyType; - cacheData.coreIndex = reference->d()->property; + cacheData.setPropType(writeBackPropertyType); + cacheData.setCoreIndex(reference->d()->property); QV4::Scoped bindingFunction(scope, (const Value &)f); bindingFunction->initBindingLocation(); @@ -459,11 +459,11 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QQmlPropertyPrivate::setBinding(newBinding); return; } else { - QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex)); + QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex())); } } - QMetaProperty property = metaObject->property(pd->coreIndex); + QMetaProperty property = metaObject->property(pd->coreIndex()); Q_ASSERT(property.isValid()); QVariant v = v4->toVariant(value, property.userType()); diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 47c125355b..c8281f02c0 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -164,8 +164,8 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() if (!pd) return; - if (pd->notifyIndex != -1) - connect(target, pd->notifyIndex, ctxt->engine); + if (pd->notifyIndex() != -1) + connect(target, pd->notifyIndex(), ctxt->engine); } metaObject.setFlag(); @@ -225,7 +225,7 @@ bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) continue; const int valueIndex = vi->m_propertyIndex.valueTypeIndex(); - int type = QQmlData::get(object)->propertyCache->property(id)->propType; + int type = QQmlData::get(object)->propertyCache->property(id)->propType(); if (type != QVariant::Invalid) { if (valueIndex != -1) { @@ -883,7 +883,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * return -1; const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); // Value type property - QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType); + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType()); Q_ASSERT(valueType); valueType->read(target, coreIndex); diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index 9d24a6c511..b974641cca 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -252,11 +252,11 @@ void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item, const UniformData &d = uniformData[shaderType].at(i); QQmlPropertyData *pd = propCache->property(QString::fromUtf8(d.name), nullptr, nullptr); if (pd && !pd->isFunction()) { - if (pd->notifyIndex == -1) { + if (pd->notifyIndex() == -1) { qWarning("QQuickOpenGLShaderEffect: property '%s' does not have notification method!", d.name.constData()); } else { auto *mapper = signalMappers[shaderType].at(i); - mapper->setSignalIndex(pd->notifyIndex); + mapper->setSignalIndex(pd->notifyIndex()); Q_ASSERT(item->metaObject() == itemMetaObject); QObjectPrivate::connectImpl(item, mapper->signalIndex(), item, nullptr, mapper, Qt::AutoConnection, nullptr, itemMetaObject); @@ -347,7 +347,7 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, } else { if (QQmlPropertyData *pd = propCache->property(QString::fromUtf8(d.name), nullptr, nullptr)) { if (!pd->isFunction()) - d.propertyIndex = pd->coreIndex; + d.propertyIndex = pd->coreIndex(); } const int mappedId = uniformData[shaderType].size() | (shaderType << 16); mapper = new QtPrivate::MappedSlotObject([this, mappedId](){ diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 8a60b04494..ad06946b0b 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -4128,7 +4128,7 @@ void tst_qqmllanguage::preservePropertyCacheOnGroupObjects() QVERIFY(subCache); QQmlPropertyData *pd = subCache->property(QStringLiteral("newProperty"), /*object*/0, /*context*/0); QVERIFY(pd); - QCOMPARE(pd->propType, qMetaTypeId()); + QCOMPARE(pd->propType(), qMetaTypeId()); } void tst_qqmllanguage::propertyCacheInSync() diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index 2916d8455c..824fe445c0 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -111,16 +111,16 @@ void tst_qqmlpropertycache::properties() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "propertyA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyA")); QVERIFY((data = cacheProperty(cache, "propertyB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyB")); QVERIFY((data = cacheProperty(cache, "propertyC"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyC")); QVERIFY((data = cacheProperty(cache, "propertyD"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyD")); } void tst_qqmlpropertycache::propertiesDerived() @@ -135,16 +135,16 @@ void tst_qqmlpropertycache::propertiesDerived() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "propertyA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyA")); QVERIFY((data = cacheProperty(cache, "propertyB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyB")); QVERIFY((data = cacheProperty(cache, "propertyC"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyC")); QVERIFY((data = cacheProperty(cache, "propertyD"))); - QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD")); + QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyD")); } void tst_qqmlpropertycache::methods() @@ -158,28 +158,28 @@ void tst_qqmlpropertycache::methods() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "slotA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotA()")); QVERIFY((data = cacheProperty(cache, "slotB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotB()")); QVERIFY((data = cacheProperty(cache, "signalA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); QVERIFY((data = cacheProperty(cache, "signalB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); QVERIFY((data = cacheProperty(cache, "propertyAChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); QVERIFY((data = cacheProperty(cache, "propertyBChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); QVERIFY((data = cacheProperty(cache, "propertyCChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); QVERIFY((data = cacheProperty(cache, "propertyDChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } void tst_qqmlpropertycache::methodsDerived() @@ -194,28 +194,28 @@ void tst_qqmlpropertycache::methodsDerived() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "slotA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotA()")); QVERIFY((data = cacheProperty(cache, "slotB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotB()")); QVERIFY((data = cacheProperty(cache, "signalA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); QVERIFY((data = cacheProperty(cache, "signalB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); QVERIFY((data = cacheProperty(cache, "propertyAChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); QVERIFY((data = cacheProperty(cache, "propertyBChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); QVERIFY((data = cacheProperty(cache, "propertyCChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); QVERIFY((data = cacheProperty(cache, "propertyDChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } void tst_qqmlpropertycache::signalHandlers() @@ -229,22 +229,22 @@ void tst_qqmlpropertycache::signalHandlers() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "onSignalA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); QVERIFY((data = cacheProperty(cache, "onSignalB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); QVERIFY((data = cacheProperty(cache, "onPropertyAChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyBChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyCChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyDChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } void tst_qqmlpropertycache::signalHandlersDerived() @@ -259,22 +259,22 @@ void tst_qqmlpropertycache::signalHandlersDerived() QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "onSignalA"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()")); QVERIFY((data = cacheProperty(cache, "onSignalB"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()")); QVERIFY((data = cacheProperty(cache, "onPropertyAChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyBChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyCChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()")); QVERIFY((data = cacheProperty(cache, "onPropertyDChanged"))); - QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()")); + QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } class TestClass : public QObject -- cgit v1.2.3 From 8bf7cfb7880775f49dfbaf9a2be2202479eaaf99 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Aug 2016 16:03:03 +0200 Subject: Fix qmldevtools for QML compiler Re-add some run-time functions in the qmldevtools and replace the use of the WTF assert that pulls in some wtf functions with a Q_ASSERT. Change-Id: Id7b4bdb02c54e8b498db3fab78870463fa4fac9a Reviewed-by: Erik Verbruggen --- src/3rdparty/masm/wtf/StdLibExtras.h | 2 +- src/qml/jsruntime/qv4runtime.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/3rdparty/masm/wtf/StdLibExtras.h b/src/3rdparty/masm/wtf/StdLibExtras.h index 605f98ec82..f0d792ed52 100644 --- a/src/3rdparty/masm/wtf/StdLibExtras.h +++ b/src/3rdparty/masm/wtf/StdLibExtras.h @@ -166,7 +166,7 @@ template char (&ArrayLengthHelperFunction(T (&)[0]))[0]; // Efficient implementation that takes advantage of powers of two. inline size_t roundUpToMultipleOf(size_t divisor, size_t x) { - ASSERT(divisor && !(divisor & (divisor - 1))); + Q_ASSERT(divisor && !(divisor & (divisor - 1))); size_t remainderMask = divisor - 1; return (x + remainderMask) & ~remainderMask; } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index f3d166d695..c95e767bb0 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1716,6 +1716,8 @@ ReturnedValue Runtime::method_ushr(const Value &left, const Value &right) return Encode(res); } +#endif // V4_BOOTSTRAP + ReturnedValue Runtime::method_greaterThan(const Value &left, const Value &right) { TRACE2(left, right); @@ -1824,8 +1826,6 @@ Bool Runtime::method_toBoolean(const Value &value) return value.toBoolean(); } -#endif // V4_BOOTSTRAP - } // namespace QV4 QT_END_NAMESPACE -- cgit v1.2.3 From eea2754976a6b4363be7575c7082a4df92e1217e Mon Sep 17 00:00:00 2001 From: Dominik Holland Date: Mon, 2 May 2016 17:29:40 +0200 Subject: Added Logging Category support to QML New Logging Categories can be defined by using the LoggingCategory type and define a name for the category When the id of a valid LoggingCategory is provided as the first argument to console.log and friends the LoggingCategory is used instead of the default "qml" LoggingCategory [ChangeLog][QML Elements] Added a LoggingCategory type and added support for it to the console object Change-Id: Ifaeed5f71de6ea6d8172d8c838d6e7789c4d6b9d Reviewed-by: Simon Hausmann --- src/qml/qml/qml.pri | 6 +- src/qml/qml/qqmlengine.cpp | 2 + src/qml/qml/qqmlloggingcategory.cpp | 128 +++++++++++++++++++++ src/qml/qml/qqmlloggingcategory_p.h | 89 ++++++++++++++ src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 25 +++- .../qml/qqmlconsole/data/categorized_logging.qml | 65 +++++++++++ tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp | 36 ++++++ tests/auto/shared/util.cpp | 11 +- tests/auto/shared/util.h | 5 +- 9 files changed, 358 insertions(+), 9 deletions(-) create mode 100644 src/qml/qml/qqmlloggingcategory.cpp create mode 100644 src/qml/qml/qqmlloggingcategory_p.h create mode 100644 tests/auto/qml/qqmlconsole/data/categorized_logging.qml diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 87be140cbb..4244b16210 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -49,7 +49,8 @@ SOURCES += \ $$PWD/qqmlfileselector.cpp \ $$PWD/qqmlobjectcreator.cpp \ $$PWD/qqmldirparser.cpp \ - $$PWD/qqmldelayedcallqueue.cpp + $$PWD/qqmldelayedcallqueue.cpp \ + $$PWD/qqmlloggingcategory.cpp HEADERS += \ $$PWD/qqmlglobal_p.h \ @@ -121,7 +122,8 @@ HEADERS += \ $$PWD/qqmlfileselector.h \ $$PWD/qqmlobjectcreator_p.h \ $$PWD/qqmldirparser_p.h \ - $$PWD/qqmldelayedcallqueue_p.h + $$PWD/qqmldelayedcallqueue_p.h \ + $$PWD/qqmlloggingcategory_p.h include(ftw/ftw.pri) include(v8/v8.pri) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 48832f3b07..4acc74ee43 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -89,6 +89,7 @@ #include #include #include +#include #ifdef Q_OS_WIN // for %APPDATA% # include @@ -184,6 +185,7 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int qmlRegisterType(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1 qmlRegisterCustomType(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser); qmlRegisterType(); + qmlRegisterType(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "LoggingCategory"); //Only available in >=2.8 } diff --git a/src/qml/qml/qqmlloggingcategory.cpp b/src/qml/qml/qqmlloggingcategory.cpp new file mode 100644 index 0000000000..88cf14cba0 --- /dev/null +++ b/src/qml/qml/qqmlloggingcategory.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#include "qqmlloggingcategory_p.h" + +#include + +/*! + \qmltype LoggingCategory + \ingroup qml-utility-elements + \inqmlmodule QtQml + \brief Defines a logging category in QML + \since 5.8 + + A logging category can be passed to console.log() and friends as the first argument. + If supplied to to the logger the LoggingCategory's name will be used as Logging Category + otherwise the default logging category will be used. + + \qml + import QtQuick 2.8 + + Item { + LoggingCategory { + id: category + name: "com.qt.category" + } + + Component.onCompleted: { + console.log(category, "message"); + } + } + \endqml + + \note As the creation of objects is expensive, it is encouraged to put the needed + LoggingCategory definitions into a singleton and import this where needed. + + \sa QLoggingCategory +*/ + +/*! + \qmlproperty string QtQml::LoggingCategory::name + + Holds the name of the logging category. + + \note This property needs to be set when declaring the LoggingCategory + and cannot be changed later. + + \sa QLoggingCategory::name() +*/ + +QQmlLoggingCategory::QQmlLoggingCategory(QObject *parent) + : QObject(parent) + , m_initialized(false) +{ +} + +QQmlLoggingCategory::~QQmlLoggingCategory() +{ +} + +QString QQmlLoggingCategory::name() const +{ + return QString::fromUtf8(m_name); +} + +QLoggingCategory *QQmlLoggingCategory::category() const +{ + return m_category.data(); +} + +void QQmlLoggingCategory::classBegin() +{ +} + +void QQmlLoggingCategory::componentComplete() +{ + m_initialized = true; + if (m_name.isNull()) + qmlInfo(this) << QString(QLatin1String("Declaring the name of the LoggingCategory is mandatory and cannot be changed later !")); +} + +void QQmlLoggingCategory::setName(const QString &name) +{ + if (m_initialized) { + qmlInfo(this) << QString(QLatin1String("The name of a LoggingCategory cannot be changed after the Item is created")); + return; + } + + m_name = name.toUtf8(); + QScopedPointer category(new QLoggingCategory(m_name.constData())); + m_category.swap(category); +} diff --git a/src/qml/qml/qqmlloggingcategory_p.h b/src/qml/qml/qqmlloggingcategory_p.h new file mode 100644 index 0000000000..2b7f2f5b53 --- /dev/null +++ b/src/qml/qml/qqmlloggingcategory_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +#ifndef QQMLLOGGINGCATEGORY_P_H +#define QQMLLOGGINGCATEGORY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQmlLoggingCategory : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + + Q_PROPERTY(QString name READ name WRITE setName) + +public: + QQmlLoggingCategory(QObject *parent = 0); + virtual ~QQmlLoggingCategory(); + + QString name() const; + void setName(const QString &name); + + QLoggingCategory *category() const; + + void classBegin() override; + void componentComplete() override; + +private: + QByteArray m_name; + QScopedPointer m_category; + bool m_initialized; +}; + +QT_END_NAMESPACE + +#endif // QQMLLOGGINGCATEGORY_H diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 76a900c846..edc83b3bf1 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE using namespace QV4; @@ -1464,11 +1467,26 @@ static QString jsStack(QV4::ExecutionEngine *engine) { static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *ctx, bool printStack = false) { + QLoggingCategory *loggingCategory = 0; QString result; QV4::ExecutionEngine *v4 = ctx->d()->engine; - for (int i = 0; i < ctx->argc(); ++i) { - if (i != 0) + int start = 0; + if (ctx->argc() > 0) { + if (const QObjectWrapper* wrapper = ctx->args()[0].as()) { + if (QQmlLoggingCategory* category = qobject_cast(wrapper->object())) { + if (category->category()) + loggingCategory = category->category(); + else + V4THROW_ERROR("A QmlLoggingCatgory was provided without a valid name"); + start = 1; + } + } + } + + + for (int i = start; i < ctx->argc(); ++i) { + if (i != start) result.append(QLatin1Char(' ')); if (ctx->args()[i].as()) @@ -1485,7 +1503,8 @@ static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *c static QLoggingCategory qmlLoggingCategory("qml"); static QLoggingCategory jsLoggingCategory("js"); - QLoggingCategory *loggingCategory = v4->qmlEngine() ? &qmlLoggingCategory : &jsLoggingCategory; + if (!loggingCategory) + loggingCategory = v4->qmlEngine() ? &qmlLoggingCategory : &jsLoggingCategory; QV4::StackFrame frame = v4->currentStackFrame(); const QByteArray baSource = frame.source.toUtf8(); const QByteArray baFunction = frame.function.toUtf8(); diff --git a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml new file mode 100644 index 0000000000..d19b6ecc41 --- /dev/null +++ b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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$ +** +****************************************************************************/ + +import QtQuick 2.8 + +Item { + id:root + + LoggingCategory { + id: testCategory + name: "qt.test" + } + + LoggingCategory { + id: emptyCategory + } + + Component.onCompleted: { + console.debug(testCategory, "console.debug"); + console.log(testCategory, "console.log"); + console.info(testCategory, "console.info"); + console.warn(testCategory, "console.warn"); + console.error(testCategory, "console.error"); + + testCategory.name = "qt.test2"; + + console.error(emptyCategory, "console.error"); + } +} diff --git a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp index f12656c5fe..f832143935 100644 --- a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp +++ b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp @@ -40,6 +40,7 @@ public: private slots: void logging(); + void categorized_logging(); void tracing(); void profiling(); void testAssert(); @@ -87,6 +88,41 @@ void tst_qqmlconsole::logging() delete object; } +void tst_qqmlconsole::categorized_logging() +{ + QUrl testUrl = testFileUrl("categorized_logging.qml"); + QQmlTestMessageHandler messageHandler; + messageHandler.setIncludeCategoriesEnabled(true); + + QLoggingCategory testCategory("qt.test"); + testCategory.setEnabled(QtDebugMsg, true); + QVERIFY(testCategory.isDebugEnabled()); + QVERIFY(testCategory.isWarningEnabled()); + QVERIFY(testCategory.isCriticalEnabled()); + + QQmlComponent component(&engine, testUrl); + QObject *object = component.create(); + QVERIFY2(object != 0, component.errorString().toUtf8()); + + QVERIFY(messageHandler.messages().contains("qt.test: console.info")); + QVERIFY(messageHandler.messages().contains("qt.test: console.warn")); + QVERIFY(messageHandler.messages().contains("qt.test: console.error")); + + QString emptyCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(50).arg(5) + + "QML LoggingCategory: Declaring the name of the LoggingCategory is mandatory and cannot be changed later !"; + QVERIFY(messageHandler.messages().contains(emptyCategory)); + + QString changedCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(45).arg(5) + + "QML LoggingCategory: The name of a LoggingCategory cannot be changed after the Item is created"; + QVERIFY(messageHandler.messages().contains(changedCategory)); + + QString useEmptyCategory = "default: " + QString::fromLatin1("%1:%2: ").arg(testUrl.toString()).arg(63) + + "Error: A QmlLoggingCatgory was provided without a valid name"; + QVERIFY(messageHandler.messages().contains(useEmptyCategory)); + + delete object; +} + void tst_qqmlconsole::tracing() { QUrl testUrl = testFileUrl("tracing.qml"); diff --git a/tests/auto/shared/util.cpp b/tests/auto/shared/util.cpp index 55041eeb4d..96beb51612 100644 --- a/tests/auto/shared/util.cpp +++ b/tests/auto/shared/util.cpp @@ -101,11 +101,15 @@ Q_GLOBAL_STATIC(QMutex, qQmlTestMessageHandlerMutex) QQmlTestMessageHandler *QQmlTestMessageHandler::m_instance = 0; -void QQmlTestMessageHandler::messageHandler(QtMsgType, const QMessageLogContext &, const QString &message) +void QQmlTestMessageHandler::messageHandler(QtMsgType, const QMessageLogContext &context, const QString &message) { QMutexLocker locker(qQmlTestMessageHandlerMutex()); - if (QQmlTestMessageHandler::m_instance) - QQmlTestMessageHandler::m_instance->m_messages.push_back(message); + if (QQmlTestMessageHandler::m_instance) { + if (QQmlTestMessageHandler::m_instance->m_includeCategories) + QQmlTestMessageHandler::m_instance->m_messages.push_back(QString("%1: %2").arg(context.category, message)); + else + QQmlTestMessageHandler::m_instance->m_messages.push_back(message); + } } QQmlTestMessageHandler::QQmlTestMessageHandler() @@ -114,6 +118,7 @@ QQmlTestMessageHandler::QQmlTestMessageHandler() Q_ASSERT(!QQmlTestMessageHandler::m_instance); QQmlTestMessageHandler::m_instance = this; m_oldHandler = qInstallMessageHandler(messageHandler); + m_includeCategories = false; } QQmlTestMessageHandler::~QQmlTestMessageHandler() diff --git a/tests/auto/shared/util.h b/tests/auto/shared/util.h index 47a4aae231..33d7cbd1d0 100644 --- a/tests/auto/shared/util.h +++ b/tests/auto/shared/util.h @@ -87,12 +87,15 @@ public: void clear() { m_messages.clear(); } + void setIncludeCategoriesEnabled(bool enabled) { m_includeCategories = enabled; } + private: - static void messageHandler(QtMsgType, const QMessageLogContext &, const QString &message); + static void messageHandler(QtMsgType, const QMessageLogContext &context, const QString &message); static QQmlTestMessageHandler *m_instance; QStringList m_messages; QtMessageHandler m_oldHandler; + bool m_includeCategories; }; #endif // QQMLTESTUTILS_H -- cgit v1.2.3 From 6e687e85719cf6e8cad218b0307749f7abd049a6 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 8 Jul 2016 16:40:28 +0200 Subject: V4: Handle QTime->DateObject conversion better By specification, date conversion functions for dates before the epoch are not DST corrected. We converted QTime to a QDateTime where we set the date part to Jan. 1, 1970, and then convert that to msecs since the epoch UTC. For places on Earth where they had DST on that day (e.g. Hobart in Australia), strange things happen: conversion from a QTime to DateObject will use DST (because it's after the epoch in local time), but conversions from DateObject to QTime won't use the DST because it's before the epoch (in UTC). Now as everyone knows, a 24-hour clock time has no meaning without a date, only "elapsed time" has. But users still expect to be able to pass QTime to QML/JS. So, we do the conversion on day 0 of month 0 of year 0, and all of it in local time. This gives a stable conversion in both directions, and the values in both C++ and QML/JS are the same for any timezone (with or without DST) on this planet. Task-number: QTBUG-54378 Change-Id: I892e16a93f015e92d311c6cae3ae7768b7373f6a Reviewed-by: Edward Welbourne --- src/qml/doc/src/cppintegration/data.qdoc | 12 +++ src/qml/jsruntime/qv4dateobject.cpp | 22 ++++++ src/qml/jsruntime/qv4dateobject_p.h | 2 + src/qml/jsruntime/qv4engine.cpp | 9 ++- src/qml/jsruntime/qv4engine_p.h | 1 + tests/auto/qml/qqmlqt/data/timeRoundtrip.qml | 8 ++ tests/auto/qml/qqmlqt/tst_qqmlqt.cpp | 112 ++++++++++++++++++++++++++- 7 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 tests/auto/qml/qqmlqt/data/timeRoundtrip.qml diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index 7bb4d701e2..cc2fe90483 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -249,6 +249,18 @@ parameter, the value can be created as a JavaScript \c Date object in QML, and is automatically converted to a QDateTime value when it is passed to C++. +\section2 QTime to JavaScript Date + +The QML engine provides automatic type conversion from QTime values to +JavaScript \c Date objects. The date component of the resulting Date +object should not be relied upon, as it is operating system dependent. +Specifically, the year (and month and day) are set to zero. Conversion +from a JavaScript \c Date object to QTime is done by converting to a +QDateTime, and then relying on QVariant to convert it to a QTime. The end +effect is that the date part of the \c Date object is ignored, but the +local timezone will be used ignoring any DST complications it may have. + + \section2 Sequence Type to JavaScript Array Certain C++ sequence types are supported transparently in QML as JavaScript diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index df648ba9ee..04358fe3b5 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -639,6 +639,28 @@ Heap::DateObject::DateObject(const QDateTime &date) this->date = date.isValid() ? date.toMSecsSinceEpoch() : qt_qnan(); } +Heap::DateObject::DateObject(const QTime &time) +{ + if (!time.isValid()) { + date = qt_qnan(); + return; + } + + /* All programmers know that stuff starts at 0. Whatever that may mean in this context (and + * local timezone), it's before the epoch, so there is defenitely no DST problem. Specifically: + * you can't start with a date before the epoch, add some[*] hours, and end up with a date + * after. That's a problem for timezones where new year happens during DST, like + * Australia/Hobart, because we have to ignore DST before the epoch (but honor it after the + * epoch). + * + * [*] Well, when "some" is in the range 0-24. If you add something like 1M then this might + * still happen. + */ + static const double d = MakeDay(0, 0, 0); + double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec()); + date = TimeClip(UTC(MakeDate(d, t))); +} + QDateTime DateObject::toQDateTime() const { return ToDateTime(date(), Qt::LocalTime); diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 13e9e04040..c67acdcfa2 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -74,6 +74,8 @@ struct DateObject : Object { } DateObject(const QDateTime &date); double date; + + DateObject(const QTime &time); }; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 20294700f6..27397fe3d8 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -621,6 +621,13 @@ Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt) return object->d(); } +Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) +{ + Scope scope(this); + Scoped object(scope, memoryManager->allocObject(t)); + return object->d(); +} + Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { bool global = (flags & IR::RegExp::RegExp_Global); @@ -1291,7 +1298,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) case QMetaType::QDate: return QV4::Encode(newDateObject(QDateTime(*reinterpret_cast(ptr)))); case QMetaType::QTime: - return QV4::Encode(newDateObject(QDateTime(QDate(1970,1,1), *reinterpret_cast(ptr)))); + return QV4::Encode(newDateObjectFromTime(*reinterpret_cast(ptr))); case QMetaType::QRegExp: return QV4::Encode(newRegExpObject(*reinterpret_cast(ptr))); case QMetaType::QObjectStar: diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index f42f727295..843a6f4d94 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -419,6 +419,7 @@ public: Heap::DateObject *newDateObject(const Value &value); Heap::DateObject *newDateObject(const QDateTime &dt); + Heap::DateObject *newDateObjectFromTime(const QTime &t); Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags); Heap::RegExpObject *newRegExpObject(RegExp *re, bool global); diff --git a/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml new file mode 100644 index 0000000000..9d73640c87 --- /dev/null +++ b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +QtObject { + Component.onCompleted: { + var t = tp.time; + tp.time = t; + } +} diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index 69791085c5..0576650d01 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -46,6 +46,15 @@ #include #include "../../shared/util.h" +// Copied from tst_qdatetime.cpp +#ifdef Q_OS_WIN +# include +# include +# if defined(Q_OS_WINRT) +# define tzset() +# endif +#endif + class tst_qqmlqt : public QQmlDataTest { Q_OBJECT @@ -93,6 +102,9 @@ private slots: void later(); void qtObjectContents(); + void timeRoundtrip_data(); + void timeRoundtrip(); + private: QQmlEngine engine; }; @@ -873,8 +885,6 @@ void tst_qqmlqt::dateTimeFormattingVariants_data() QTime time(11, 16, 39, 755); temporary = QDateTime(QDate(1970,1,1), time); - QTest::newRow("formatDate, qtime") << "formatDate" << QVariant::fromValue(time) << (QStringList() << temporary.date().toString(Qt::DefaultLocaleShortDate) << temporary.date().toString(Qt::DefaultLocaleLongDate) << temporary.date().toString("ddd MMMM d yy")); - QTest::newRow("formatDateTime, qtime") << "formatDateTime" << QVariant::fromValue(time) << (QStringList() << temporary.toString(Qt::DefaultLocaleShortDate) << temporary.toString(Qt::DefaultLocaleLongDate) << temporary.toString("M/d/yy H:m:s a")); QTest::newRow("formatTime, qtime") << "formatTime" << QVariant::fromValue(time) << (QStringList() << temporary.time().toString(Qt::DefaultLocaleShortDate) << temporary.time().toString(Qt::DefaultLocaleLongDate) << temporary.time().toString("H:m:s a") << temporary.time().toString("hh:mm:ss.zzz")); QDate date(2011,5,31); @@ -1154,6 +1164,104 @@ void tst_qqmlqt::qtObjectContents() delete object; } +class TimeProvider: public QObject +{ + Q_OBJECT + Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged) + +public: + TimeProvider(const QTime &t) + : m_getTime(t) + {} + + QTime time() const { return m_getTime; } + void setTime(const QTime &t) { m_putTime = t; emit timeChanged(); } + +signals: + void timeChanged(); + +public: + QTime m_getTime, m_putTime; +}; + +class TimeZoneSwitch +{ +public: + TimeZoneSwitch(const char *newZone) + : doChangeZone(qstrcmp(newZone, "localtime") == 0) + { + if (!doChangeZone) + return; + + hadOldZone = qEnvironmentVariableIsSet("TZ"); + if (hadOldZone) { + oldZone = qgetenv("TZ"); + } + qputenv("TZ", newZone); + tzset(); + } + + ~TimeZoneSwitch() + { + if (!doChangeZone) + return; + + if (hadOldZone) + qputenv("TZ", oldZone); + else + qunsetenv("TZ"); + tzset(); + } + +private: + bool doChangeZone; + bool hadOldZone; + QByteArray oldZone; +}; + +void tst_qqmlqt::timeRoundtrip_data() +{ + QTest::addColumn("time"); + + // Local timezone: + QTest::newRow("localtime") << QTime(0, 0, 0); + + // No DST: + QTest::newRow("UTC") << QTime(0, 0, 0); + QTest::newRow("Europe/Amsterdam") << QTime(1, 0, 0); + QTest::newRow("Asia/Jakarta") << QTime(7, 0, 0); + + // DST: + QTest::newRow("Namibia/Windhoek") << QTime(1, 0, 0); + QTest::newRow("Australia/Adelaide") << QTime(10, 0, 0); + QTest::newRow("Australia/Hobart") << QTime(10, 0, 0); + QTest::newRow("Pacific/Auckland") << QTime(12, 0, 0); + QTest::newRow("Pacific/Samoa") << QTime(13, 0, 0); +} + +void tst_qqmlqt::timeRoundtrip() +{ +#ifdef Q_OS_WIN + QSKIP("On Windows, the DateObject doesn't handle DST transitions correctly when the timezone is not localtime."); // I.e.: for this test. +#endif + + TimeZoneSwitch tzs(QTest::currentDataTag()); + QFETCH(QTime, time); + + TimeProvider tp(time); + + QQmlEngine eng; + eng.rootContext()->setContextProperty(QLatin1String("tp"), &tp); + QQmlComponent component(&eng, testFileUrl("timeRoundtrip.qml")); + QObject *obj = component.create(); + QVERIFY(obj != 0); + + // QML reads m_getTime and saves the result as m_putTime; this should come out the same, without + // any perturbation (e.g. by DST effects) from converting from QTime to V4's Date and back + // again. + QCOMPARE(tp.m_getTime, tp.m_putTime); +} + QTEST_MAIN(tst_qqmlqt) #include "tst_qqmlqt.moc" -- cgit v1.2.3 From 580f2872f09cf7ad83ec9ae5dca686683a3cac80 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 9 Aug 2016 15:14:55 +0200 Subject: TextInput: fix horizontal alignment when implicit resizing is disabled By default, TextInput updates its implicit width to match the content width. This implies that when TextInput does not have an explicit width set, there is no need to handle horizontal alignment. TextField wants to override the default implicit width, so it disables the "implicit resizing" feature of TextInput. In this scenario, the implicit width does not match the content width. Therefore the text layouting code should also treat the item as if it had an explicit width set to achieve the correct horizontal alignment. Task-number: QTBUG-55138 Change-Id: I8c04971a6aff44c6f1734df50153a9788849e98a Reviewed-by: Mitch Curtis --- src/quick/items/qquicktextinput.cpp | 2 +- .../quick/qquicktextinput/tst_qquicktextinput.cpp | 35 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 03c6b892c4..63e8505ffa 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -2916,7 +2916,7 @@ void QQuickTextInputPrivate::updateLayout() if (inLayout) // probably the result of a binding loop, but by letting it return; // get this far we'll get a warning to that effect. } - qreal lineWidth = q->widthValid() ? q->width() - q->leftPadding() - q->rightPadding() : INT_MAX; + qreal lineWidth = q->widthValid() || !isImplicitResizeEnabled() ? q->width() - q->leftPadding() - q->rightPadding() : INT_MAX; qreal height = 0; qreal width = 0; do { diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 18ccd81633..ea88f9dadb 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -200,6 +200,8 @@ private slots: void implicitSize(); void implicitSizeBinding_data(); void implicitSizeBinding(); + void implicitResize_data(); + void implicitResize(); void negativeDimensions(); @@ -5967,6 +5969,39 @@ void tst_qquicktextinput::implicitSizeBinding() QCOMPARE(textObject->height(), textObject->implicitHeight()); } +void tst_qquicktextinput::implicitResize_data() +{ + QTest::addColumn("alignment"); + QTest::newRow("left") << int(Qt::AlignLeft); + QTest::newRow("center") << int(Qt::AlignHCenter); + QTest::newRow("right") << int(Qt::AlignRight); +} + +void tst_qquicktextinput::implicitResize() +{ + QFETCH(int, alignment); + + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\nTextInput { }", QUrl::fromLocalFile("")); + + QScopedPointer textInput(qobject_cast(component.create())); + QVERIFY(!textInput.isNull()); + + QScopedPointer textField(qobject_cast(component.create())); + QVERIFY(!textField.isNull()); + QQuickTextInputPrivate::get(textField.data())->setImplicitResizeEnabled(false); + + textInput->setWidth(200); + textField->setImplicitWidth(200); + + textInput->setHAlign(QQuickTextInput::HAlignment(alignment)); + textField->setHAlign(QQuickTextInput::HAlignment(alignment)); + + textInput->setText("Qt"); + textField->setText("Qt"); + + QCOMPARE(textField->positionToRectangle(0), textInput->positionToRectangle(0)); +} void tst_qquicktextinput::negativeDimensions() { -- cgit v1.2.3 From 4fcd5472233fa4afe3962207af5df4f55f1af276 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 8 Aug 2016 11:11:47 +0200 Subject: QML: Move flag from QQmlPropertyRawData fields into its flags Change-Id: I4f1aaf92fd1c67ec18ab0f80d6d0bf58b4af6a17 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache_p.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 79fd77dd45..6d3c4a8a7e 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -125,8 +125,9 @@ public: // Internal QQmlPropertyCache flags unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved + unsigned overrideIndexIsProperty: 1; - unsigned _padding : 10; // align to 32 bits + unsigned _padding : 9; // align to 32 bits inline Flags(); inline bool operator==(const Flags &other) const; @@ -201,8 +202,8 @@ public: _metaObjectOffset = qint16(off); } - bool overrideIndexIsProperty() const { return _overrideIndexIsProperty; } - void setOverrideIndexIsProperty(bool onoff) { _overrideIndexIsProperty = onoff; } + bool overrideIndexIsProperty() const { return _flags.overrideIndexIsProperty; } + void setOverrideIndexIsProperty(bool onoff) { _flags.overrideIndexIsProperty = onoff; } int overrideIndex() const { return _overrideIndex; } void setOverrideIndex(int idx) @@ -235,10 +236,7 @@ private: qint16 _revision; qint16 _metaObjectOffset; - struct { // When !IsValueTypeVirtual - uint _overrideIndexIsProperty : 1; - signed int _overrideIndex : 31; - }; + signed int _overrideIndex; // When !IsValueTypeVirtual }; struct { // When HasAccessors QQmlAccessors *_accessors; @@ -584,6 +582,7 @@ QQmlPropertyRawData::Flags::Flags() , isCloned(false) , isConstructor(false) , notFullyResolved(false) + , overrideIndexIsProperty(false) , _padding(0) {} @@ -606,7 +605,8 @@ bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &ot isOverload == other.isOverload && isCloned == other.isCloned && isConstructor == other.isConstructor && - notFullyResolved == other.notFullyResolved; + notFullyResolved == other.notFullyResolved && + overrideIndexIsProperty == other.overrideIndexIsProperty; } void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flags from) @@ -627,7 +627,6 @@ QQmlPropertyData::QQmlPropertyData() { setPropType(0); setNotifyIndex(-1); - setOverrideIndexIsProperty(false); setOverrideIndex(-1); setRevision(0); setMetaObjectOffset(-1); -- cgit v1.2.3 From fdd8bf7dbf91b8e8ce84c990a03767c0b8ba064a Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 8 Jun 2016 10:02:29 +0200 Subject: QQmlObjectCreator: initialize _bindingTarget Coverity (CID 163180) noticed _bindingTarget wasn't initialized. Change-Id: Ia727d00a161e514c437a72084b6ef01a7ebf4abc Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlobjectcreator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 0c13a7a017..c06e1827ac 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -114,6 +114,7 @@ void QQmlObjectCreator::init(QQmlContextData *providedParentContext) context = 0; _qobject = 0; _scopeObject = 0; + _bindingTarget = 0; _valueTypeProperty = 0; _compiledObject = 0; _compiledObjectIndex = -1; -- cgit v1.2.3 From b054a700576f61d9fbd196987f42fdea7a806db1 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 8 Jun 2016 10:03:37 +0200 Subject: QQmlObjectCreator: prefer initializer syntax over assignment Greater uniformity; also opens the door to potential const-ing, should this ever be worht considering. Change-Id: I91b44472cb7d84f85b3033f14a763beeea837459 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlobjectcreator.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index c06e1827ac..8fa1f48d18 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -68,12 +68,12 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile , resolvedTypes(compiledData->resolvedTypes) , propertyCaches(compiledData->propertyCaches) , vmeMetaObjectData(compiledData->metaObjects) + , sharedState(new QQmlObjectCreatorSharedState) + , topLevelCreator(true) , activeVMEDataForRootContext(activeVMEDataForRootContext) { init(parentContext); - sharedState = new QQmlObjectCreatorSharedState; - topLevelCreator = true; sharedState->componentAttached = 0; sharedState->allCreatedBindings.allocate(compiledData->totalBindingsCount); sharedState->allParserStatusCallbacks.allocate(compiledData->totalParserStatusCount); @@ -93,12 +93,11 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile , resolvedTypes(compiledData->resolvedTypes) , propertyCaches(compiledData->propertyCaches) , vmeMetaObjectData(compiledData->metaObjects) + , sharedState(inheritedSharedState) + , topLevelCreator(false) , activeVMEDataForRootContext(0) { init(parentContext); - - sharedState = inheritedSharedState; - topLevelCreator = false; } void QQmlObjectCreator::init(QQmlContextData *providedParentContext) -- cgit v1.2.3 From e0acbde6da2127d9fa75aa6c032682086295ce27 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 30 May 2016 11:15:01 +0200 Subject: Shader examples: initialize all members Coverity (CIDs 161677, 161674) caught us setting a bad example in our example code. Change-Id: I395f689586f9a6ad783328b9258096cbc9ccd692 Reviewed-by: Gunnar Sletta --- examples/quick/scenegraph/graph/linenode.cpp | 2 +- examples/quick/scenegraph/graph/noisynode.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/quick/scenegraph/graph/linenode.cpp b/examples/quick/scenegraph/graph/linenode.cpp index 473be7da17..992e2d44c9 100644 --- a/examples/quick/scenegraph/graph/linenode.cpp +++ b/examples/quick/scenegraph/graph/linenode.cpp @@ -56,7 +56,7 @@ class LineShader : public QSGSimpleMaterialShader QSG_DECLARE_SIMPLE_SHADER(LineShader, LineMaterial) public: - LineShader() { + LineShader() : id_color(-1), id_spread(-1), id_size(-1) { setShaderSourceFile(QOpenGLShader::Vertex, ":/scenegraph/graph/shaders/line.vsh"); setShaderSourceFile(QOpenGLShader::Fragment, ":/scenegraph/graph/shaders/line.fsh"); } diff --git a/examples/quick/scenegraph/graph/noisynode.cpp b/examples/quick/scenegraph/graph/noisynode.cpp index bc273cf632..834151599b 100644 --- a/examples/quick/scenegraph/graph/noisynode.cpp +++ b/examples/quick/scenegraph/graph/noisynode.cpp @@ -61,7 +61,7 @@ class NoisyShader : public QSGSimpleMaterialShader QSG_DECLARE_SIMPLE_SHADER(NoisyShader, NoisyMaterial) public: - NoisyShader() { + NoisyShader() : id_color(-1), id_texture(-1), id_textureSize(-1) { setShaderSourceFile(QOpenGLShader::Vertex, ":/scenegraph/graph/shaders/noisy.vsh"); setShaderSourceFile(QOpenGLShader::Fragment, ":/scenegraph/graph/shaders/noisy.fsh"); } -- cgit v1.2.3 From 3b3959198c05b4ec35671b110902a07a05a802f2 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 11 Aug 2016 12:24:48 +0300 Subject: SplineEditor: replace QList with QVector There are only appending operations, and QList is optimized for types with sizeof(void*), so storing qreal or QPointF in QList is inefficient. Change-Id: Ic2f216fa81549bfb8790c6ff58dd6053c776c297 Reviewed-by: Simon Hausmann --- tools/qmleasing/splineeditor.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp index 78ed9606db..ab22d579f8 100644 --- a/tools/qmleasing/splineeditor.cpp +++ b/tools/qmleasing/splineeditor.cpp @@ -34,6 +34,7 @@ #include #include #include +#include const int canvasWidth = 640; const int canvasHeight = 320; @@ -677,7 +678,7 @@ void SplineEditor::setEasingCurve(const QString &code) cleanCode.chop(1); const QStringList stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts); if (stringList.count() >= 6 && (stringList.count() % 6 == 0)) { - QList realList; + QVector realList; realList.reserve(stringList.count()); foreach (const QString &string, stringList) { bool ok; @@ -685,12 +686,12 @@ void SplineEditor::setEasingCurve(const QString &code) if (!ok) return; } - QList points; + QVector points; const int count = realList.count() / 2; points.reserve(count); for (int i = 0; i < count; ++i) points.append(QPointF(realList.at(i * 2), realList.at(i * 2 + 1))); - if (points.last() == QPointF(1.0, 1.0)) { + if (points.constLast() == QPointF(1.0, 1.0)) { QEasingCurve easingCurve(QEasingCurve::BezierSpline); for (int i = 0; i < points.count() / 3; ++i) { -- cgit v1.2.3 From 125c4e2fd1cd1bc2d85f445d9a3feb2de98489e4 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 9 Aug 2016 15:21:58 +0300 Subject: QQmlXMLHttpRequest: optimize header() Early out to avoid string conversion. Change-Id: I533a5ec671087d887184b4e92d79f5ab9cc1e013 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlxmlhttprequest.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 0310d42fa4..85e17525a5 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1146,11 +1146,12 @@ void QQmlXMLHttpRequest::addHeader(const QString &name, const QString &value) QString QQmlXMLHttpRequest::header(const QString &name) const { - QByteArray utfname = name.toLower().toUtf8(); - - for (const HeaderPair &header : m_headersList) { - if (header.first == utfname) - return QString::fromUtf8(header.second); + if (!m_headersList.isEmpty()) { + const QByteArray utfname = name.toLower().toUtf8(); + for (const HeaderPair &header : m_headersList) { + if (header.first == utfname) + return QString::fromUtf8(header.second); + } } return QString(); } -- cgit v1.2.3 From d39d23b9f2aab71c942954e68d92abfd19018e96 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 11 Aug 2016 17:28:55 +0300 Subject: QmlTest: replace 'foreach' with 'range for' And add QT_NO_FOREACH define to .pro file. Now QmlTest is 'foreach' free. Change-Id: I17d8563cafd34bb90fe455ae955da5c56fbd2899 Reviewed-by: Shawn Rutledge --- src/qmltest/qmltest.pro | 2 +- src/qmltest/quicktest.cpp | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro index ebbcbcd1eb..9852861334 100644 --- a/src/qmltest/qmltest.pro +++ b/src/qmltest/qmltest.pro @@ -1,6 +1,6 @@ TARGET = QtQuickTest -DEFINES += QT_NO_URL_CAST_FROM_STRING +DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_FOREACH QT = core testlib-private QT_PRIVATE = quick qml-private gui core-private diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index bc26a19033..0e348eee11 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -144,7 +144,7 @@ void handleCompileErrors(const QFileInfo &fi, QQuickView *view) QTextStream str(&message); str << "\n " << QDir::toNativeSeparators(fi.absoluteFilePath()) << " produced " << errors.size() << " error(s):\n"; - foreach (const QQmlError &e, errors) { + for (const QQmlError &e : errors) { str << " "; if (e.url().isLocalFile()) { str << QDir::toNativeSeparators(e.url().toLocalFile()); @@ -158,11 +158,12 @@ void handleCompileErrors(const QFileInfo &fi, QQuickView *view) str << " Working directory: " << QDir::toNativeSeparators(QDir::current().absolutePath()) << '\n'; if (QQmlEngine *engine = view->engine()) { str << " View: " << view->metaObject()->className() << ", import paths:\n"; - foreach (const QString &i, engine->importPathList()) + const auto importPaths = engine->importPathList(); + for (const QString &i : importPaths) str << " '" << QDir::toNativeSeparators(i) << "'\n"; const QStringList pluginPaths = engine->pluginPathList(); str << " Plugin paths:\n"; - foreach (const QString &p, pluginPaths) + for (const QString &p : pluginPaths) str << " '" << QDir::toNativeSeparators(p) << "'\n"; } qWarning("%s", qPrintable(message)); @@ -338,11 +339,11 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD &eventLoop, SLOT(quit())); view->rootContext()->setContextProperty (QLatin1String("qtest"), QTestRootObject::instance()); // Deprecated. Use QTestRootObject from Qt.test.qtestroot instead - foreach (const QString &path, imports) + for (const QString &path : qAsConst(imports)) view->engine()->addImportPath(path); - foreach (const QString &path, pluginPaths) + for (const QString &path : qAsConst(pluginPaths)) view->engine()->addPluginPath(path); - foreach (const QString &file, files) { + for (const QString &file : qAsConst(files)) { const QFileInfo fi(file); if (!fi.exists()) continue; -- cgit v1.2.3 From b4327851a4895b28b2c60ca51e77eca3ee0a1b4e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 12 Aug 2016 08:02:37 +0200 Subject: Move the moth stack slot allocator into qmldevtools This way it's accessible to the QML compiler. Change-Id: I3918a796c698fc75e134b29a61eed2ec028bc851 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4isel_moth.cpp | 163 +------------------------------------- src/qml/compiler/qv4ssa_p.h | 163 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 162 deletions(-) diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index afb36c5f14..f87cbe3f1b 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -143,167 +143,6 @@ inline bool isBoolType(IR::Expr *e) return (e->type == IR::BoolType); } -/* - * stack slot allocation: - * - * foreach bb do - * foreach stmt do - * if the current statement is not a phi-node: - * purge ranges that end before the current statement - * check for life ranges to activate, and if they don't have a stackslot associated then allocate one - * renumber temps to stack - * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated - * if it's a jump: - * foreach phi node in the successor: - * allocate slots for each temp (both sources and targets) if they don't have one allocated already - * insert moves before the jump - */ -class AllocateStackSlots: protected ConvertTemps -{ - IR::LifeTimeIntervals::Ptr _intervals; - QVector _unhandled; - QVector _live; - QBitArray _slotIsInUse; - IR::Function *_function; - - int defPosition(IR::Stmt *s) const - { - return usePosition(s) + 1; - } - - int usePosition(IR::Stmt *s) const - { - return _intervals->positionForStatement(s); - } - -public: - AllocateStackSlots(const IR::LifeTimeIntervals::Ptr &intervals) - : _intervals(intervals) - , _slotIsInUse(intervals->size(), false) - , _function(0) - { - _live.reserve(8); - _unhandled = _intervals->intervals(); - } - - void forFunction(IR::Function *function) - { - IR::Optimizer::showMeTheCode(function, "Before stack slot allocation"); - _function = function; - toStackSlots(function); - } - -protected: - virtual int allocateFreeSlot() - { - for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) { - if (!_slotIsInUse[i]) { - if (_nextUnusedStackSlot <= i) { - Q_ASSERT(_nextUnusedStackSlot == i); - _nextUnusedStackSlot = i + 1; - } - _slotIsInUse[i] = true; - return i; - } - } - - Q_UNREACHABLE(); - return -1; - } - - virtual void process(IR::Stmt *s) - { -// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id); - - if (IR::Phi *phi = s->asPhi()) { - visitPhi(phi); - } else { - // purge ranges no longer alive: - for (int i = 0; i < _live.size(); ) { - const IR::LifeTimeInterval *lti = _live.at(i); - if (lti->end() < usePosition(s)) { -// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index]; - _live.remove(i); - Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]); - _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false; - continue; - } else { - ++i; - } - } - - // active new ranges: - while (!_unhandled.isEmpty()) { - IR::LifeTimeInterval *lti = _unhandled.last(); - if (lti->start() > defPosition(s)) - break; // we're done - Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index)); - _stackSlotForTemp[lti->temp().index] = allocateFreeSlot(); -// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index]; - _live.append(lti); - _unhandled.removeLast(); - } - - s->accept(this); - } - - if (IR::Jump *jump = s->asJump()) { - IR::MoveMapping moves; - foreach (IR::Stmt *succStmt, jump->target->statements()) { - if (IR::Phi *phi = succStmt->asPhi()) { - forceActivation(*phi->targetTemp); - for (int i = 0, ei = phi->d->incoming.size(); i != ei; ++i) { - IR::Expr *e = phi->d->incoming[i]; - if (IR::Temp *t = e->asTemp()) { - forceActivation(*t); - } - if (jump->target->in[i] == _currentBasicBlock) - moves.add(phi->d->incoming[i], phi->targetTemp); - } - } else { - break; - } - } - moves.order(); - QList newMoves = moves.insertMoves(_currentBasicBlock, _function, true); - foreach (IR::Move *move, newMoves) - move->accept(this); - } - } - - void forceActivation(const IR::Temp &t) - { - if (_stackSlotForTemp.contains(t.index)) - return; - - int i = _unhandled.size() - 1; - for (; i >= 0; --i) { - IR::LifeTimeInterval *lti = _unhandled[i]; - if (lti->temp() == t) { - _live.append(lti); - _unhandled.remove(i); - break; - } - } - Q_ASSERT(i >= 0); // check that we always found the entry - - _stackSlotForTemp[t.index] = allocateFreeSlot(); -// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index]; - } - - virtual void visitPhi(IR::Phi *phi) - { - Q_UNUSED(phi); -#if !defined(QT_NO_DEBUG) - Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index)); - Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]); - foreach (IR::Expr *e, phi->d->incoming) { - if (IR::Temp *t = e->asTemp()) - Q_ASSERT(_stackSlotForTemp.contains(t->index)); - } -#endif // defined(QT_NO_DEBUG) - } -}; } // anonymous namespace InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) @@ -353,7 +192,7 @@ void InstructionSelection::run(int functionIndex) qEnvironmentVariableIsEmpty("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION"); if (doStackSlotAllocation) { - AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function); + IR::AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function); } else { opt.convertOutOfSSA(); ConvertTemps().toStackSlots(_function); diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index d06774e803..c5690a8581 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -46,6 +46,7 @@ // #include "qv4jsir_p.h" +#include "qv4isel_util_p.h" #include QT_BEGIN_NAMESPACE @@ -271,6 +272,168 @@ private: QList &swaps) const; }; +/* + * stack slot allocation: + * + * foreach bb do + * foreach stmt do + * if the current statement is not a phi-node: + * purge ranges that end before the current statement + * check for life ranges to activate, and if they don't have a stackslot associated then allocate one + * renumber temps to stack + * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated + * if it's a jump: + * foreach phi node in the successor: + * allocate slots for each temp (both sources and targets) if they don't have one allocated already + * insert moves before the jump + */ +class AllocateStackSlots: protected ConvertTemps +{ + IR::LifeTimeIntervals::Ptr _intervals; + QVector _unhandled; + QVector _live; + QBitArray _slotIsInUse; + IR::Function *_function; + + int defPosition(IR::Stmt *s) const + { + return usePosition(s) + 1; + } + + int usePosition(IR::Stmt *s) const + { + return _intervals->positionForStatement(s); + } + +public: + AllocateStackSlots(const IR::LifeTimeIntervals::Ptr &intervals) + : _intervals(intervals) + , _slotIsInUse(intervals->size(), false) + , _function(0) + { + _live.reserve(8); + _unhandled = _intervals->intervals(); + } + + void forFunction(IR::Function *function) + { + IR::Optimizer::showMeTheCode(function, "Before stack slot allocation"); + _function = function; + toStackSlots(function); + } + +protected: + virtual int allocateFreeSlot() + { + for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) { + if (!_slotIsInUse[i]) { + if (_nextUnusedStackSlot <= i) { + Q_ASSERT(_nextUnusedStackSlot == i); + _nextUnusedStackSlot = i + 1; + } + _slotIsInUse[i] = true; + return i; + } + } + + Q_UNREACHABLE(); + return -1; + } + + virtual void process(IR::Stmt *s) + { +// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id); + + if (IR::Phi *phi = s->asPhi()) { + visitPhi(phi); + } else { + // purge ranges no longer alive: + for (int i = 0; i < _live.size(); ) { + const IR::LifeTimeInterval *lti = _live.at(i); + if (lti->end() < usePosition(s)) { +// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index]; + _live.remove(i); + Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]); + _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false; + continue; + } else { + ++i; + } + } + + // active new ranges: + while (!_unhandled.isEmpty()) { + IR::LifeTimeInterval *lti = _unhandled.last(); + if (lti->start() > defPosition(s)) + break; // we're done + Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index)); + _stackSlotForTemp[lti->temp().index] = allocateFreeSlot(); +// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index]; + _live.append(lti); + _unhandled.removeLast(); + } + + s->accept(this); + } + + if (IR::Jump *jump = s->asJump()) { + IR::MoveMapping moves; + foreach (IR::Stmt *succStmt, jump->target->statements()) { + if (IR::Phi *phi = succStmt->asPhi()) { + forceActivation(*phi->targetTemp); + for (int i = 0, ei = phi->d->incoming.size(); i != ei; ++i) { + IR::Expr *e = phi->d->incoming[i]; + if (IR::Temp *t = e->asTemp()) { + forceActivation(*t); + } + if (jump->target->in[i] == _currentBasicBlock) + moves.add(phi->d->incoming[i], phi->targetTemp); + } + } else { + break; + } + } + moves.order(); + QList newMoves = moves.insertMoves(_currentBasicBlock, _function, true); + foreach (IR::Move *move, newMoves) + move->accept(this); + } + } + + void forceActivation(const IR::Temp &t) + { + if (_stackSlotForTemp.contains(t.index)) + return; + + int i = _unhandled.size() - 1; + for (; i >= 0; --i) { + IR::LifeTimeInterval *lti = _unhandled[i]; + if (lti->temp() == t) { + _live.append(lti); + _unhandled.remove(i); + break; + } + } + Q_ASSERT(i >= 0); // check that we always found the entry + + _stackSlotForTemp[t.index] = allocateFreeSlot(); +// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index]; + } + + virtual void visitPhi(IR::Phi *phi) + { + Q_UNUSED(phi); +#if !defined(QT_NO_DEBUG) + Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index)); + Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]); + foreach (IR::Expr *e, phi->d->incoming) { + if (IR::Temp *t = e->asTemp()) + Q_ASSERT(_stackSlotForTemp.contains(t->index)); + } +#endif // defined(QT_NO_DEBUG) + } +}; + } // IR namespace } // QV4 namespace -- cgit v1.2.3 From 4a7e03fc700cb7431df20a31bed6834a31e6833e Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 11 Aug 2016 17:34:59 +0300 Subject: QuickWidgets: replace 'foreach' with 'range for' And add QT_NO_FOREACH define to .pro file. Now QuickWidgets is 'foreach' free. Change-Id: Iff711ab3a8bb78188aac48371f6e45eb4b174699 Reviewed-by: Shawn Rutledge --- src/quickwidgets/qquickwidget.cpp | 12 ++++++------ src/quickwidgets/quickwidgets.pro | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index ad04c76783..b8c5b900c9 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -575,8 +575,8 @@ void QQuickWidget::setContent(const QUrl& url, QQmlComponent *component, QObject d->component = component; if (d->component && d->component->isError()) { - QList errorList = d->component->errors(); - foreach (const QQmlError &error, errorList) { + const QList errorList = d->component->errors(); + for (const QQmlError &error : errorList) { QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning() << error; } @@ -1018,8 +1018,8 @@ void QQuickWidget::continueExecute() disconnect(d->component, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(continueExecute())); if (d->component->isError()) { - QList errorList = d->component->errors(); - foreach (const QQmlError &error, errorList) { + const QList errorList = d->component->errors(); + for (const QQmlError &error : errorList) { QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning() << error; } @@ -1030,8 +1030,8 @@ void QQuickWidget::continueExecute() QObject *obj = d->component->create(); if (d->component->isError()) { - QList errorList = d->component->errors(); - foreach (const QQmlError &error, errorList) { + const QList errorList = d->component->errors(); + for (const QQmlError &error : errorList) { QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning() << error; } diff --git a/src/quickwidgets/quickwidgets.pro b/src/quickwidgets/quickwidgets.pro index 87409e31c5..2438e577ae 100644 --- a/src/quickwidgets/quickwidgets.pro +++ b/src/quickwidgets/quickwidgets.pro @@ -2,7 +2,7 @@ TARGET = QtQuickWidgets QT = core-private gui-private qml-private quick-private widgets-private -DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES +DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FOREACH HEADERS += \ qquickwidget.h \ -- cgit v1.2.3 From 829c8faee2bd3eed6b62325290f16738e2b5cf5e Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Wed, 8 Jun 2016 15:12:08 +0300 Subject: tools: use QStringRef more Change-Id: I4fccbfb2b965daf3a31846d1d51d39eb74ad944d Reviewed-by: Shawn Rutledge Reviewed-by: Simon Hausmann --- tools/qmleasing/splineeditor.cpp | 8 +++----- tools/qmlimportscanner/main.cpp | 6 +++--- tools/qmlprofiler/qmlprofilerapplication.cpp | 6 +++--- tools/qmlprofiler/qmlprofilerdata.cpp | 6 +++--- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/tools/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp index ab22d579f8..ee55931a46 100644 --- a/tools/qmleasing/splineeditor.cpp +++ b/tools/qmleasing/splineeditor.cpp @@ -673,14 +673,12 @@ void SplineEditor::setEasingCurve(const QString &code) if (m_block) return; if (code.startsWith(QLatin1Char('[')) && code.endsWith(QLatin1Char(']'))) { - QString cleanCode = code; - cleanCode.remove(0, 1); - cleanCode.chop(1); - const QStringList stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts); + const QStringRef cleanCode(&code, 1, code.size() - 2); + const auto stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts); if (stringList.count() >= 6 && (stringList.count() % 6 == 0)) { QVector realList; realList.reserve(stringList.count()); - foreach (const QString &string, stringList) { + for (const QStringRef &string : stringList) { bool ok; realList.append(string.toDouble(&ok)); if (!ok) diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 2569d78c63..2371057878 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -224,11 +224,11 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) if (plugininfo.contains(QStringLiteral("dependencies"))) { QStringList dependencies = plugininfo.value(QStringLiteral("dependencies")).toStringList(); foreach (const QString &line, dependencies) { - QList dep = line.split(QLatin1Char(' ')); + const auto dep = line.splitRef(QLatin1Char(' ')); QVariantMap depImport; depImport[QStringLiteral("type")] = QStringLiteral("module"); - depImport[QStringLiteral("name")] = dep[0]; - depImport[QStringLiteral("version")] = dep[1]; + depImport[QStringLiteral("name")] = dep[0].toString(); + depImport[QStringLiteral("version")] = dep[1].toString(); importsCopy.append(depImport); } } diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp index 063e5e2961..45c32487a2 100644 --- a/tools/qmlprofiler/qmlprofilerapplication.cpp +++ b/tools/qmlprofiler/qmlprofilerapplication.cpp @@ -347,7 +347,7 @@ bool QmlProfilerApplication::checkOutputFile(PendingRequest pending) void QmlProfilerApplication::userCommand(const QString &command) { - QStringList args = command.split(QChar::Space, QString::SkipEmptyParts); + auto args = command.splitRef(QChar::Space, QString::SkipEmptyParts); if (args.isEmpty()) { prompt(); return; @@ -401,7 +401,7 @@ void QmlProfilerApplication::userCommand(const QString &command) } else if (m_profilerData.isEmpty()) { prompt(tr("No data was recorded so far.")); } else { - m_interactiveOutputFile = args.length() > 0 ? args[0] : m_outputFile; + m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile; if (checkOutputFile(REQUEST_OUTPUT_FILE)) output(); } @@ -418,7 +418,7 @@ void QmlProfilerApplication::userCommand(const QString &command) if (!m_recording && m_profilerData.isEmpty()) { prompt(tr("No data was recorded so far.")); } else { - m_interactiveOutputFile = args.length() > 0 ? args[0] : m_outputFile; + m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile; if (checkOutputFile(REQUEST_FLUSH_FILE)) flush(); } diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp index 74fa44c1d6..b651b2724f 100644 --- a/tools/qmlprofiler/qmlprofilerdata.cpp +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -248,7 +248,7 @@ void QmlProfilerData::addQmlEvent(QQmlProfilerDefinitions::RangeType type, eventHashStr = getHashStringForQmlEvent(eventLocation, type); } else { const QString filePath = QUrl(eventLocation.filename).path(); - displayName = filePath.mid( + displayName = filePath.midRef( filePath.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') + QString::number(eventLocation.line); eventHashStr = getHashStringForQmlEvent(eventLocation, type); @@ -327,8 +327,8 @@ void QmlProfilerData::addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventTy QString filePath = QUrl(location).path(); - QString eventHashStr = filePath.mid(filePath.lastIndexOf(QLatin1Char('/')) + 1) + - QStringLiteral(":") + QString::number(type); + const QString eventHashStr = filePath.midRef(filePath.lastIndexOf(QLatin1Char('/')) + 1) + + QLatin1Char(':') + QString::number(type); QmlRangeEventData *newEvent; if (d->eventDescriptions.contains(eventHashStr)) { newEvent = d->eventDescriptions[eventHashStr]; -- cgit v1.2.3 From 87c23b33b7293a43a851a6e43a0ddc6ef44c17e4 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Thu, 11 Aug 2016 10:53:58 +0200 Subject: Fix 'lighter' compositionmode in Context2D 'lighter' is defined as Source + Destination which is the equivalent of QPainter::CompositionMode_Plus. Change-Id: I88857ffbabe319e955f588b6d74bc96da8714f3d Reviewed-by: Laszlo Agocs --- src/quick/items/context2d/qquickcontext2d.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index b924701f2b..c26f641754 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -794,7 +794,7 @@ static QPainter::CompositionMode qt_composite_mode_from_string(const QString &co } else if (compositeOperator == QLatin1String("destination-over")) { return QPainter::CompositionMode_DestinationOver; } else if (compositeOperator == QLatin1String("lighter")) { - return QPainter::CompositionMode_Lighten; + return QPainter::CompositionMode_Plus; } else if (compositeOperator == QLatin1String("copy")) { return QPainter::CompositionMode_Source; } else if (compositeOperator == QLatin1String("xor")) { @@ -857,7 +857,7 @@ static QString qt_composite_mode_to_string(QPainter::CompositionMode op) case QPainter::CompositionMode_Xor: return QStringLiteral("xor"); case QPainter::CompositionMode_Plus: - return QStringLiteral("plus"); + return QStringLiteral("lighter"); case QPainter::CompositionMode_Multiply: return QStringLiteral("qt-multiply"); case QPainter::CompositionMode_Screen: -- cgit v1.2.3 From 273e5b0bdc1239df77e2e5694fdd5ad068595be3 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 24 May 2016 12:21:39 +0300 Subject: PathView: fix item creation First call of QQuickPathView::refill() did not use currentIndex for item prepending and there was situation when items were not created, e.g.: PathView with current item in center and currentIndex was set so that item with index 0 was after current item and before path end. The result of this situation: items from path begin to current item were not created. The reason was that idx always equaled (modelCount-1) for item prepending. Now first filling uses currentIndex to calculate valid idx. Task-number: QTBUG-53464 Change-Id: I7e343b0712c9c5c5cd56b1d8e020cf8c0f6e6301 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickpathview.cpp | 16 ++++- .../auto/quick/qquickpathview/data/qtbug53464.qml | 77 ++++++++++++++++++++++ .../quick/qquickpathview/tst_qquickpathview.cpp | 24 +++++++ 3 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 tests/auto/quick/qquickpathview/data/qtbug53464.qml diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 7e1fa95692..1dbdd10303 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1854,6 +1854,14 @@ void QQuickPathView::updatePolish() refill(); } +static inline int currentIndexRemainder(int currentIndex, int modelCount) Q_DECL_NOTHROW +{ + if (currentIndex < 0) + return modelCount + currentIndex % modelCount; + else + return currentIndex % modelCount; +} + void QQuickPathView::componentComplete() { Q_D(QQuickPathView); @@ -1865,7 +1873,7 @@ void QQuickPathView::componentComplete() if (d->model) { d->modelCount = d->model->count(); if (d->modelCount && d->currentIndex != 0) // an initial value has been provided for currentIndex - d->offset = qmlMod(d->modelCount - d->currentIndex, d->modelCount); + d->offset = qmlMod(d->modelCount - currentIndexRemainder(d->currentIndex, d->modelCount), d->modelCount); } d->createHighlight(); @@ -1939,7 +1947,8 @@ void QQuickPathView::refill() qreal endPos; int startIdx = 0; qreal startPos = 0.0; - if (d->items.count()) { + const bool wasEmpty = d->items.isEmpty(); + if (!wasEmpty) { //Find the beginning and end, items may not be in sorted order endPos = -1.0; startPos = 2.0; @@ -1998,7 +2007,8 @@ void QQuickPathView::refill() } //Prepend - idx = startIdx - 1; + idx = (wasEmpty ? d->calcCurrentIndex() : startIdx) - 1; + if (idx < 0) idx = d->modelCount - 1; nextPos = d->positionOfIndex(idx); diff --git a/tests/auto/quick/qquickpathview/data/qtbug53464.qml b/tests/auto/quick/qquickpathview/data/qtbug53464.qml new file mode 100644 index 0000000000..d30d404e68 --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/qtbug53464.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Netris +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + width: 600 + height: 400 + PathView { + objectName: "pathView" + model: 10 + anchors.fill: parent + pathItemCount: 5 + highlightRangeMode: PathView.StrictlyEnforceRange + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + currentIndex: 8 + + path: Path { + startX: 0 + startY: 50 + PathLine { + x: 600 + y: 50 + } + } + + delegate: Component { + Text { + width: 50 + height: 50 + text: index + objectName: "delegate" + index + font.pixelSize: 24 + color: PathView.isCurrentItem ? "green" : "black" + } + } + } +} + diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index fc5dd3bbca..2046391d02 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -141,6 +141,7 @@ private slots: void flickableDelegate(); void jsArrayChange(); void qtbug42716(); + void qtbug53464(); void addCustomAttribute(); }; @@ -2381,6 +2382,29 @@ void tst_QQuickPathView::qtbug42716() QVERIFY(!itemMiss); } +void tst_QQuickPathView::qtbug53464() +{ + QScopedPointer window(createView()); + + window->setSource(testFileUrl("qtbug53464.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + QQuickPathView *pathView = findItem(window->rootObject(), "pathView"); + QVERIFY(pathView != Q_NULLPTR); + const int currentIndex = pathView->currentIndex(); + QCOMPARE(currentIndex, 8); + + const int pathItemCount = pathView->pathItemCount(); + int totalCount = 0; + foreach (QQuickItem *item, pathView->childItems()) { + if (item->objectName().startsWith(QLatin1String("delegate"))) + ++totalCount; + } + QCOMPARE(pathItemCount, totalCount); +} + void tst_QQuickPathView::addCustomAttribute() { const QScopedPointer window(createView()); -- cgit v1.2.3 From 9010dd0e767c407f107aacfe6be145229e3ba793 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Fri, 12 Aug 2016 13:38:54 +0200 Subject: Fix test failure: wait between mouse events the proper way Apparently, QTest::mouseEvent() disregards the wall time for the time stamps for events, but synthesizes the timestamps instead. The proper way is to specify the waiting time in the delay argument. This will adjust both the timestamp and perform a qWait() before the event handler is called. Without this patch, the qWait(doubleClickInterval) was disregarded, which lead to that a doubleclick event was generated, and it failed with the following message: Actual (((window->rootObject()->property("doubleClicks").toInt()))): 2 Expected (1) : 1 tst_qquickmousearea.cpp(1105) : failure location Change-Id: Ieda3b670d3cda0bd522db2f9677dad5745c88a21 Reviewed-by: Shawn Rutledge --- .../quick/qquickmousearea/tst_qquickmousearea.cpp | 31 +++++++++------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 9cdfd21f9c..d51c228b7c 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -1032,17 +1032,17 @@ void tst_QQuickMouseArea::clickThrough() QVERIFY(QTest::qWaitForWindowExposed(window.data())); QVERIFY(window->rootObject() != 0); + // to avoid generating a double click. + const int doubleClickInterval = qApp->styleHints()->mouseDoubleClickInterval() + 10; + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100)); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(100,100)); QTRY_COMPARE(window->rootObject()->property("presses").toInt(), 0); QTRY_COMPARE(window->rootObject()->property("clicks").toInt(), 1); - // to avoid generating a double click. - int doubleClickInterval = qApp->styleHints()->mouseDoubleClickInterval() + 10; - QTest::qWait(doubleClickInterval); - - QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100)); + QCOMPARE(window->rootObject()->property("doubleClicks").toInt(), 0); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100), doubleClickInterval); QTest::qWait(1000); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(100,100)); @@ -1072,9 +1072,7 @@ void tst_QQuickMouseArea::clickThrough() QCOMPARE(window->rootObject()->property("presses").toInt(), 0); QCOMPARE(window->rootObject()->property("clicks").toInt(), 0); - QTest::qWait(doubleClickInterval); // to avoid generating a double click. - - QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100), doubleClickInterval); QTest::qWait(1000); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(100,100)); QTest::qWait(100); @@ -1093,15 +1091,13 @@ void tst_QQuickMouseArea::clickThrough() window->rootObject()->setProperty("letThrough", QVariant(true)); - QTest::qWait(doubleClickInterval); // to avoid generating a double click. - QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100), doubleClickInterval); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(100,100)); QCOMPARE(window->rootObject()->property("presses").toInt(), 0); QTRY_COMPARE(window->rootObject()->property("clicks").toInt(), 1); - QTest::qWait(doubleClickInterval); // to avoid generating a double click. - QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100), doubleClickInterval); QTest::qWait(1000); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(100,100)); QTest::qWait(100); @@ -1120,12 +1116,10 @@ void tst_QQuickMouseArea::clickThrough() window->rootObject()->setProperty("noPropagation", QVariant(true)); - QTest::qWait(doubleClickInterval); // to avoid generating a double click. - QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100), doubleClickInterval); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(100,100)); - QTest::qWait(doubleClickInterval); // to avoid generating a double click. - QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100), doubleClickInterval); QTest::qWait(1000); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(100,100)); QTest::qWait(100); @@ -1146,7 +1140,7 @@ void tst_QQuickMouseArea::clickThrough() QVERIFY(QTest::qWaitForWindowExposed(window.data())); QVERIFY(window->rootObject() != 0); - QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100), doubleClickInterval); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(100,100)); QCOMPARE(window->rootObject()->property("clicksEnabled").toInt(), 1); @@ -1154,8 +1148,7 @@ void tst_QQuickMouseArea::clickThrough() window->rootObject()->setProperty("disableLower", QVariant(true)); - QTest::qWait(doubleClickInterval); // to avoid generating a double click. - QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(100,100), doubleClickInterval); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(100,100)); QCOMPARE(window->rootObject()->property("clicksEnabled").toInt(), 2); -- cgit v1.2.3 From e5592e75b5d17f409a0623f663474f963d66486b Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 9 Aug 2016 18:32:48 +0300 Subject: Quick: use const (and const APIs) more For CoW types, prefer const methods to avoid needless detach()ing. Change-Id: I270cdc6eb8c5946f5c20b379bbb7c60f0ba518eb Reviewed-by: Shawn Rutledge Reviewed-by: Simon Hausmann --- .../context2d/qquickcontext2dcommandbuffer_p.h | 22 ++-- src/quick/items/qquickgridview.cpp | 18 ++-- src/quick/items/qquickimagebase.cpp | 2 +- src/quick/items/qquickitemanimation.cpp | 4 +- src/quick/items/qquickitemview.cpp | 33 +++--- src/quick/items/qquicklistview.cpp | 24 ++--- src/quick/items/qquickmultipointtoucharea.cpp | 4 +- src/quick/items/qquickmultipointtoucharea_p.h | 2 +- src/quick/items/qquickpathview.cpp | 12 +-- src/quick/items/qquickspriteengine.cpp | 114 ++++++++++----------- src/quick/items/qquickspriteengine_p.h | 34 +++--- src/quick/items/qquickwindow.cpp | 12 +-- src/quick/scenegraph/coreapi/qsgnode.cpp | 2 +- src/quick/util/qquickanimatorcontroller.cpp | 2 +- src/quick/util/qquickfontloader.cpp | 4 +- src/quick/util/qquickpath.cpp | 8 +- src/quick/util/qquickpixmapcache.cpp | 2 +- src/quick/util/qquickpropertychanges.cpp | 4 +- src/quick/util/qquicksmoothedanimation.cpp | 2 +- src/quick/util/qquicktimeline.cpp | 2 +- 20 files changed, 154 insertions(+), 153 deletions(-) diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h index a82b88f36f..f281cec2d5 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h @@ -70,7 +70,7 @@ public: inline int size() {return commands.size();} inline bool isEmpty() const {return commands.isEmpty(); } inline bool hasNext() const {return cmdIdx < commands.size(); } - inline QQuickContext2D::PaintCommand takeNextCommand() { return commands[cmdIdx++]; } + inline QQuickContext2D::PaintCommand takeNextCommand() { return commands.at(cmdIdx++); } inline qreal takeGlobalAlpha() { return takeReal(); } inline QPainter::CompositionMode takeGlobalCompositeOperation(){ return static_cast(takeInt()); } @@ -227,20 +227,20 @@ public: colors << color; } - inline QTransform takeMatrix() { return matrixes[matrixIdx++]; } + inline QTransform takeMatrix() { return matrixes.at(matrixIdx++); } - inline QRectF takeRect() { return rects[rectIdx++]; } + inline QRectF takeRect() { return rects.at(rectIdx++); } - inline QPainterPath takePath() { return pathes[pathIdx++]; } + inline QPainterPath takePath() { return pathes.at(pathIdx++); } - inline const QImage& takeImage() { return images[imageIdx++]; } - inline QQmlRefPointer takePixmap() { return pixmaps[pixmapIdx++]; } + inline const QImage& takeImage() { return images.at(imageIdx++); } + inline QQmlRefPointer takePixmap() { return pixmaps.at(pixmapIdx++); } - inline int takeInt() { return ints[intIdx++]; } - inline bool takeBool() {return bools[boolIdx++]; } - inline qreal takeReal() { return reals[realIdx++]; } - inline QColor takeColor() { return colors[colorIdx++]; } - inline QBrush takeBrush() { return brushes[brushIdx++]; } + inline int takeInt() { return ints.at(intIdx++); } + inline bool takeBool() {return bools.at(boolIdx++); } + inline qreal takeReal() { return reals.at(realIdx++); } + inline QColor takeColor() { return colors.at(colorIdx++); } + inline QBrush takeBrush() { return brushes.at(brushIdx++); } void replay(QPainter* painter, QQuickContext2D::State& state, const QVector2D &scaleFactor); diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index cb2611199e..b5d5e43f7a 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -478,7 +478,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal qreal colPos = colPosAt(visibleIndex); qreal rowPos = rowPosAt(visibleIndex); if (visibleItems.count()) { - FxGridItemSG *lastItem = static_cast(visibleItems.last()); + FxGridItemSG *lastItem = static_cast(visibleItems.constLast()); rowPos = lastItem->rowPos(); int colNum = qFloor((lastItem->colPos()+colSize()/2) / colSize()); if (++colNum >= columns) { @@ -536,7 +536,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal // Find first column if (visibleItems.count()) { - FxGridItemSG *firstItem = static_cast(visibleItems.first()); + FxGridItemSG *firstItem = static_cast(visibleItems.constFirst()); rowPos = firstItem->rowPos(); colNum = qFloor((firstItem->colPos()+colSize()/2) / colSize()); if (--colNum < 0) { @@ -586,7 +586,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer bool changed = false; while (visibleItems.count() > 1 - && (item = static_cast(visibleItems.first())) + && (item = static_cast(visibleItems.constFirst())) && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) { if (item->attached->delayRemove()) break; @@ -598,7 +598,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer changed = true; } while (visibleItems.count() > 1 - && (item = static_cast(visibleItems.last())) + && (item = static_cast(visibleItems.constLast())) && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) { if (item->attached->delayRemove()) break; @@ -623,7 +623,7 @@ void QQuickGridViewPrivate::layoutVisibleItems(int fromModelIndex) const qreal from = isContentFlowReversed() ? -position()-displayMarginBeginning-size() : position()-displayMarginBeginning; const qreal to = isContentFlowReversed() ? -position()+displayMarginEnd : position()+size()+displayMarginEnd; - FxGridItemSG *firstItem = static_cast(visibleItems.first()); + FxGridItemSG *firstItem = static_cast(visibleItems.constFirst()); qreal rowPos = firstItem->rowPos(); qreal colPos = firstItem->colPos(); int col = visibleIndex % columns; @@ -679,7 +679,7 @@ void QQuickGridViewPrivate::repositionPackageItemAt(QQuickItem *item, int index) void QQuickGridViewPrivate::resetFirstItemPosition(qreal pos) { - FxGridItemSG *item = static_cast(visibleItems.first()); + FxGridItemSG *item = static_cast(visibleItems.constFirst()); item->setPosition(0, pos); } @@ -692,7 +692,7 @@ void QQuickGridViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int if (moveCount == 0 && changeBeforeVisible != 0) moveCount += (changeBeforeVisible % columns) - (columns - 1); - FxGridItemSG *gridItem = static_cast(visibleItems.first()); + FxGridItemSG *gridItem = static_cast(visibleItems.constFirst()); gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize())); } @@ -2517,7 +2517,7 @@ void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex int markerItemIndex = -1; for (int i=0; iindex == afterModelIndex) { + if (visibleItems.at(i)->index == afterModelIndex) { markerItemIndex = i; break; } @@ -2536,7 +2536,7 @@ void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex countItemsRemoved -= removalResult.countChangeAfterVisibleItems; for (int i=markerItemIndex+1; iposition() < viewEndPos; i++) { - FxGridItemSG *gridItem = static_cast(visibleItems[i]); + FxGridItemSG *gridItem = static_cast(visibleItems.at(i)); if (!gridItem->transitionScheduledOrRunning()) { qreal origRowPos = gridItem->colPos(); qreal origColPos = gridItem->rowPos(); diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp index 60e31631c0..a2b99b6395 100644 --- a/src/quick/items/qquickimagebase.cpp +++ b/src/quick/items/qquickimagebase.cpp @@ -355,7 +355,7 @@ void QQuickImageBase::resolve2xLocalFile(const QUrl &url, qreal targetDevicePixe if (disable2xImageLoading) return; - QString localFile = QQmlFile::urlToLocalFileOrQrc(url); + const QString localFile = QQmlFile::urlToLocalFileOrQrc(url); // Non-local file path: @2x loading is not supported. if (localFile.isEmpty()) diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp index 027c07ec07..5c0caf5ca2 100644 --- a/src/quick/items/qquickitemanimation.cpp +++ b/src/quick/items/qquickitemanimation.cpp @@ -339,9 +339,9 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act qreal w = target->width(); qreal h = target->height(); if (pc->widthIsSet() && i < actions.size() - 1) - w = actions[++i].toValue.toReal(); + w = actions.at(++i).toValue.toReal(); if (pc->heightIsSet() && i < actions.size() - 1) - h = actions[++i].toValue.toReal(); + h = actions.at(++i).toValue.toReal(); const QPointF &transformOrigin = d->computeTransformOrigin(target->transformOrigin(), w,h); qreal tempxt = transformOrigin.x(); diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 3c1a5a6ebe..d3f045f35c 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -1199,9 +1199,9 @@ void QQuickItemViewPrivate::showVisibleItems() const { qDebug() << "Visible items:"; for (int i = 0; i < visibleItems.count(); ++i) { - qDebug() << "\t" << visibleItems[i]->index - << visibleItems[i]->item->objectName() - << visibleItems[i]->position(); + qDebug() << "\t" << visibleItems.at(i)->index + << visibleItems.at(i)->item->objectName() + << visibleItems.at(i)->position(); } } @@ -1998,7 +1998,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult prevViewPos = prevFirstVisible->position(); prevFirstVisibleIndex = prevFirstVisible->index; } - qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0; + qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.constFirst()->position() : 0.0; totalInsertionResult->visiblePos = prevViewPos; totalRemovalResult->visiblePos = prevViewPos; @@ -2077,13 +2077,13 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult // can transition it from this "original" position to its new position in the view if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) { for (int i=0; i= 0) { if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex) - repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos); + repositionItemAt(movingIntoView.at(i).item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos); else - repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos); - movingIntoView[i].item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true); + repositionItemAt(movingIntoView.at(i).item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos); + movingIntoView.at(i).item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true); } } } @@ -2129,11 +2129,11 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &remo Q_Q(QQuickItemView); bool visibleAffected = false; - if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) { - if (removal.index > visibleItems.last()->index) + if (visibleItems.count() && removal.index + removal.count > visibleItems.constLast()->index) { + if (removal.index > visibleItems.constLast()->index) removeResult->countChangeAfterVisibleItems += removal.count; else - removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index); + removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.constLast()->index); } QList::Iterator it = visibleItems.begin(); @@ -2223,10 +2223,11 @@ void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirs qreal moveBackwardsBy = 0; // shift visibleItems.first() relative to the number of added/removed items - if (visibleItems.first()->position() > prevViewPos) { + const auto pos = visibleItems.constFirst()->position(); + if (pos > prevViewPos) { moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos; moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos; - } else if (visibleItems.first()->position() < prevViewPos) { + } else if (pos < prevViewPos) { moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos; moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos; } @@ -2304,7 +2305,7 @@ bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, co void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item) { for (int i=0; itransitionableItem == item) { + if (releasePendingTransition.at(i)->transitionableItem == item) { releaseItem(releasePendingTransition.takeAt(i)); return; } @@ -2324,8 +2325,8 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous) return 0; for (int i=0; iindex == modelIndex - && !releasePendingTransition[i]->isPendingRemoval()) { + if (releasePendingTransition.at(i)->index == modelIndex + && !releasePendingTransition.at(i)->isPendingRemoval()) { releasePendingTransition[i]->releaseAfterTransition = false; return releasePendingTransition.takeAt(i); } diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 7bbc6dc9ba..50d75d3dab 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -400,7 +400,7 @@ FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const ++idx; } if (lastIndex == modelIndex-1) - return visibleItems.last(); + return visibleItems.constLast(); return 0; } @@ -752,7 +752,7 @@ bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer } } - while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) { + while (visibleItems.count() > 1 && (item = visibleItems.constLast()) && item->position() > bufferTo) { if (item->attached->delayRemove()) break; qCDebug(lcItemViewDelegateLifecycle) << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position() << (QObject *)(item->item); @@ -839,7 +839,7 @@ void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index) void QQuickListViewPrivate::resetFirstItemPosition(qreal pos) { - FxListItemSG *item = static_cast(visibleItems.first()); + FxListItemSG *item = static_cast(visibleItems.constFirst()); item->setPosition(pos); } @@ -848,12 +848,12 @@ void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int if (!visibleItems.count()) return; qreal diff = forwards - backwards; - static_cast(visibleItems.first())->setPosition(visibleItems.first()->position() + diff); + static_cast(visibleItems.constFirst())->setPosition(visibleItems.constFirst()->position() + diff); } void QQuickListViewPrivate::updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult) { - if (item != visibleItems.first()) + if (item != visibleItems.constFirst()) QQuickItemViewPrivate::updateSizeChangesBeforeVisiblePos(item, removeResult); } @@ -1270,7 +1270,7 @@ void QQuickListViewPrivate::initializeCurrentItem() if (!actualItem) { if (currentIndex == visibleIndex - 1 && visibleItems.count()) { // We can calculate exact postion in this case - listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing); + listItem->setPosition(visibleItems.constFirst()->position() - currentItem->size() - spacing); } else { // Create current item now and position as best we can. // Its position will be corrected when it becomes visible. @@ -1424,8 +1424,8 @@ void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometry // if visibleItems.first() has resized, adjust its pos since it is used to // position all subsequent items - if (visibleItems.count() && item == visibleItems.first()->item) { - FxListItemSG *listItem = static_cast(visibleItems.first()); + if (visibleItems.count() && item == visibleItems.constFirst()->item) { + FxListItemSG *listItem = static_cast(visibleItems.constFirst()); const QRectF oldGeometry(item->x() - diff.x(), item->y() - diff.y(), item->width() - diff.width(), @@ -3111,7 +3111,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch int i = visibleItems.count() - 1; while (i > 0 && visibleItems.at(i)->index == -1) --i; - if (i == 0 && visibleItems.first()->index == -1) { + if (i == 0 && visibleItems.constFirst()->index == -1) { // there are no visible items except items marked for removal index = visibleItems.count(); } else if (visibleItems.at(i)->index + 1 == modelIndex @@ -3136,7 +3136,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch qreal pos = 0; if (visibleItems.count()) { pos = index < visibleItems.count() ? visibleItems.at(index)->position() - : visibleItems.last()->endPosition()+spacing; + : visibleItems.constLast()->endPosition() + spacing; } // Update the indexes of the following visible items. @@ -3271,7 +3271,7 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex int markerItemIndex = -1; for (int i=0; iindex == afterModelIndex) { + if (visibleItems.at(i)->index == afterModelIndex) { markerItemIndex = i; break; } @@ -3284,7 +3284,7 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing)); for (int i=markerItemIndex+1; iposition() < viewEndPos; i++) { - FxListItemSG *listItem = static_cast(visibleItems[i]); + FxListItemSG *listItem = static_cast(visibleItems.at(i)); if (!listItem->transitionScheduledOrRunning()) { qreal pos = listItem->position(); listItem->setPosition(pos - sizeRemoved); diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index 89bff6e057..d31807de7f 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -549,13 +549,13 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) addTouchPoint(&p); started = true; } else if (touchPointState & Qt::TouchPointMoved) { - QQuickTouchPoint* dtp = static_cast(_touchPoints[id]); + QQuickTouchPoint* dtp = static_cast(_touchPoints.value(id)); Q_ASSERT(dtp); _movedTouchPoints.append(dtp); updateTouchPoint(dtp,&p); moved = true; } else { - QQuickTouchPoint* dtp = static_cast(_touchPoints[id]); + QQuickTouchPoint* dtp = static_cast(_touchPoints.value(id)); Q_ASSERT(dtp); updateTouchPoint(dtp,&p); } diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h index 52913f53f6..b9ef7816e0 100644 --- a/src/quick/items/qquickmultipointtoucharea_p.h +++ b/src/quick/items/qquickmultipointtoucharea_p.h @@ -231,7 +231,7 @@ public: static QQuickTouchPoint* touchPoint_at(QQmlListProperty *list, int index) { QQuickMultiPointTouchArea *q = static_cast(list->object); - return q->_touchPrototypes[index]; + return q->_touchPrototypes.value(index); } Q_SIGNALS: diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index c7e476c4b5..1de20ed5b9 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -245,7 +245,7 @@ void QQuickPathViewPrivate::clear() currentItem = 0; } for (int i=0; i= 0) { @@ -1993,8 +1993,8 @@ void QQuickPathView::refill() endPos = -1.0; startPos = 2.0; - for (int i = 0; i < d->items.count(); i++) { - int idx = d->model->indexOf(d->items[i], 0); + for (QQuickItem * item : qAsConst(d->items)) { + int idx = d->model->indexOf(item, 0); qreal curPos = d->positionOfIndex(idx); if (curPos > endPos) { endPos = curPos; @@ -2084,7 +2084,7 @@ void QQuickPathView::refill() if (!waiting && d->items.count() < count+d->cacheSize) { qCDebug(lcItemViewDelegateLifecycle) << "Checking for pathview middle inserts, items count was" << d->items.count(); idx = startIdx; - QQuickItem *lastItem = d->items[0]; + QQuickItem *lastItem = d->items.at(0); while (idx != endIdx) { //This gets the reference from the delegate model, and will not re-create QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0); @@ -2300,8 +2300,8 @@ void QQuickPathViewPrivate::createCurrentItem() return; bool inItems = false; - for (int i = 0; i < items.count(); i++) { - if (model->indexOf(items[i], 0) == currentIndex) { + for (QQuickItem *item : qAsConst(items)) { + if (model->indexOf(item, 0) == currentIndex) { inItems = true; break; } diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index 083e02fe3f..db04a83afc 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -94,7 +94,7 @@ QQuickStochasticEngine::QQuickStochasticEngine(QObject *parent) : setCount(1); } -QQuickStochasticEngine::QQuickStochasticEngine(QList states, QObject *parent) : +QQuickStochasticEngine::QQuickStochasticEngine(const QList &states, QObject *parent) : QObject(parent), m_states(states), m_timeOffset(0), m_addAdvance(false) { //Default size 1 @@ -122,7 +122,7 @@ QQuickSpriteEngine::~QQuickSpriteEngine() } -int QQuickSpriteEngine::maxFrames() +int QQuickSpriteEngine::maxFrames() const { return m_maxFrames; } @@ -140,7 +140,7 @@ TODO: Above idea needs to have the varying duration offset added to it m_startTimes will be set in advance/restart to 0->(m_framesPerRow-1) and can be used directly as extra. This makes it 'frame' instead, but is more memory efficient than two arrays and less hideous than a vector of unions. */ -int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDuration) +int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDuration) const { int myRowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow / m_sprites[state]->m_frames; if (rowDuration) @@ -153,7 +153,7 @@ int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDura return (m_timeOffset - m_startTimes[sprite]) / myRowDuration; } -int QQuickSpriteEngine::spriteState(int sprite) +int QQuickSpriteEngine::spriteState(int sprite) const { if (!m_loaded) return 0; @@ -174,7 +174,7 @@ int QQuickSpriteEngine::spriteState(int sprite) return state + extra; } -int QQuickSpriteEngine::spriteStart(int sprite) +int QQuickSpriteEngine::spriteStart(int sprite) const { if (!m_duration[sprite] || !m_loaded) return m_timeOffset; @@ -188,7 +188,7 @@ int QQuickSpriteEngine::spriteStart(int sprite) return m_startTimes[sprite] + extra*rowDuration; } -int QQuickSpriteEngine::spriteFrames(int sprite) +int QQuickSpriteEngine::spriteFrames(int sprite) const { if (!m_loaded) return 1; @@ -215,7 +215,7 @@ int QQuickSpriteEngine::spriteFrames(int sprite) return m_sprites[state]->m_framesPerRow; } -int QQuickSpriteEngine::spriteDuration(int sprite)//Full duration, not per frame +int QQuickSpriteEngine::spriteDuration(int sprite) const //Full duration, not per frame { if (!m_duration[sprite] || !m_loaded) return m_duration[sprite]; @@ -235,7 +235,7 @@ int QQuickSpriteEngine::spriteDuration(int sprite)//Full duration, not per frame return rowDuration; } -int QQuickSpriteEngine::spriteY(int sprite) +int QQuickSpriteEngine::spriteY(int sprite) const { if (!m_loaded) return 0; @@ -257,7 +257,7 @@ int QQuickSpriteEngine::spriteY(int sprite) return m_sprites[state]->m_rowY + m_sprites[state]->m_frameHeight * extra; } -int QQuickSpriteEngine::spriteX(int sprite) +int QQuickSpriteEngine::spriteX(int sprite) const { if (!m_loaded) return 0; @@ -280,24 +280,24 @@ int QQuickSpriteEngine::spriteX(int sprite) return m_sprites[state]->m_rowStartX; } -QQuickSprite* QQuickSpriteEngine::sprite(int sprite) +QQuickSprite* QQuickSpriteEngine::sprite(int sprite) const { return m_sprites[m_things[sprite]]; } -int QQuickSpriteEngine::spriteWidth(int sprite) +int QQuickSpriteEngine::spriteWidth(int sprite) const { int state = m_things[sprite]; return m_sprites[state]->m_frameWidth; } -int QQuickSpriteEngine::spriteHeight(int sprite) +int QQuickSpriteEngine::spriteHeight(int sprite) const { int state = m_things[sprite]; return m_sprites[state]->m_frameHeight; } -int QQuickSpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together +int QQuickSpriteEngine::spriteCount() const //TODO: Actually image state count, need to rename these things to make sense together { return m_imageStateCount; } @@ -312,14 +312,14 @@ void QQuickStochasticEngine::setGoal(int state, int sprite, bool jump) return; } - if (m_things[sprite] == state) + if (m_things.at(sprite) == state) return;//Already there m_things[sprite] = state; - m_duration[sprite] = m_states[state]->variedDuration(); + m_duration[sprite] = m_states.at(state)->variedDuration(); m_goals[sprite] = -1; restart(sprite); emit stateChanged(sprite); - emit m_states[state]->entered(); + emit m_states.at(state)->entered(); return; } @@ -516,8 +516,8 @@ void QQuickStochasticEngine::start(int index, int state) if (index >= m_things.count()) return; m_things[index] = state; - m_duration[index] = m_states[state]->variedDuration(); - if (m_states[state]->randomStart()) + m_duration[index] = m_states.at(state)->variedDuration(); + if (m_states.at(state)->randomStart()) m_startTimes[index] = NINF; else m_startTimes[index] = 0; @@ -538,33 +538,33 @@ void QQuickStochasticEngine::stop(int index) void QQuickStochasticEngine::restart(int index) { - bool randomStart = (m_startTimes[index] == NINF); + bool randomStart = (m_startTimes.at(index) == NINF); m_startTimes[index] = m_timeOffset; if (m_addAdvance) m_startTimes[index] += m_advanceTime.elapsed(); if (randomStart) - m_startTimes[index] -= qrand() % m_duration[index]; - int time = m_duration[index] + m_startTimes[index]; + m_startTimes[index] -= qrand() % m_duration.at(index); + int time = m_duration.at(index) + m_startTimes.at(index); for (int i=0; i= 0) + if (m_duration.at(index) >= 0) addToUpdateList(time, index); } void QQuickSpriteEngine::restart(int index) //Reimplemented to recognize and handle pseudostates { - bool randomStart = (m_startTimes[index] == NINF); - if (m_loaded && m_sprites[m_things[index]]->frameSync()) {//Manually advanced + bool randomStart = (m_startTimes.at(index) == NINF); + if (m_loaded && m_sprites.at(m_things.at(index))->frameSync()) {//Manually advanced m_startTimes[index] = 0; - if (randomStart && m_sprites[m_things[index]]->m_generatedCount) - m_startTimes[index] += qrand() % m_sprites[m_things[index]]->m_generatedCount; + if (randomStart && m_sprites.at(m_things.at(index))->m_generatedCount) + m_startTimes[index] += qrand() % m_sprites.at(m_things.at(index))->m_generatedCount; } else { m_startTimes[index] = m_timeOffset; if (m_addAdvance) m_startTimes[index] += m_advanceTime.elapsed(); if (randomStart) - m_startTimes[index] -= qrand() % m_duration[index]; - int time = spriteDuration(index) + m_startTimes[index]; + m_startTimes[index] -= qrand() % m_duration.at(index); + int time = spriteDuration(index) + m_startTimes.at(index); if (randomStart) { int curTime = m_timeOffset + (m_addAdvance ? m_advanceTime.elapsed() : 0); while (time < curTime) //Fast forward through psuedostates as needed @@ -581,11 +581,11 @@ void QQuickStochasticEngine::advance(int idx) { if (idx >= m_things.count()) return;//TODO: Proper fix(because this has happened and I just ignored it) - int nextIdx = nextState(m_things[idx],idx); + int nextIdx = nextState(m_things.at(idx), idx); m_things[idx] = nextIdx; - m_duration[idx] = m_states[nextIdx]->variedDuration(); + m_duration[idx] = m_states.at(nextIdx)->variedDuration(); restart(idx); - emit m_states[nextIdx]->entered(); + emit m_states.at(nextIdx)->entered(); emit stateChanged(idx); } @@ -598,29 +598,29 @@ void QQuickSpriteEngine::advance(int idx) //Reimplemented to recognize and handl if (idx >= m_things.count()) return;//TODO: Proper fix(because this has happened and I just ignored it) - if (m_duration[idx] == 0) { - if (m_sprites[m_things[idx]]->frameSync()) { + if (m_duration.at(idx) == 0) { + if (m_sprites.at(m_things.at(idx))->frameSync()) { //Manually called, advance inner substate count m_startTimes[idx]++; - if (m_startTimes[idx] < m_sprites[m_things[idx]]->m_generatedCount) { + if (m_startTimes.at(idx) < m_sprites.at(m_things.at(idx))->m_generatedCount) { //only a pseudostate ended emit stateChanged(idx); return; } } //just go past the pseudostate logic - } else if (m_startTimes[idx] + m_duration[idx] + } else if (m_startTimes.at(idx) + m_duration.at(idx) > int(m_timeOffset + (m_addAdvance ? m_advanceTime.elapsed() : 0))) { //only a pseduostate ended emit stateChanged(idx); addToUpdateList(spriteStart(idx) + spriteDuration(idx) + (m_addAdvance ? m_advanceTime.elapsed() : 0), idx); return; } - int nextIdx = nextState(m_things[idx],idx); + int nextIdx = nextState(m_things.at(idx), idx); m_things[idx] = nextIdx; - m_duration[idx] = m_states[nextIdx]->variedDuration(); + m_duration[idx] = m_states.at(nextIdx)->variedDuration(); restart(idx); - emit m_states[nextIdx]->entered(); + emit m_states.at(nextIdx)->entered(); emit stateChanged(idx); } @@ -631,16 +631,16 @@ int QQuickStochasticEngine::nextState(int curState, int curThing) if (goalPath == -1){//Random qreal r =(qreal) qrand() / (qreal) RAND_MAX; qreal total = 0.0; - for (QVariantMap::const_iterator iter=m_states[curState]->m_to.constBegin(); - iter!=m_states[curState]->m_to.constEnd(); ++iter) + for (QVariantMap::const_iterator iter=m_states.at(curState)->m_to.constBegin(); + iter!=m_states.at(curState)->m_to.constEnd(); ++iter) total += (*iter).toReal(); r*=total; - for (QVariantMap::const_iterator iter= m_states[curState]->m_to.constBegin(); - iter!=m_states[curState]->m_to.constEnd(); ++iter){ + for (QVariantMap::const_iterator iter= m_states.at(curState)->m_to.constBegin(); + iter!=m_states.at(curState)->m_to.constEnd(); ++iter){ if (r < (*iter).toReal()){ bool superBreak = false; for (int i=0; iname() == iter.key()){ + if (m_states.at(i)->name() == iter.key()){ nextIdx = i; superBreak = true; break; @@ -664,8 +664,8 @@ uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a lis //Sprite State Update; m_timeOffset = time; m_addAdvance = false; - while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){ - foreach (int idx, m_stateUpdates.first().second) + while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.constFirst().first){ + foreach (int idx, m_stateUpdates.constFirst().second) advance(idx); m_stateUpdates.pop_front(); } @@ -674,14 +674,14 @@ uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a lis m_addAdvance = true; if (m_stateUpdates.isEmpty()) return uint(-1); - return m_stateUpdates.first().first; + return m_stateUpdates.constFirst().first; } int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) { QString goalName; - if (m_goals[spriteIdx] != -1) - goalName = m_states[m_goals[spriteIdx]]->name(); + if (m_goals.at(spriteIdx) != -1) + goalName = m_states.at(m_goals.at(spriteIdx))->name(); else goalName = m_globalGoal; if (goalName.isEmpty()) @@ -689,16 +689,16 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) //TODO: caching instead of excessively redoing iterative deepening (which was chosen arbitrarily anyways) // Paraphrased - implement in an *efficient* manner for (int i=0; iname() == goalName) + if (m_states.at(curIdx)->name() == goalName) return curIdx; if (dist < 0) dist = m_states.count(); - QQuickStochasticState* curState = m_states[curIdx]; + QQuickStochasticState* curState = m_states.at(curIdx); for (QVariantMap::const_iterator iter = curState->m_to.constBegin(); iter!=curState->m_to.constEnd(); ++iter){ if (iter.key() == goalName) for (int i=0; iname() == goalName) + if (m_states.at(i)->name() == goalName) return i; } QSet options; @@ -707,7 +707,7 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) iter!=curState->m_to.constEnd(); ++iter){ int option = -1; for (int j=0; jname() == iter.key()) + if (m_states.at(j)->name() == iter.key()) if (goalSeek(j, spriteIdx, i) != -1) option = j; if (option != -1) @@ -721,13 +721,13 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) qreal total = 0; for (QSet::const_iterator iter=options.constBegin(); iter!=options.constEnd(); ++iter) - total += curState->m_to.value(m_states[(*iter)]->name()).toReal(); + total += curState->m_to.value(m_states.at((*iter))->name()).toReal(); r *= total; for (QVariantMap::const_iterator iter = curState->m_to.constBegin(); iter!=curState->m_to.constEnd(); ++iter){ bool superContinue = true; for (int j=0; jname() == iter.key()) + if (m_states.at(j)->name() == iter.key()) if (options.contains(j)) superContinue = false; if (superContinue) @@ -735,7 +735,7 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) if (r < (*iter).toReal()){ bool superBreak = false; for (int j=0; jname() == iter.key()){ + if (m_states.at(j)->name() == iter.key()){ option = j; superBreak = true; break; @@ -755,10 +755,10 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) void QQuickStochasticEngine::addToUpdateList(uint t, int idx) { for (int i=0; i t){ + } else if (m_stateUpdates.at(i).first > t) { QList tmpList; tmpList << idx; m_stateUpdates.insert(i, qMakePair(t, tmpList)); diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h index 1bdcc9a741..424fa18a54 100644 --- a/src/quick/items/qquickspriteengine_p.h +++ b/src/quick/items/qquickspriteengine_p.h @@ -189,7 +189,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickStochasticEngine : public QObject Q_PROPERTY(QQmlListProperty states READ states) public: explicit QQuickStochasticEngine(QObject *parent = 0); - QQuickStochasticEngine(QList states, QObject *parent=0); + QQuickStochasticEngine(const QList &states, QObject *parent = 0); ~QQuickStochasticEngine(); QQmlListProperty states() @@ -210,11 +210,11 @@ public: virtual void restart(int index=0); virtual void advance(int index=0);//Sends state to the next chosen state, unlike goal. void stop(int index=0); - int curState(int index=0) {return m_things[index];} + int curState(int index=0) const {return m_things[index];} - QQuickStochasticState* state(int idx){return m_states[idx];} - int stateIndex(QQuickStochasticState* s){return m_states.indexOf(s);} - int stateIndex(const QString& s) { + QQuickStochasticState* state(int idx) const {return m_states[idx];} + int stateIndex(QQuickStochasticState* s) const {return m_states.indexOf(s);} + int stateIndex(const QString& s) const { for (int i=0; iname() == s) return i; @@ -273,17 +273,17 @@ public: return QQmlListProperty(this, m_sprites); } - QQuickSprite* sprite(int sprite=0); - int spriteState(int sprite=0); - int spriteStart(int sprite=0); - int spriteFrames(int sprite=0); - int spriteDuration(int sprite=0); - int spriteX(int sprite=0); - int spriteY(int sprite=0); - int spriteWidth(int sprite=0); - int spriteHeight(int sprite=0); - int spriteCount();//Like state count - int maxFrames(); + QQuickSprite* sprite(int sprite = 0) const; + int spriteState(int sprite = 0) const; + int spriteStart(int sprite = 0) const; + int spriteFrames(int sprite = 0) const; + int spriteDuration(int sprite = 0) const; + int spriteX(int sprite = 0) const; + int spriteY(int sprite = 0) const; + int spriteWidth(int sprite = 0) const; + int spriteHeight(int sprite = 0) const; + int spriteCount() const;//Like state count + int maxFrames() const; void restart(int index=0) Q_DECL_OVERRIDE; void advance(int index=0) Q_DECL_OVERRIDE; @@ -298,7 +298,7 @@ public: QImage assembledImage(int maxSize = 2048); private: - int pseudospriteProgress(int,int,int*rd=0); + int pseudospriteProgress(int, int, int *rd = 0) const; QList m_sprites; bool m_startedImageAssembly; bool m_loaded; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 16cbb2aa2f..f958d1a087 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1691,7 +1691,7 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce if (itemPrivate->hoverEnabled) { QPointF p = item->mapFromScene(scenePos); if (item->contains(p)) { - if (!hoverItems.isEmpty() && hoverItems[0] == item) { + if (!hoverItems.isEmpty() && hoverItems.at(0) == item) { //move accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted); } else { @@ -1702,24 +1702,24 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce itemsToHover << parent; // Leaving from previous hovered items until we reach the item or one of its ancestors. - while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) { + while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems.at(0))) { QQuickItem *hoverLeaveItem = hoverItems.takeFirst(); sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted); } - if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item + if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {//Not entering a new Item // ### Shouldn't we send moves for the parent items as well? accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted); } else { // Enter items that are not entered yet. int startIdx = -1; if (!hoverItems.isEmpty()) - startIdx = itemsToHover.indexOf(hoverItems[0]) - 1; + startIdx = itemsToHover.indexOf(hoverItems.at(0)) - 1; if (startIdx == -1) startIdx = itemsToHover.count() - 1; for (int i = startIdx; i >= 0; i--) { - QQuickItem *itemToHover = itemsToHover[i]; + QQuickItem *itemToHover = itemsToHover.at(i); QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(itemToHover); // The item may be about to be deleted or reparented to another window // due to another hover event delivered in this function. If that is the @@ -3104,7 +3104,7 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) << itemPriv->paintNode; nodes.removeAll(0); - Q_ASSERT(nodes.first() == itemPriv->itemNodeInstance); + Q_ASSERT(nodes.constFirst() == itemPriv->itemNodeInstance); for (int i=1; isetRootNode(0); + m_renderers.constLast()->setRootNode(0); destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode. } diff --git a/src/quick/util/qquickanimatorcontroller.cpp b/src/quick/util/qquickanimatorcontroller.cpp index 3fc7d87840..eb902b2972 100644 --- a/src/quick/util/qquickanimatorcontroller.cpp +++ b/src/quick/util/qquickanimatorcontroller.cpp @@ -265,7 +265,7 @@ void QQuickAnimatorController::animationFinished(QAbstractAnimationJob *job) * needed in any case. */ if (!m_deleting.contains(job)) { - QQuickAnimatorProxyJob *proxy = m_animatorRoots[job]; + QQuickAnimatorProxyJob *proxy = m_animatorRoots.value(job); if (proxy) { m_window->update(); m_proxiesToStop << proxy; diff --git a/src/quick/util/qquickfontloader.cpp b/src/quick/util/qquickfontloader.cpp index b7367f3934..6e23d99f7e 100644 --- a/src/quick/util/qquickfontloader.cpp +++ b/src/quick/util/qquickfontloader.cpp @@ -264,7 +264,7 @@ void QQuickFontLoader::setSource(const QUrl &url) updateFontInfo(QString(), Error); } } else { - updateFontInfo(QFontDatabase::applicationFontFamilies(fontLoaderFonts()->map[d->url]->id).at(0), Ready); + updateFontInfo(QFontDatabase::applicationFontFamilies(fontLoaderFonts()->map.value(d->url)->id).at(0), Ready); } } else { if (!fontLoaderFonts()->map.contains(d->url)) { @@ -280,7 +280,7 @@ void QQuickFontLoader::setSource(const QUrl &url) // Silently fail if compiled with no_network #endif } else { - QQuickFontObject *fo = fontLoaderFonts()->map[d->url]; + QQuickFontObject *fo = fontLoaderFonts()->map.value(d->url); if (fo->id == -1) { #ifndef QT_NO_NETWORK d->status = Loading; diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 6b491a433c..dfed4c1885 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -382,7 +382,7 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en } // Fixup end points - const AttributePoint &last = attributePoints.last(); + const AttributePoint &last = attributePoints.constLast(); for (int ii = 0; ii < attributes.count(); ++ii) { if (!last.values.contains(attributes.at(ii))) endpoint(attributePoints, attributes.at(ii)); @@ -407,11 +407,11 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en } attributePoints[ii].origpercent /= length; attributePoints[ii].percent = point.values.value(percentString); - prevorigpercent = attributePoints[ii].origpercent; - prevpercent = attributePoints[ii].percent; + prevorigpercent = attributePoints.at(ii).origpercent; + prevpercent = attributePoints.at(ii).percent; } else { attributePoints[ii].origpercent /= length; - attributePoints[ii].percent = attributePoints[ii].origpercent; + attributePoints[ii].percent = attributePoints.at(ii).origpercent; } } diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index dc4b27d738..49956de822 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -629,7 +629,7 @@ void QQuickPixmapReader::processJobs() // Find a job we can use bool usableJob = false; for (int i = jobs.count() - 1; !usableJob && i >= 0; i--) { - QQuickPixmapReply *job = jobs[i]; + QQuickPixmapReply *job = jobs.at(i); const QUrl url = job->url; QString localFile; QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid; diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index c4be68cd31..555533a44e 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -287,7 +287,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler; handler->property = prop; handler->expression.take(new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(prop)->signalIndex(), - QQmlContextData::get(qmlContext(q)), object, compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex])); + QQmlContextData::get(qmlContext(q)), object, compilationUnit->runtimeFunctions.at(binding->value.compiledScriptIndex))); signalReplacements << handler; return; } @@ -455,7 +455,7 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions() QQmlBinding *newBinding = 0; if (e.id != QQmlBinding::Invalid) { QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this))); - QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, object(), d->compilationUnit->runtimeFunctions[e.id])); + QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, object(), d->compilationUnit->runtimeFunctions.at(e.id))); newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, function, object(), context); } diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp index a992589040..e7beee8ed3 100644 --- a/src/quick/util/qquicksmoothedanimation.cpp +++ b/src/quick/util/qquicksmoothedanimation.cpp @@ -409,7 +409,7 @@ QAbstractAnimationJob* QQuickSmoothedAnimation::transition(QQuickStateActions &a Q_UNUSED(direction); Q_D(QQuickSmoothedAnimation); - QQuickStateActions dataActions = QQuickPropertyAnimation::createTransitionActions(actions, modified, defaultTarget); + const QQuickStateActions dataActions = QQuickPropertyAnimation::createTransitionActions(actions, modified, defaultTarget); QContinuingAnimationGroupJob *wrapperGroup = new QContinuingAnimationGroupJob(); diff --git a/src/quick/util/qquicktimeline.cpp b/src/quick/util/qquicktimeline.cpp index 74baa3bfda..b3f0caa2b3 100644 --- a/src/quick/util/qquicktimeline.cpp +++ b/src/quick/util/qquicktimeline.cpp @@ -154,7 +154,7 @@ void QQuickTimeLinePrivate::add(QQuickTimeLineObject &g, const Op &o) } if (!iter->ops.isEmpty() && o.type == Op::Pause && - iter->ops.last().type == Op::Pause) { + iter->ops.constLast().type == Op::Pause) { iter->ops.last().length += o.length; iter->length += o.length; } else { -- cgit v1.2.3 From f5a05313757e234c35455b2e1d6fbba43bc9ace2 Mon Sep 17 00:00:00 2001 From: Ariel Molina Date: Thu, 11 Aug 2016 12:20:37 -0500 Subject: Private export the Particle Emitter Affectors were already private exported to allow for advanced particle uses. The Emitter was missing from those exports. No new features only exporting code already in place. Task-number: QTBUG-55265 Change-Id: Id2a54f45b10323101359898f844e938414457407 Reviewed-by: Shawn Rutledge --- src/particles/qquickparticleemitter_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/particles/qquickparticleemitter_p.h b/src/particles/qquickparticleemitter_p.h index dd55fdc7a6..b3f96ae3e6 100644 --- a/src/particles/qquickparticleemitter_p.h +++ b/src/particles/qquickparticleemitter_p.h @@ -62,7 +62,7 @@ #include QT_BEGIN_NAMESPACE -class QQuickParticleEmitter : public QQuickItem +class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleEmitter : public QQuickItem { Q_OBJECT Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) -- cgit v1.2.3 From b40d005544cf4b991b797b0058a23dc61e8de5c2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 7 Aug 2016 14:53:57 +0200 Subject: Enable disk cache for files coming from resources By storing the cached data in QStandardPaths::CacheDir + "/qmlcache/" + sha1(filename) Change-Id: I6a8b4ca701019204fff362504697c3dac9f54bf1 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compilationunitmapper_p.h | 2 +- src/qml/compiler/qv4compilationunitmapper_unix.cpp | 6 ++--- src/qml/compiler/qv4compilationunitmapper_win.cpp | 3 +-- src/qml/compiler/qv4compileddata.cpp | 26 +++++++++++++++----- tests/auto/qml/qmldiskcache/qmldiskcache.pro | 2 ++ tests/auto/qml/qmldiskcache/test.qml | 4 ++++ tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 28 ++++++++++++++++++++++ 7 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 tests/auto/qml/qmldiskcache/test.qml diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h index 69007f4618..5b6939f1cf 100644 --- a/src/qml/compiler/qv4compilationunitmapper_p.h +++ b/src/qml/compiler/qv4compilationunitmapper_p.h @@ -68,7 +68,7 @@ public: CompilationUnitMapper(); ~CompilationUnitMapper(); - CompiledData::Unit *open(const QString &sourcePath, QString *errorString); + CompiledData::Unit *open(const QString &cacheFilePath, const QString &sourcePath, QString *errorString); void close(); private: diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp index 7119acc80e..1aa3e05f5f 100644 --- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp @@ -50,13 +50,11 @@ QT_BEGIN_NAMESPACE using namespace QV4; -CompiledData::Unit *CompilationUnitMapper::open(const QString &sourcePath, QString *errorString) +CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString) { close(); - QByteArray cacheFileName = QFile::encodeName(sourcePath); - cacheFileName.append('c'); - int fd = qt_safe_open(cacheFileName.constData(), O_RDONLY); + int fd = qt_safe_open(QFile::encodeName(cacheFileName).constData(), O_RDONLY); if (fd == -1) { *errorString = qt_error_string(errno); return nullptr; diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp index d58c46c090..7e62cbfe8b 100644 --- a/src/qml/compiler/qv4compilationunitmapper_win.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp @@ -49,13 +49,12 @@ QT_BEGIN_NAMESPACE using namespace QV4; -CompiledData::Unit *CompilationUnitMapper::open(const QString &sourcePath, QString *errorString) +CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString) { close(); // ### TODO: fix up file encoding/normalization/unc handling once QFileSystemEntry // is exported from QtCore. - const QString cacheFileName = sourcePath + QLatin1Char('c'); HANDLE handle = #if defined(Q_OS_WINRT) CreateFile2(reinterpret_cast(cacheFileName.constData()), diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index cc1f8ae261..35f61b4f69 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -56,6 +56,8 @@ #include #include #include +#include +#include #endif #include #include @@ -321,6 +323,19 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine, sizeof(data->dependencyMD5Checksum)) == 0; } +static QString cacheFilePath(const QUrl &url) +{ + const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); + const QString localCachePath = localSourcePath + QLatin1Char('c'); + if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) + return localCachePath; + QCryptographicHash fileNameHash(QCryptographicHash::Sha1); + fileNameHash.addData(localSourcePath.toUtf8()); + QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); + QDir::root().mkpath(directory); + return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix(); +} + bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) { errorString->clear(); @@ -330,13 +345,13 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return false; } - if (!unitUrl.isLocalFile()) { + if (!QQmlFile::isLocalFile(unitUrl)) { *errorString = QStringLiteral("File has to be a local file."); return false; } // Foo.qml -> Foo.qmlc - QSaveFile cacheFile(unitUrl.toLocalFile() + QLatin1Char('c')); + QSaveFile cacheFile(cacheFilePath(unitUrl)); if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { *errorString = cacheFile.errorString(); return false; @@ -371,7 +386,7 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) { - if (!url.isLocalFile()) { + if (!QQmlFile::isLocalFile(url)) { *errorString = QStringLiteral("File has to be a local file."); return false; } @@ -379,10 +394,9 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory const QString sourcePath = url.toLocalFile(); QScopedPointer cacheFile(new CompilationUnitMapper()); - CompiledData::Unit *mappedUnit = cacheFile->open(sourcePath, errorString); - if (!mappedUnit) { + CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString); + if (!mappedUnit) return false; - } const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; QScopedValueRollback dataPtrChange(data, mappedUnit); diff --git a/tests/auto/qml/qmldiskcache/qmldiskcache.pro b/tests/auto/qml/qmldiskcache/qmldiskcache.pro index f2d1a04780..f98a157b6a 100644 --- a/tests/auto/qml/qmldiskcache/qmldiskcache.pro +++ b/tests/auto/qml/qmldiskcache/qmldiskcache.pro @@ -4,4 +4,6 @@ osx:CONFIG -= app_bundle SOURCES += tst_qmldiskcache.cpp +RESOURCES += test.qml + QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmldiskcache/test.qml b/tests/auto/qml/qmldiskcache/test.qml new file mode 100644 index 0000000000..d632422616 --- /dev/null +++ b/tests/auto/qml/qmldiskcache/test.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int value: 20 +} diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index dd23dfca07..8ccf4714ba 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include class tst_qmldiskcache: public QObject { @@ -52,6 +55,7 @@ private slots: void recompileAfterChange(); void fileSelectors(); void localAliases(); + void cacheResources(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -529,6 +533,30 @@ void tst_qmldiskcache::localAliases() } } +void tst_qmldiskcache::cacheResources() +{ + const QString cacheDirectory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + QVERIFY(QDir::root().mkpath(cacheDirectory)); + + const QString qmlCacheDirectory = cacheDirectory + QLatin1String("/qmlcache/"); + QVERIFY(QDir(qmlCacheDirectory).removeRecursively()); + QVERIFY(QDir::root().mkpath(qmlCacheDirectory)); + QVERIFY(QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot).isEmpty()); + + + QQmlEngine engine; + + { + CleanlyLoadingComponent component(&engine, QUrl("qrc:/test.qml")); + qDebug() << component.errorString(); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 20); + } + + QCOMPARE(QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot | QDir::Files).count(), 1); +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" -- cgit v1.2.3 From 6f15de1d2da9c83d7fca1d5c8243c0d69a1390ee Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 26 May 2016 14:45:31 +0300 Subject: PathView: fix infinite construction/destruction loop ... when all (or almost all) items are in the cache. When all items are in cache, check lower bound is equal to upper_bound. In rare cases, especially when almost all items are in cache, the inserting code was used (not only appending and prepending). In this code there was not bound check before creation of item and there was such situation: 1. Create item by inserting code (without bound check) 2. At the next call of refill() remove this item by life cycle because this item does not meet the conditions. And go to step 1. In other words at the first call we create some item, at the second remove this item. And again. So we had infinite construction/destruction loop. To break it we should check position of new item before creation in inserting code too (like we do in appending and prepending code). Task-number: QTBUG-37815 Change-Id: I015cdeb67ca5fcd06c34b3145b49cbd3e38d4078 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickpathview.cpp | 44 ++++++++----- .../auto/quick/qquickpathview/data/qtbug37815.qml | 77 ++++++++++++++++++++++ .../quick/qquickpathview/tst_qquickpathview.cpp | 26 ++++++++ 3 files changed, 129 insertions(+), 18 deletions(-) create mode 100644 tests/auto/quick/qquickpathview/data/qtbug37815.qml diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 1dbdd10303..f858b18c84 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -289,6 +289,8 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const // account the circular space. bool QQuickPathViewPrivate::isInBound(qreal position, qreal lower, qreal upper) const { + if (lower == upper) + return true; if (lower > upper) { if (position > upper && position > lower) position -= mappedRange; @@ -2047,27 +2049,33 @@ void QQuickPathView::refill() idx = startIdx; QQuickItem *lastItem = d->items[0]; while (idx != endIdx) { - //This gets the reference from the delegate model, and will not re-create - QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0); - if (!item) { - waiting = true; - break; - } - if (!d->items.contains(item)) { //We found a hole - nextPos = d->positionOfIndex(idx); - qCDebug(lcItemViewDelegateLifecycle) << "middle insert" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count(); - if (d->currentIndex == idx) { - currentVisible = true; - d->currentItemOffset = nextPos; + nextPos = d->positionOfIndex(idx); + if (d->isInBound(nextPos, d->mappedRange - d->mappedCache, 1.0 + d->mappedCache)) { + //This gets the reference from the delegate model, and will not re-create + QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0); + if (!item) { + waiting = true; + break; } - int lastListIdx = d->items.indexOf(lastItem); - d->items.insert(lastListIdx + 1, item); - d->updateItem(item, nextPos); - } else { - d->releaseItem(item); + + if (!d->items.contains(item)) { //We found a hole + qCDebug(lcItemViewDelegateLifecycle) << "middle insert" << idx << "@" << nextPos + << (d->currentIndex == idx ? "current" : "") + << "items count was" << d->items.count(); + if (d->currentIndex == idx) { + currentVisible = true; + d->currentItemOffset = nextPos; + } + int lastListIdx = d->items.indexOf(lastItem); + d->items.insert(lastListIdx + 1, item); + d->updateItem(item, nextPos); + } else { + d->releaseItem(item); + } + + lastItem = item; } - lastItem = item; ++idx; if (idx >= d->modelCount) idx = 0; diff --git a/tests/auto/quick/qquickpathview/data/qtbug37815.qml b/tests/auto/quick/qquickpathview/data/qtbug37815.qml new file mode 100644 index 0000000000..3fd4daca63 --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/qtbug37815.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Netris +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + width: 600 + height: 400 + PathView { + objectName: "pathView" + model: 10 + anchors.fill: parent + pathItemCount: 5 + cacheItemCount: 5 + highlightRangeMode: PathView.StrictlyEnforceRange + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + + path: Path { + startX: 0 + startY: 50 + PathLine { + x: 600 + y: 50 + } + } + + delegate: Component { + Text { + width: 50 + height: 50 + text: index + objectName: "delegate" + index + font.pixelSize: 24 + color: PathView.isCurrentItem ? "green" : "black" + } + } + } +} + diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index 2046391d02..493af97521 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -140,6 +140,7 @@ private slots: void nestedinFlickable(); void flickableDelegate(); void jsArrayChange(); + void qtbug37815(); void qtbug42716(); void qtbug53464(); void addCustomAttribute(); @@ -2334,6 +2335,31 @@ void tst_QQuickPathView::jsArrayChange() QCOMPARE(spy.count(), 1); } +void tst_QQuickPathView::qtbug37815() +{ + QScopedPointer window(createView()); + + window->setSource(testFileUrl("qtbug37815.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + // cache items will be created async. Let's wait... + QTest::qWait(1000); + + QQuickPathView *pathView = findItem(window->rootObject(), "pathView"); + QVERIFY(pathView != Q_NULLPTR); + + const int pathItemCount = pathView->pathItemCount(); + const int cacheItemCount = pathView->cacheItemCount(); + int totalCount = 0; + foreach (QQuickItem *item, pathView->childItems()) { + if (item->objectName().startsWith(QLatin1String("delegate"))) + ++totalCount; + } + QCOMPARE(pathItemCount + cacheItemCount, totalCount); +} + /* This bug was one where if you jump the list such that the sole missing item needed to be * added in the middle of the list, it would instead move an item somewhere else in the list * to the middle (messing it up very badly). -- cgit v1.2.3 From 16834ed8cb68b5aa5efecf5c670d971a59e2419c Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 12 Aug 2016 16:46:20 +0200 Subject: autotests: remove qWait(1) in touch sequences After d04982dc84d66bec92b4b3767538676cf925ef17 in qtbase, QTest::touchEvent() and QTouchEventSequence::commit() slow down every sequence of touch events, so we don't need to do it in individual tests. Change-Id: Id8133b100797d4bd2d7282ee874dbb81ed2cab47 Reviewed-by: Liang Qi --- tests/auto/quick/qquickflickable/tst_qquickflickable.cpp | 1 - .../tst_qquickmultipointtoucharea.cpp | 8 -------- tests/auto/quick/touchmouse/tst_touchmouse.cpp | 13 ------------- 3 files changed, 22 deletions(-) diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 4a0b8a8bdf..96fe97f372 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -1461,7 +1461,6 @@ void tst_qquickflickable::flickWithTouch(QQuickWindow *window, QTouchDevice *tou for (int i = 1; i <= 8; ++i) { QTest::touchEvent(window, touchDevice).move(0, from + i*diff/8, window); QQuickTouchUtils::flush(window); - QTest::qWait(1); // because Flickable pays attention to velocity, we need some time between movements } QTest::touchEvent(window, touchDevice).release(0, to, window); QQuickTouchUtils::flush(window); diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 1b32e6c4ab..c3981c466f 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -596,22 +596,18 @@ void tst_QQuickMultiPointTouchArea::inFlickable() QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); - QTest::qWait(1); // because Flickable pays attention to velocity, we need some time between movements QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); - QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); - QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); - QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); @@ -787,22 +783,18 @@ void tst_QQuickMultiPointTouchArea::inFlickable2() QCOMPARE(point11->pressed(), true); p1 += QPoint(0,15); - QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); - QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); - QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); - QTest::qWait(1); QTest::touchEvent(window.data(), device).move(0, p1); QQuickTouchUtils::flush(window.data()); diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index a2a23031e1..15d7ffd9d9 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -586,13 +586,10 @@ void tst_TouchMouse::buttonOnFlickable() QPoint p2 = p1 + QPoint(0, -10); QPoint p3 = p2 + QPoint(0, -10); QQuickTouchUtils::flush(window); - QTest::qWait(1); // because Flickable pays attention to velocity, we need some time between movements QTest::touchEvent(window, device).move(0, p1, window); QQuickTouchUtils::flush(window); - QTest::qWait(1); QTest::touchEvent(window, device).move(0, p2, window); QQuickTouchUtils::flush(window); - QTest::qWait(1); QTest::touchEvent(window, device).move(0, p3, window); QQuickTouchUtils::flush(window); @@ -674,13 +671,10 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() QPoint p2 = p1 + QPoint(0, -10); QPoint p3 = p2 + QPoint(0, -10); QQuickTouchUtils::flush(window); - QTest::qWait(1); QTest::touchEvent(window, device).move(0, p1, window); QQuickTouchUtils::flush(window); - QTest::qWait(1); QTest::touchEvent(window, device).move(0, p2, window); QQuickTouchUtils::flush(window); - QTest::qWait(1); QTest::touchEvent(window, device).move(0, p3, window); QQuickTouchUtils::flush(window); QVERIFY(flickable->isMovingVertically()); @@ -872,19 +866,15 @@ void tst_TouchMouse::pinchOnFlickable() QQuickTouchUtils::flush(window); QCOMPARE(rect->position(), QPointF(200.0, 200.0)); p -= QPoint(10, 0); - QTest::qWait(1); QTest::touchEvent(window, device).move(0, p, window); QQuickTouchUtils::flush(window); p -= QPoint(10, 0); - QTest::qWait(1); QTest::touchEvent(window, device).move(0, p, window); QQuickTouchUtils::flush(window); p -= QPoint(10, 0); - QTest::qWait(1); QTest::touchEvent(window, device).move(0, p, window); QQuickTouchUtils::flush(window); p -= QPoint(10, 0); - QTest::qWait(1); QTest::touchEvent(window, device).move(0, p, window); QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).release(0, p, window); @@ -1097,16 +1087,13 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() QQuickTouchUtils::flush(window); QCOMPARE(rect->position(), QPointF(200.0, 200.0)); p -= QPoint(10, 0); - QTest::qWait(1); pinchSequence.move(0, p, window).commit(); QQuickTouchUtils::flush(window); p -= QPoint(10, 0); - QTest::qWait(1); pinchSequence.move(0, p, window).commit(); QQuickTouchUtils::flush(window); QGuiApplication::processEvents(); p -= QPoint(10, 0); - QTest::qWait(1); pinchSequence.move(0, p, window).commit(); QQuickTouchUtils::flush(window); -- cgit v1.2.3 From c0f3c8ae5cee3ecda1ac8829336aa95cbe4d330e Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Sun, 14 Aug 2016 12:20:45 +0200 Subject: Doc: mention when Flickable.AutoFlickIfNeeded was added Change-Id: Icf72c05c9573715771353bd03735f64eadd808f2 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickflickable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 03b2a8128c..870a0268e1 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -886,7 +886,7 @@ QQuickFlickableVisibleArea *QQuickFlickable::visibleArea() \li Flickable.AutoFlickIfNeeded - allows flicking vertically if the \e contentHeight is greater than the \e height of the Flickable. Allows flicking horizontally if the \e contentWidth is greater than - to the \e width of the Flickable. + to the \e width of the Flickable. (since \c{QtQuick 2.7}) \li Flickable.HorizontalFlick - allows flicking horizontally. \li Flickable.VerticalFlick - allows flicking vertically. \li Flickable.HorizontalAndVerticalFlick - allows flicking in both directions. -- cgit v1.2.3 From 2e277da4d17cbb4da04d34b260ee157aedf60f3f Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Fri, 12 Aug 2016 10:57:39 +0200 Subject: Doc: Change instances of 'OS X' to 'macOS' As of version 10.12 (Sierra), the name of Apple's desktop operating system will be macOS. Change all occurrences where the Mac platform is discussed to use the macro \macos (defined in the documentation configuration in qtbase). Change-Id: Iea114ac73c01d74401bcd77373b41a825d2636c9 Reviewed-by: Leena Miettinen Reviewed-by: Jake Petroules --- src/imports/settings/qqmlsettings.cpp | 2 +- src/qml/doc/src/cppintegration/extending-tutorial.qdoc | 2 +- src/qml/qml/qqmlengine.cpp | 2 +- src/qml/qml/qqmlimport.cpp | 2 +- src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc | 2 +- src/quick/items/qquickevents.cpp | 2 +- src/quick/items/qquickpincharea.cpp | 2 +- src/quick/items/qquicktextinput.cpp | 2 +- src/quick/util/qquickshortcut.cpp | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/imports/settings/qqmlsettings.cpp b/src/imports/settings/qqmlsettings.cpp index 954161b60b..8db998fefb 100644 --- a/src/imports/settings/qqmlsettings.cpp +++ b/src/imports/settings/qqmlsettings.cpp @@ -208,7 +208,7 @@ QT_BEGIN_NAMESPACE instance, even if they are referring to the same setting in the same category. The information is stored in the system registry on Windows, and in XML - preferences files on OS X. On other Unix systems, in the absence of a + preferences files on \macos. On other Unix systems, in the absence of a standard, INI text files are used. See \l QSettings documentation for more details. diff --git a/src/qml/doc/src/cppintegration/extending-tutorial.qdoc b/src/qml/doc/src/cppintegration/extending-tutorial.qdoc index c0cfc3e1aa..58cc650e01 100644 --- a/src/qml/doc/src/cppintegration/extending-tutorial.qdoc +++ b/src/qml/doc/src/cppintegration/extending-tutorial.qdoc @@ -389,7 +389,7 @@ directory. When building this example on Windows or Linux, the \c Charts directory will be located at the same level as the application that uses our new import module. This way, the QML engine will find our module as the default search path for QML -imports includes the directory of the application executable. On OS X, the +imports includes the directory of the application executable. On \macos, the plugin binary is copied to \c Contents/PlugIns in the the application bundle; this path is set in \l {tutorials/extending-qml/chapter6-plugins/app.pro} {chapter6-plugins/app.pro}: diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 9a06f6b055..8f22533472 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -398,7 +398,7 @@ The following functions are also on the Qt object. \li \c "blackberry" - BlackBerry OS \li \c "ios" - iOS \li \c "linux" - Linux - \li \c "osx" - OS X + \li \c "osx" - \macos \li \c "unix" - Other Unix-based OS \li \c "windows" - Windows \li \c "wince" - Windows CE diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index c4e0c7b778..dfdf2edbe0 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1658,7 +1658,7 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, \header \li Platform \li Valid suffixes \row \li Windows \li \c .dll \row \li Unix/Linux \li \c .so - \row \li OS X \li \c .dylib, \c .bundle, \c .so + \row \li \macos \li \c .dylib, \c .bundle, \c .so \endtable Version number on unix are ignored. diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc index caedd72dc5..e5cd7e2424 100644 --- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc @@ -263,7 +263,7 @@ animations, process events, etc. \endlist The threaded renderer is currently used by default on Windows with -opengl32.dll, Linux with non-Mesa based drivers, OS X, mobile +opengl32.dll, Linux with non-Mesa based drivers, \macos, mobile platforms, and Embedded Linux with EGLFS but this is subject to change. It is possible to force use of the threaded renderer by setting \c {QSG_RENDER_LOOP=threaded} in the environment. diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 88deefbd9a..23a99ba616 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -323,7 +323,7 @@ Item { \qmlproperty point QtQuick::WheelEvent::pixelDelta This property holds the delta in screen pixels and is available in platforms that - have high-resolution trackpads, such as OS X. + have high-resolution trackpads, such as \macos. The x and y cordinate of this property holds the delta in horizontal and vertical orientation. The value should be used directly to scroll content on screen. diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index f419d570f3..70b3ca6450 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -239,7 +239,7 @@ QQuickPinchAreaPrivate::~QQuickPinchAreaPrivate() \since 5.5 This signal is emitted when the pinch area detects the smart zoom gesture. - This gesture occurs only on certain operating systems such as OS X. + This gesture occurs only on certain operating systems such as \macos. The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, including the location where the gesture occurred. \c pinch.scale diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index ffc94dfd46..f3b217dd7f 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -78,7 +78,7 @@ DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) and setting \l echoMode to an appropriate value enables TextInput to be used for a password input field. - On OS X, the Up/Down key bindings for Home/End are explicitly disabled. + On \macos, the Up/Down key bindings for Home/End are explicitly disabled. If you want such bindings (on any platform), you will need to construct them in QML. \sa TextEdit, Text diff --git a/src/quick/util/qquickshortcut.cpp b/src/quick/util/qquickshortcut.cpp index e6f66f7bf1..d47f2282b7 100644 --- a/src/quick/util/qquickshortcut.cpp +++ b/src/quick/util/qquickshortcut.cpp @@ -138,7 +138,7 @@ void QQuickShortcut::setSequence(const QVariant &sequence) \since 5.6 This property provides the shortcut's key sequence as a platform specific - string. This means that it will be shown translated, and on OS X it will + string. This means that it will be shown translated, and on \macos it will resemble a key sequence from the menu bar. It is best to display this text to the user (for example, on a tooltip). -- cgit v1.2.3 From b1eeb7cdde17f70e8e9ad3c610d6492f3ca70717 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 13 Jun 2016 10:12:39 +0200 Subject: Update .gitignore Added the target wrappers shell scripts generated by commit 282f15feaae4c525602d537ab65cb61987eb5f7f from qtbase to the list of ignored files. Similarly the config.log is also not desirable for version tracking. Change-Id: I5cf832ea706f2109d2935cc6a086ece0979cc588 Reviewed-by: Simon Hausmann --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index e949cddb22..504317b59e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,12 +4,17 @@ config.tests/*/*/* !config.tests/*/*/*[.]* config.tests/*/*/*[.]app +config.log callgrind.out.* pcviewer.cfg *~ *.a *.la +*_wrapper.sh +*_wrapper.bat +wrapper.sh +wrapper.bat *.core *.moc *.o -- cgit v1.2.3 From 852e60e67b7ee8e7aec9b89bbe26297b163b3cdc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 15 Aug 2016 09:59:19 +0200 Subject: Ignore generated QML/JS code cache files Change-Id: Ic6fd156a1f9088c35d217a6f450610ac4b7d3284 Reviewed-by: Simon Hausmann --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index f33da3c8b3..e0af30c299 100644 --- a/.gitignore +++ b/.gitignore @@ -284,3 +284,7 @@ src/qml/udis86_itab.h # Generated HLSL bytecode headers *.hlslh + +# Compiled QML/JS code +*.qmlc +*.jsc -- cgit v1.2.3 From b7c4d7a25f6327657258acae46bd55a7e62b9b4e Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Fri, 12 Aug 2016 14:13:29 +0200 Subject: Fix exponential signal emission growth Commit 9ab8b9e4 (QML: Do not register static QML dependencies on every call., 2016-04-05) introduced a new concept of separating guards into permanent and active containers. When a property is 'captured' it is added to one or other container. However, while one of the captureProperty overloads added the guards to the correct container, the other overload adds to the wrong container. Presumably this means that a binding can be invoked C+2^N times if the condition for invoking it changes N times. Task-number: QTBUG-55280 Change-Id: I51a31b3dd95825304ba208252289cc5abc744d21 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmljavascriptexpression.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 5d96240e5b..efa824dfae 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -235,7 +235,7 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration) g->connect(n); } - if (duration == OnlyOnce) + if (duration == Permanently) expression->permanentGuards.prepend(g); else expression->activeGuards.prepend(g); -- cgit v1.2.3 From 8b8492d3ffdcdba9e9decb6d9ae8b7939be6c7f2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 11 Aug 2016 15:37:21 +0200 Subject: Fix throwing an exception inside a finally block with a return in catch When exiting a catch block with a return statement, we'll unwind the exception handling manually and emit finally statements right before jumping to the exit block. If we throw an exception in the final block, we'll end up using the exception handler of the catch block that contains the return statement, which means we'll end up popping the exception scope one too many times, once through ScopeAndFinally::CatchScope in unwindException() and then when executing the exception handler block. The latter we should not be executing, instead we should jump straight to the exit block. Therefore any statements emitted as part of the manual exception unwinding (finally block here) need to be part of a new basic block with no exception handler. This bug became visible in debug builds where the Scope destructor compares the scope mark against the engine stack top to ensure correct cleanup order (which was wrong). However that in turn was hidden in debug builds again due to an accidental = instead of == in a Q_ASSERT. With the Q_ASSERT fixed this use-case is covered by ch12/12.14/S12.14_A13_T3 Change-Id: Id74a1b2bb3e063871b89cc05353b601dd60df08e Reviewed-by: Lars Knoll --- src/qml/compiler/qv4codegen.cpp | 7 +++++++ src/qml/jsruntime/qv4runtime.cpp | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 6bf931c882..d29b11ac84 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2506,6 +2506,13 @@ bool Codegen::visit(ReturnStatement *ast) Result expr = expression(ast->expression); move(_block->TEMP(_returnAddress), *expr); } + + // Since we're leaving, don't let any finally statements we emit as part of the unwinding + // jump to exception handlers at run-time if they throw. + IR::BasicBlock *unwindBlock = _function->newBasicBlock(/*no exception handler*/Q_NULLPTR); + _block->JUMP(unwindBlock); + _block = unwindBlock; + unwindException(0); _block->JUMP(_exitBlock); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index d8ae7d4e92..0a05c50432 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1204,19 +1204,19 @@ ReturnedValue Runtime::unwindException(ExecutionEngine *engine) void Runtime::pushWithScope(const Value &o, ExecutionEngine *engine) { engine->pushContext(engine->currentContext->newWithContext(o.toObject(engine))); - Q_ASSERT(engine->jsStackTop = engine->currentContext + 2); + Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); } void Runtime::pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex) { ExecutionContext *c = engine->currentContext; engine->pushContext(c->newCatchContext(c->d()->compilationUnit->runtimeStrings[exceptionVarNameIndex], engine->catchException(0))); - Q_ASSERT(engine->jsStackTop = engine->currentContext + 2); + Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); } void Runtime::popScope(ExecutionEngine *engine) { - Q_ASSERT(engine->jsStackTop = engine->currentContext + 2); + Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); engine->popContext(); engine->jsStackTop -= 2; } -- cgit v1.2.3 From 78f5b9e7f7295b9ba11da6908713fb938a1e0b90 Mon Sep 17 00:00:00 2001 From: Andreas Cord-Landwehr Date: Tue, 19 Jul 2016 22:00:54 +0200 Subject: Do not override QT_QPA_PLATFORM env variables The QGuiApplication documentation explicitly says that QT_QPA_PLATFORM can be used to specify the QPA for every QGuiApplication. Unconditionally overriding this value within the application is a unexpected behavior. Task-number: QTBUG-54789 Change-Id: Ia7a61a0f2921ef8ec27fe630e8d307d20ba1eb37 Reviewed-by: Shawn Rutledge --- tools/qmlplugindump/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 5a39e497d2..0e5d3646c2 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -977,7 +977,7 @@ int main(int argc, char *argv[]) } } - if (!requireWindowManager) + if (!requireWindowManager && qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM")) qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("minimal")); else QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); -- cgit v1.2.3 From 12c7f225e6072264964bd3a0cbec834c5382031e Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 16 Aug 2016 17:56:46 +0200 Subject: QV4Object: fix GCC 6.1 tautologial compare warning GCC warned: qtdeclarative/src/imports/localstorage/plugin.cpp:152:126: error: self-comparison always evaluates to true [-Werror=tautological-compare] Fix by comparing the types for equality instead of the addresses of their static_vtbls. Task-number: QTBUG-53373 Change-Id: Idd1598610ad6381c03c3a46abe56a332726bd6a0 Reviewed-by: Thiago Macieira Reviewed-by: Shawn Rutledge --- src/qml/jsruntime/qv4object_p.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 5c660f7e3f..4e39ccaf99 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -51,6 +51,8 @@ #include "qv4scopedvalue_p.h" #include "qv4value_p.h" +#include + QT_BEGIN_NAMESPACE @@ -125,7 +127,7 @@ struct ObjectVTable #define DEFINE_OBJECT_VTABLE(classname) \ const QV4::ObjectVTable classname::static_vtbl = \ { \ - DEFINE_MANAGED_VTABLE_INT(classname, &classname::SuperClass::static_vtbl == &Object::static_vtbl ? 0 : &classname::SuperClass::static_vtbl.vTable), \ + DEFINE_MANAGED_VTABLE_INT(classname, (QT_PREPEND_NAMESPACE(QtPrivate)::is_same::value) ? Q_NULLPTR : &classname::SuperClass::static_vtbl.vTable), \ call, \ construct, \ get, \ -- cgit v1.2.3 From b03c85de2ab337858b44bf0a21692d7b544313a5 Mon Sep 17 00:00:00 2001 From: Venugopal Shivashankar Date: Tue, 16 Aug 2016 13:10:51 +0200 Subject: Doc: Remove references to Windows CE The platform is not supported since Qt 5.7 Task-number: QTBUG-55331 Change-Id: I5a38940bd8ebf7dd62d04015e1738ee23ac65bb2 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlengine.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 28f8bf4855..dd54760760 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -409,7 +409,6 @@ The following functions are also on the Qt object. \li \c "osx" - OS X \li \c "unix" - Other Unix-based OS \li \c "windows" - Windows - \li \c "wince" - Windows CE \li \c "winrt" - Windows Runtime \li \c "winphone" - Windows Phone \endlist -- cgit v1.2.3 From 0bf89f3ce602ce2ecd3d21bc989aef6f8fb02207 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 16 Aug 2016 16:31:46 -0700 Subject: D3D12: Convert 8-bit glyphs to 32-bit to prevent using R8 formats Until we figure out why DXGI_FORMAT_R8_UNORM sampling produces weird results with AMD cards, disable it and convert to RGBA. The check could be done at runtime based on the device's VendorId (0x1002), however the shader needs changes too so it would make things more convoluted. Hence only a compile-time define for now. Task-number: QTBUG-55330 Change-Id: I73f038cf0aeba742e8ea6669359c8a6e8e400a06 Reviewed-by: Andy Nichols --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 34 +++++++++++++++------- src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h | 15 +++++++--- src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h | 3 +- .../scenegraph/d3d12/qsgd3d12glyphcache.cpp | 17 +++++++++-- src/plugins/scenegraph/d3d12/shaders/textmask.hlsl | 16 +++++----- 5 files changed, 59 insertions(+), 26 deletions(-) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 87215d5b54..900dfe7ef7 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -497,14 +497,15 @@ void QSGD3D12Engine::queueTextureResize(uint id, const QSize &size) d->queueTextureResize(id, size); } -void QSGD3D12Engine::queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos) +void QSGD3D12Engine::queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos, TextureUploadFlags flags) { - d->queueTextureUpload(id, QVector() << image, QVector() << dstPos); + d->queueTextureUpload(id, QVector() << image, QVector() << dstPos, flags); } -void QSGD3D12Engine::queueTextureUpload(uint id, const QVector &images, const QVector &dstPos) +void QSGD3D12Engine::queueTextureUpload(uint id, const QVector &images, const QVector &dstPos, + TextureUploadFlags flags) { - d->queueTextureUpload(id, images, dstPos); + d->queueTextureUpload(id, images, dstPos, flags); } void QSGD3D12Engine::releaseTexture(uint id) @@ -2321,7 +2322,7 @@ uint QSGD3D12EnginePrivate::genTexture() return id; } -static inline DXGI_FORMAT textureFormat(QImage::Format format, bool wantsAlpha, bool mipmap, +static inline DXGI_FORMAT textureFormat(QImage::Format format, bool wantsAlpha, bool mipmap, bool force32bit, QImage::Format *imageFormat, int *bytesPerPixel) { DXGI_FORMAT f = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -2333,8 +2334,12 @@ static inline DXGI_FORMAT textureFormat(QImage::Format format, bool wantsAlpha, case QImage::Format_Grayscale8: case QImage::Format_Indexed8: case QImage::Format_Alpha8: - f = DXGI_FORMAT_R8_UNORM; - bpp = 1; + if (!force32bit) { + f = DXGI_FORMAT_R8_UNORM; + bpp = 1; + } else { + convFormat = QImage::Format_RGBA8888; + } break; case QImage::Format_RGB32: f = DXGI_FORMAT_B8G8R8A8_UNORM; @@ -2410,7 +2415,9 @@ void QSGD3D12EnginePrivate::createTexture(uint id, const QSize &size, QImage::Fo textureDesc.Height = adjustedSize.height(); textureDesc.DepthOrArraySize = 1; textureDesc.MipLevels = !t.mipmap() ? 1 : QSGD3D12Engine::mipMapLevels(adjustedSize); - textureDesc.Format = textureFormat(format, t.alpha(), t.mipmap(), nullptr, nullptr); + textureDesc.Format = textureFormat(format, t.alpha(), t.mipmap(), + createFlags.testFlag(QSGD3D12Engine::TextureAlways32Bit), + nullptr, nullptr); textureDesc.SampleDesc.Count = 1; textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; if (t.mipmap()) @@ -2524,7 +2531,8 @@ void QSGD3D12EnginePrivate::queueTextureResize(uint id, const QSize &size) qDebug("submitted old content copy for texture %u on the copy queue, fence %llu", id, t.fenceValue); } -void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector &images, const QVector &dstPos) +void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector &images, const QVector &dstPos, + QSGD3D12Engine::TextureUploadFlags flags) { Q_ASSERT(id); Q_ASSERT(images.count() == dstPos.count()); @@ -2561,7 +2569,9 @@ void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector &i int totalSize = 0; for (const QImage &image : images) { int bytesPerPixel; - textureFormat(image.format(), t.alpha(), t.mipmap(), nullptr, &bytesPerPixel); + textureFormat(image.format(), t.alpha(), t.mipmap(), + flags.testFlag(QSGD3D12Engine::TextureUploadAlways32Bit), + nullptr, &bytesPerPixel); const int w = !t.mipmap() ? image.width() : adjustedTextureSize.width(); const int h = !t.mipmap() ? image.height() : adjustedTextureSize.height(); const int stride = alignedSize(w * bytesPerPixel, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); @@ -2593,7 +2603,9 @@ void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector &i for (int i = 0; i < images.count(); ++i) { QImage::Format convFormat; int bytesPerPixel; - textureFormat(images[i].format(), t.alpha(), t.mipmap(), &convFormat, &bytesPerPixel); + textureFormat(images[i].format(), t.alpha(), t.mipmap(), + flags.testFlag(QSGD3D12Engine::TextureUploadAlways32Bit), + &convFormat, &bytesPerPixel); if (Q_UNLIKELY(debug_texture() && i == 0)) qDebug("source image format %d, target format %d, bpp %d", images[i].format(), convFormat, bytesPerPixel); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h index 46cd73e63a..b30994fe0d 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h @@ -349,16 +349,22 @@ public: static QSize mipMapAdjustedSourceSize(const QSize &size); enum TextureCreateFlag { - TextureWithAlpha = 0x1, - TextureWithMipMaps = 0x2 + TextureWithAlpha = 0x01, + TextureWithMipMaps = 0x02, + TextureAlways32Bit = 0x04 }; Q_DECLARE_FLAGS(TextureCreateFlags, TextureCreateFlag) + enum TextureUploadFlag { + TextureUploadAlways32Bit = 0x01 + }; + Q_DECLARE_FLAGS(TextureUploadFlags, TextureUploadFlag) + uint genTexture(); void createTexture(uint id, const QSize &size, QImage::Format format, TextureCreateFlags flags); void queueTextureResize(uint id, const QSize &size); - void queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos = QPoint()); - void queueTextureUpload(uint id, const QVector &images, const QVector &dstPos); + void queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos = QPoint(), TextureUploadFlags flags = 0); + void queueTextureUpload(uint id, const QVector &images, const QVector &dstPos, TextureUploadFlags flags = 0); void releaseTexture(uint id); void useTexture(uint id); @@ -381,6 +387,7 @@ private: Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::ClearFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::TextureCreateFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::TextureUploadFlags) QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h index b3b244cd86..1048ed63e7 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h @@ -170,7 +170,8 @@ public: uint genTexture(); void createTexture(uint id, const QSize &size, QImage::Format format, QSGD3D12Engine::TextureCreateFlags flags); void queueTextureResize(uint id, const QSize &size); - void queueTextureUpload(uint id, const QVector &images, const QVector &dstPos); + void queueTextureUpload(uint id, const QVector &images, const QVector &dstPos, + QSGD3D12Engine::TextureUploadFlags flags); void releaseTexture(uint id); void useTexture(uint id); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp index 45ef202e83..915917c3d5 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp @@ -42,6 +42,11 @@ QT_BEGIN_NAMESPACE +// Convert A8 glyphs to 32-bit in the engine. This is here to work around +// QTBUG-55330 for AMD cards. +// If removing, textmask.hlsl must be adjusted! (.a -> .r) +#define ALWAYS_32BIT + // NOTE: Avoid categorized logging. It is slow. #define DECLARE_DEBUG_VAR(variable) \ @@ -77,7 +82,11 @@ void QSGD3D12GlyphCache::createTextureData(int width, int height) const QImage::Format imageFormat = m_format == QFontEngine::Format_A8 ? QImage::Format_Alpha8 : QImage::Format_ARGB32_Premultiplied; - m_engine->createTexture(m_id, m_size, imageFormat, QSGD3D12Engine::TextureWithAlpha); + m_engine->createTexture(m_id, m_size, imageFormat, QSGD3D12Engine::TextureWithAlpha +#ifdef ALWAYS_32BIT + | QSGD3D12Engine::TextureAlways32Bit +#endif + ); } void QSGD3D12GlyphCache::resizeTextureData(int width, int height) @@ -146,7 +155,11 @@ void QSGD3D12GlyphCache::endFillTexture() Q_ASSERT(m_id); - m_engine->queueTextureUpload(m_id, m_glyphImages, m_glyphPos); + m_engine->queueTextureUpload(m_id, m_glyphImages, m_glyphPos +#ifdef ALWAYS_32BIT + , QSGD3D12Engine::TextureUploadAlways32Bit +#endif + ); // Nothing else left to do, it is up to the text material to call // useTexture() which will then add the texture dependency to the frame. diff --git a/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl b/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl index f9d92e8ee9..bb9381e7c0 100644 --- a/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl +++ b/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl @@ -45,7 +45,7 @@ float4 PS_TextMask32(PSInput input) : SV_TARGET float4 PS_TextMask8(PSInput input) : SV_TARGET { - return colorVec * tex.Sample(samp, input.coord).r; + return colorVec * tex.Sample(samp, input.coord).a; } struct StyledPSInput @@ -66,8 +66,8 @@ StyledPSInput VS_StyledText(VSInput input) float4 PS_StyledText(StyledPSInput input) : SV_TARGET { - float glyph = tex.Sample(samp, input.coord).r; - float style = clamp(tex.Sample(samp, input.shiftedCoord).r - glyph, 0.0, 1.0); + float glyph = tex.Sample(samp, input.coord).a; + float style = clamp(tex.Sample(samp, input.shiftedCoord).a - glyph, 0.0, 1.0); return style * styleColor + glyph * colorVec; } @@ -95,10 +95,10 @@ OutlinedPSInput VS_OutlinedText(VSInput input) float4 PS_OutlinedText(OutlinedPSInput input) : SV_TARGET { - float glyph = tex.Sample(samp, input.coord).r; - float outline = clamp(clamp(tex.Sample(samp, input.coordUp).r - + tex.Sample(samp, input.coordDown).r - + tex.Sample(samp, input.coordLeft).r - + tex.Sample(samp, input.coordRight).r, 0.0, 1.0) - glyph, 0.0, 1.0); + float glyph = tex.Sample(samp, input.coord).a; + float outline = clamp(clamp(tex.Sample(samp, input.coordUp).a + + tex.Sample(samp, input.coordDown).a + + tex.Sample(samp, input.coordLeft).a + + tex.Sample(samp, input.coordRight).a, 0.0, 1.0) - glyph, 0.0, 1.0); return outline * styleColor + glyph * colorVec; } -- cgit v1.2.3 From 6b8c845001a33bbcfbf20496bd933588cbe9d7ea Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 17 Aug 2016 15:01:55 +0200 Subject: Improve Direct3D documentation Mention the required value of the environment variable and fix typo. Change-Id: Ied5259b968988a434c2783acc22f0d2f3fb39ddf Reviewed-by: Laszlo Agocs --- src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index bbc64f1484..9ce26e1bb8 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -150,7 +150,7 @@ develpoment files are present. In practice this currently means Visual Studio The adaptation is available both in normal, OpenGL-enabled Qt builds and also when Qt was configured with \c{-no-opengl}. However, it is never the default, meaning the user or the application has to explicitly request it by setting the -\c{QT_QUICK_BACKEND} environment variable or by calling +\c{QT_QUICK_BACKEND} environment variable to \c{d3d12} or by calling QQuickWindow::setSceneGraphBackend(). \section2 Motivation @@ -165,7 +165,7 @@ vendor-supported solution. This means that there are fewer problems anticipated with drivers, operations like window resizes, and special events like graphics device loss caused by device resets or graphics driver updates. -Peformance-wise the general expectation is a somewhat lower CPU usage compared +Performance-wise the general expectation is a somewhat lower CPU usage compared to OpenGL due to lower driver overhead, and a higher GPU utilization with less wasted idle time. The backend does not heavily utilize threads yet, which means there are opportunities for further improvements in the future, for example to -- cgit v1.2.3 From 9132b7731c5e2b418d240bac998a10112be50938 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 17 Aug 2016 15:05:24 +0200 Subject: stabilize tst_TouchMouse::hoverEnabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The autotest does not generate the mouse movement that will occur if you touch an actual touchscreen on X11. QQuickWindowPrivate::flushFrameSynchronousEvents() will hover the MouseArea as if the mouse had moved, but that doesn't update QGuiApplicationPrivate::lastCursorPosition. The test checks that the synth mouse move in QQuickWindowPrivate::deliverTouchAsMouse() occurred in response to the touch release, but if flushFrameSynchronousEvents occurs before these lines QVERIFY(mouseArea1->hovered()); QVERIFY(!mouseArea2->hovered()); then it will fail because the touchpoint left mouseArea2 hovered. Also replace QVERIFY with QCOMPARE where we are comparing a value, so that when it fails we can see why. Task-number: QTBUG-55350 Change-Id: I6c9c9ff1aa57c199cdc4be59156bd36e487cbd2b Reviewed-by: Jan Arve Sæther --- tests/auto/quick/touchmouse/tst_touchmouse.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 1ec24e35d5..baa4bcb21c 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -1373,8 +1373,8 @@ void tst_TouchMouse::hoverEnabled() // ------------------------- Touch click on mouseArea1 QTest::touchEvent(window, device).press(0, p1, window); - QVERIFY(enterSpy1.count() == 1); - QVERIFY(enterSpy2.count() == 0); + QCOMPARE(enterSpy1.count(), 1); + QCOMPARE(enterSpy2.count(), 0); QVERIFY(mouseArea1->pressed()); QVERIFY(mouseArea1->hovered()); QVERIFY(!mouseArea2->hovered()); @@ -1385,33 +1385,36 @@ void tst_TouchMouse::hoverEnabled() QVERIFY(!mouseArea2->hovered()); // ------------------------- Touch click on mouseArea2 + if (QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive) == 0) + QSKIP("hover can be momentarily inconsistent on X11, depending on timing of flushFrameSynchronousEvents with touch and mouse movements (QTBUG-55350)"); + QTest::touchEvent(window, device).press(0, p2, window); QVERIFY(mouseArea1->hovered()); QVERIFY(mouseArea2->hovered()); QVERIFY(mouseArea2->pressed()); - QVERIFY(enterSpy1.count() == 1); - QVERIFY(enterSpy2.count() == 1); + QCOMPARE(enterSpy1.count(), 1); + QCOMPARE(enterSpy2.count(), 1); QTest::touchEvent(window, device).release(0, p2, window); QVERIFY(clickSpy2.count() == 1); QVERIFY(mouseArea1->hovered()); QVERIFY(!mouseArea2->hovered()); - QVERIFY(exitSpy1.count() == 0); - QVERIFY(exitSpy2.count() == 1); + QCOMPARE(exitSpy1.count(), 0); + QCOMPARE(exitSpy2.count(), 1); // ------------------------- Another touch click on mouseArea1 QTest::touchEvent(window, device).press(0, p1, window); - QVERIFY(enterSpy1.count() == 1); - QVERIFY(enterSpy2.count() == 1); + QCOMPARE(enterSpy1.count(), 1); + QCOMPARE(enterSpy2.count(), 1); QVERIFY(mouseArea1->pressed()); QVERIFY(mouseArea1->hovered()); QVERIFY(!mouseArea2->hovered()); QTest::touchEvent(window, device).release(0, p1, window); - QVERIFY(clickSpy1.count() == 2); + QCOMPARE(clickSpy1.count(), 2); QVERIFY(mouseArea1->hovered()); QVERIFY(!mouseArea1->pressed()); QVERIFY(!mouseArea2->hovered()); -- cgit v1.2.3 From 9fcca80bc4682fd274d2e4d8014390962dab5daa Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 12 Aug 2016 16:05:34 +0200 Subject: Make the unit mapping on Windows configurable with regards to executable mapping If we generate byte code, then we can mmap without the executable flags, otherwise we need them. This should make things work out of the box on platforms where special rights are needed before executable mappings are allowed. Change-Id: I24e663f85d661bc51cd3bf2463547b1d1590ea32 Reviewed-by: Maurice Kalinowski --- src/qml/compiler/qqmlirbuilder.cpp | 2 -- src/qml/compiler/qqmlirbuilder_p.h | 1 - src/qml/compiler/qv4compilationunitmapper_win.cpp | 9 +++++++-- src/qml/compiler/qv4compileddata_p.h | 5 +++-- src/qml/compiler/qv4compiler.cpp | 1 + src/qml/compiler/qv4jsir_p.h | 2 ++ src/qml/jit/qv4isel_masm.cpp | 1 + src/qml/qml/qqmltypeloader.cpp | 2 +- 8 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index ef8ffa8620..31b964897f 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -302,7 +302,6 @@ Document::Document(bool debugMode) , program(0) , indexOfRootObject(0) , jsGenerator(&jsModule) - , unitFlags(0) { } @@ -1393,7 +1392,6 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, QQmlEngine QV4::CompiledData::Unit *qmlUnit = reinterpret_cast(data); qmlUnit->unitSize = totalSize; - qmlUnit->flags |= output.unitFlags; qmlUnit->flags |= QV4::CompiledData::Unit::IsQml; qmlUnit->offsetToImports = unitSize; qmlUnit->nImports = output.imports.count(); diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index eedc262e7a..cc16dc2104 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -435,7 +435,6 @@ struct Q_QML_PRIVATE_EXPORT Document int indexOfRootObject; QVector objects; QV4::Compiler::JSUnitGenerator jsGenerator; - quint32 unitFlags; QQmlRefPointer javaScriptCompilationUnit; diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp index 7e62cbfe8b..6c2f36e7a0 100644 --- a/src/qml/compiler/qv4compilationunitmapper_win.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp @@ -94,9 +94,14 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co if (!verifyHeader(&header, sourcePath, errorString)) return nullptr; + const uint mappingFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode + ? PAGE_EXECUTE_READ : PAGE_READONLY; + const uint viewFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode + ? (FILE_MAP_READ | FILE_MAP_EXECUTE) : FILE_MAP_READ; + // Data structure and qt version matched, so now we can access the rest of the file safely. - HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_EXECUTE_READ, 0, 0, 0); + HANDLE fileMappingHandle = CreateFileMapping(handle, 0, mappingFlags, 0, 0, 0); if (!fileMappingHandle) { *errorString = qt_error_string(GetLastError()); return false; @@ -106,7 +111,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co CloseHandle(fileMappingHandle); }); - dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ | FILE_MAP_EXECUTE, 0, 0, 0); + dataPtr = MapViewOfFile(fileMappingHandle, viewFlags, 0, 0, 0); if (!dataPtr) { *errorString = qt_error_string(GetLastError()); return nullptr; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index a6ca1594a4..b71c1d8185 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x02 +#define QV4_DATA_STRUCTURE_VERSION 0x03 class QIODevice; class QQmlPropertyCache; @@ -619,7 +619,8 @@ struct Unit IsQml = 0x2, StaticData = 0x4, // Unit data persistent in memory? IsSingleton = 0x8, - IsSharedLibrary = 0x10 // .pragma shared? + IsSharedLibrary = 0x10, // .pragma shared? + ContainsMachineCode = 0x20 // used to determine if we need to mmap with execute permissions }; LEUInt32 flags; LEUInt32 stringTableSize; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 50ade2c6e5..c6a872cc34 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -364,6 +364,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp CompiledData::Unit unit; memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic)); unit.flags = QV4::CompiledData::Unit::IsJavascript; + unit.flags |= irModule->unitFlags; unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; unit.architectureIndex = registerString(QSysInfo::buildAbi()); diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 51b8797862..73aa6c4975 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -934,6 +934,7 @@ struct Q_QML_PRIVATE_EXPORT Module { QString fileName; qint64 sourceTimeStamp; bool isQmlModule; // implies rootFunction is always 0 + uint unitFlags; // flags merged into CompiledData::Unit::flags #ifdef QT_NO_QML_DEBUGGER static const bool debugMode = false; #else @@ -946,6 +947,7 @@ struct Q_QML_PRIVATE_EXPORT Module { : rootFunction(0) , sourceTimeStamp(0) , isQmlModule(false) + , unitFlags(0) #ifndef QT_NO_QML_DEBUGGER , debugMode(debugMode) {} diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index c1c42f876c..9759b72794 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -265,6 +265,7 @@ InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::Ex , qmlEngine(qmlEngine) { compilationUnit->codeRefs.resize(module->functions.size()); + module->unitFlags |= QV4::CompiledData::Unit::ContainsMachineCode; } InstructionSelection::~InstructionSelection() diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index f7846f333b..566f5ef767 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2929,7 +2929,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) irUnit.javaScriptCompilationUnit = unit; irUnit.imports = collector.imports; if (collector.hasPragmaLibrary) - irUnit.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary; + irUnit.jsModule.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary; QmlIR::QmlUnitGenerator qmlGenerator; QV4::CompiledData::ResolvedTypeReferenceMap emptyDependencies; -- cgit v1.2.3 From 7101ca92fe9449d4da1b45cf57b9fa62e527b500 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 15 Aug 2016 11:24:17 +0200 Subject: Enable QML disk cache on WinRT 10 Change-Id: I6a61d015c93bf507c0b68f3a975d8574334a05e5 Reviewed-by: Maurice Kalinowski --- src/qml/compiler/qv4compilationunitmapper_win.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp index 6c2f36e7a0..4361752f93 100644 --- a/src/qml/compiler/qv4compilationunitmapper_win.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp @@ -75,10 +75,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co CloseHandle(handle); }); -#if defined(Q_OS_WINRT) - *errorString = QStringLiteral("Compilation unit mapping not supported on WinRT yet"); - return nullptr; -#else +#if !defined(Q_OS_WINRT) || _MSC_VER >= 1900 CompiledData::Unit header; DWORD bytesRead; if (!ReadFile(handle, reinterpret_cast(&header), sizeof(header), &bytesRead, nullptr)) { @@ -118,12 +115,15 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co } return reinterpret_cast(dataPtr); +#else + *errorString = QStringLiteral("Compilation unit mapping not supported on WinRT 8.1"); + return nullptr; #endif } void CompilationUnitMapper::close() { -#if !defined(Q_OS_WINRT) +#if !defined(Q_OS_WINRT) || _MSC_VER >= 1900 if (dataPtr != nullptr) UnmapViewOfFile(dataPtr); #endif -- cgit v1.2.3 From 39179d41f0b7278f869f3d746aa276940754875e Mon Sep 17 00:00:00 2001 From: Maurice Kalinowski Date: Wed, 17 Aug 2016 13:26:12 +0200 Subject: Fix return value CompilationUnitMapper::open is supposed to return a pointer, not bool. Change-Id: I798be4038b8df047d380201b746f8d97cc1db526 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compilationunitmapper_win.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp index 4361752f93..abf109484b 100644 --- a/src/qml/compiler/qv4compilationunitmapper_win.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp @@ -80,7 +80,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co DWORD bytesRead; if (!ReadFile(handle, reinterpret_cast(&header), sizeof(header), &bytesRead, nullptr)) { *errorString = qt_error_string(GetLastError()); - return false; + return nullptr; } if (bytesRead != sizeof(header)) { @@ -101,7 +101,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co HANDLE fileMappingHandle = CreateFileMapping(handle, 0, mappingFlags, 0, 0, 0); if (!fileMappingHandle) { *errorString = qt_error_string(GetLastError()); - return false; + return nullptr; } QDeferredCleanup mappingCleanup([fileMappingHandle]{ -- cgit v1.2.3 From a86e6e1821da51065b2fb3392c23da17e6e13f97 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 16 Aug 2016 12:51:12 +0200 Subject: Prospective fix for invalidating caches for qml files from resources The Qt resource system does not store any time stamps, so to validation purposes fall back to the time stamp of the application binary. Change-Id: Ia1c4d06a634ffdb2d4d281aae55e5ed34f044a3c Reviewed-by: J-P Nurmi Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compilationunitmapper.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp index b53b7cf784..2e1213464c 100644 --- a/src/qml/compiler/qv4compilationunitmapper.cpp +++ b/src/qml/compiler/qv4compilationunitmapper.cpp @@ -42,6 +42,7 @@ #include "qv4compileddata_p.h" #include #include +#include QT_BEGIN_NAMESPACE @@ -77,7 +78,16 @@ bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const { QFileInfo sourceCode(sourcePath); - if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header->sourceTimeStamp) { + QDateTime sourceTimeStamp; + if (sourceCode.exists()) + sourceTimeStamp = sourceCode.lastModified(); + + // Files from the resource system do not have any time stamps, so fall back to the application + // executable. + if (!sourceTimeStamp.isValid()) + sourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); + + if (sourceTimeStamp.isValid() && sourceTimeStamp.toMSecsSinceEpoch() != header->sourceTimeStamp) { *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); return false; } -- cgit v1.2.3 From 82e551b5fa321612cb155a450d3e0ec76a63fd45 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 8 Aug 2016 11:24:04 +0200 Subject: QML: Get rid of propTypeName in QQmlPropertyRawData This information can be recreated when needed: the property/method index is known, just like the meta-object, so we can query that for the type name. Change-Id: I9d4f557eda4e4a946a80b68d3787823767b0b1d3 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache.cpp | 17 ++++++++++++----- src/qml/qml/qqmlpropertycache_p.h | 8 +------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index af9b8cc045..7cb1425725 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -161,7 +161,6 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p) _flags.type = Flags::QVariantType; } else if (type == QVariant::UserType || type == -1) { _flags.notFullyResolved = true; - setPropTypeName(p.typeName()); } else { setPropType(type); } @@ -224,7 +223,6 @@ void QQmlPropertyData::lazyLoad(const QMetaMethod &m) if (!returnType) returnType = "\0"; if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) { - setPropTypeName(returnType); _flags.notFullyResolved = true; } @@ -691,13 +689,22 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, void QQmlPropertyCache::resolve(QQmlPropertyData *data) const { Q_ASSERT(data->notFullyResolved()); - - data->setPropType(QMetaType::type(data->propTypeName())); data->_flags.notFullyResolved = false; + const QMetaObject *mo = firstCppMetaObject(); + if (data->isFunction()) { + auto metaMethod = mo->method(data->coreIndex()); + const char *retTy = metaMethod.typeName(); + if (!retTy) + retTy = "\0"; + data->setPropType(QMetaType::type(retTy)); + } else { + auto metaProperty = mo->property(data->coreIndex()); + data->setPropType(QMetaType::type(metaProperty.typeName())); + } + if (!data->isFunction()) { if (data->propType() == QMetaType::UnknownType) { - const QMetaObject *mo = _metaObject; QQmlPropertyCache *p = _parent; while (p && (!mo || _ownMetaObject)) { mo = p->_metaObject; diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 6d3c4a8a7e..8a1f3810aa 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -177,9 +177,6 @@ public: int propType() const { Q_ASSERT(isFullyResolved()); return _propType; } void setPropType(int pt) { _propType = pt; } - const char *propTypeName() const { Q_ASSERT(!isFullyResolved()); return _propTypeName; } - void setPropTypeName(const char *ptn) { _propTypeName = ptn; } - int notifyIndex() const { return _notifyIndex; } void setNotifyIndex(int idx) { _notifyIndex = idx; } @@ -220,10 +217,7 @@ public: void setCoreIndex(int idx) { _coreIndex = idx; } private: - union { - int _propType; // When !NotFullyResolved - const char *_propTypeName; // When NotFullyResolved - }; + int _propType; // When !NotFullyResolved union { // The notify index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). -- cgit v1.2.3 From d4e09df44d55572ef4f0c3c159b214edba9033cd Mon Sep 17 00:00:00 2001 From: Maurice Kalinowski Date: Wed, 17 Aug 2016 12:41:57 +0200 Subject: winrt: Make certification pass on D3D12 backend D3D12GetDebugInterface is not allowed to be used in a package to be uploaded to the store. However, keep it enabled for debug to still access information provided by it. Change-Id: Ie19167b4000b85d519d9726a66a42c6f1ef6ce4c Reviewed-by: Laszlo Agocs --- src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 87215d5b54..9aae3fd8cd 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -736,9 +736,13 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int const bool debugLayer = qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0; if (debugLayer) { qCDebug(QSG_LOG_INFO_GENERAL, "Enabling debug layer"); +#if !defined(Q_OS_WINRT) || !defined(NDEBUG) ComPtr debugController; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) debugController->EnableDebugLayer(); +#else + qCDebug(QSG_LOG_INFO_GENERAL, "Using DebugInterface will not allow certification to pass"); +#endif } QSGD3D12DeviceManager *dev = deviceManager(); -- cgit v1.2.3 From 4840bc0c207aa09b4332842c67e65bc92f43d3bd Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Wed, 17 Aug 2016 13:13:04 +0300 Subject: QQuickPathView: fix doc for itemAt() and indexAt() These methods have real arguments. Change-Id: I61f42076d36265b58dcc598394c6b3576b02dd60 Reviewed-by: Shawn Rutledge Reviewed-by: Venugopal Shivashankar --- src/quick/items/qquickpathview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index f858b18c84..07a33db231 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1459,7 +1459,7 @@ void QQuickPathView::positionViewAtIndex(int index, int mode) } /*! - \qmlmethod int QtQuick::PathView::indexAt(int x, int y) + \qmlmethod int QtQuick::PathView::indexAt(real x, real y) Returns the index of the item containing the point \a x, \a y in content coordinates. If there is no item at the point specified, -1 is returned. @@ -1483,7 +1483,7 @@ int QQuickPathView::indexAt(qreal x, qreal y) const } /*! - \qmlmethod Item QtQuick::PathView::itemAt(int x, int y) + \qmlmethod Item QtQuick::PathView::itemAt(real x, real y) Returns the item containing the point \a x, \a y in content coordinates. If there is no item at the point specified, null is returned. -- cgit v1.2.3 From f2ba71dd55736fc2ff1d817c19d5295d70b49c07 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 16 Aug 2016 17:19:29 +0300 Subject: QQuickPathView: port some loops to 'range for' The 'range for' works with pointer increments and the generated code is less, so we save some text size. Change-Id: I66a2827c9a342d9453a52028d3ec76a91a19a606 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickpathview.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 0d047bc882..ef6a45b014 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -244,10 +244,9 @@ void QQuickPathViewPrivate::clear() releaseItem(currentItem); currentItem = 0; } - for (int i=0; i= 0) { if (model) model->cancel(requestedIndex); @@ -417,12 +416,9 @@ void QQuickPathViewPrivate::setHighlightPosition(qreal pos) void QQuickPathView::pathUpdated() { Q_D(QQuickPathView); - QList::iterator it = d->items.begin(); - while (it != d->items.end()) { - QQuickItem *item = *it; + for (QQuickItem *item : qAsConst(d->items)) { if (QQuickPathViewAttached *att = d->attached(item)) att->m_percent = -1; - ++it; } refill(); } @@ -1521,8 +1517,7 @@ int QQuickPathView::indexAt(qreal x, qreal y) const if (!d->isValid()) return -1; - for (int idx = 0; idx < d->items.count(); ++idx) { - QQuickItem *item = d->items.at(idx); + for (QQuickItem *item : d->items) { QPointF p = item->mapFromItem(this, QPointF(x, y)); if (item->contains(p)) return d->model->indexOf(item, 0); @@ -1545,8 +1540,7 @@ QQuickItem *QQuickPathView::itemAt(qreal x, qreal y) const if (!d->isValid()) return 0; - for (int idx = 0; idx < d->items.count(); ++idx) { - QQuickItem *item = d->items.at(idx); + for (QQuickItem *item : d->items) { QPointF p = item->mapFromItem(this, QPointF(x, y)); if (item->contains(p)) return item; -- cgit v1.2.3 From 286dbc48bd345d75f61fc3d64ce3d5495d602cb9 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 17 Aug 2016 08:42:37 +0200 Subject: softwarerenderer example: fix ambiguous reference to QQuickItem GCC 6 says (with paths trimmed) In file included from customrenderitem.cpp:47:0: softwarerenderer.h:51:35: error: expected ')' before '*' token softwarerenderer.h:61:5: error: reference to 'QQuickItem' is ambiguous Change-Id: I70b328426ac48f5f6140d1ad4f015858979b99a5 Reviewed-by: Laszlo Agocs --- examples/quick/scenegraph/rendernode/softwarerenderer.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.h b/examples/quick/scenegraph/rendernode/softwarerenderer.h index 5b2a475ed8..e91ca92d88 100644 --- a/examples/quick/scenegraph/rendernode/softwarerenderer.h +++ b/examples/quick/scenegraph/rendernode/softwarerenderer.h @@ -42,8 +42,7 @@ #define SOFTWARERENDERER_H #include - -class QQuickItem; +#include class SoftwareRenderNode : public QSGRenderNode { -- cgit v1.2.3 From 4354588f556953a357d23bd303a8d85296b44457 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 16 Aug 2016 17:41:23 +0300 Subject: QQuickPathView: de-duplicate code of indexAt() Re-use itemAt() method. Change-Id: Ic3673fe4d9fd3f27abc90c9436e99e0da9821cdb Reviewed-by: Shawn Rutledge --- src/quick/items/qquickpathview.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index ef6a45b014..1533a14831 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1514,16 +1514,8 @@ void QQuickPathView::positionViewAtIndex(int index, int mode) int QQuickPathView::indexAt(qreal x, qreal y) const { Q_D(const QQuickPathView); - if (!d->isValid()) - return -1; - - for (QQuickItem *item : d->items) { - QPointF p = item->mapFromItem(this, QPointF(x, y)); - if (item->contains(p)) - return d->model->indexOf(item, 0); - } - - return -1; + QQuickItem *item = itemAt(x, y); + return item ? d->model->indexOf(item, 0) : -1; } /*! -- cgit v1.2.3 From b095e0a85b1caef592f01f9786c56ac301a42dcf Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 16 Aug 2016 18:41:48 +0300 Subject: QQuickPathView: de-duplicate calls and cache results Change-Id: I4b49b912c068fd5c76a2c2c8580f616cb42e9bdb Reviewed-by: Shawn Rutledge --- src/quick/items/qquickpathview.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 1533a14831..c061c8159a 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1543,8 +1543,9 @@ QQuickItem *QQuickPathView::itemAt(qreal x, qreal y) const QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const { - qreal samples = qMin(path->path().length()/5, qreal(500.0)); - qreal res = path->path().length()/samples; + const auto pathLength = path->path().length(); + qreal samples = qMin(pathLength / 5, qreal(500.0)); + qreal res = pathLength / samples; qreal mindist = 1e10; // big number QPointF nearPoint = path->pointAt(0); @@ -1741,12 +1742,13 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *) qreal velocity = calcVelocity(); qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount); - qreal pixelVelocity = (path->path().length()/count) * velocity; + const auto averageItemLength = path->path().length() / count; + qreal pixelVelocity = averageItemLength * velocity; if (qAbs(pixelVelocity) > MinimumFlickVelocity) { if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) { // limit velocity qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity; - velocity = maxVel / (path->path().length()/count); + velocity = maxVel / averageItemLength; } // Calculate the distance to be travelled qreal v2 = velocity*velocity; -- cgit v1.2.3 From e330198669f8897212912caee65ae8910ad77519 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Wed, 17 Aug 2016 13:21:22 +0300 Subject: QQuickListView: fix doc for itemAt() and indexAt() These methods have real arguments. Change-Id: Ieb4ea8396876f237adedf5df8ab5aeec1055229f Reviewed-by: Shawn Rutledge --- src/quick/items/qquicklistview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index b324e00cac..9f126525d6 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -3318,7 +3318,7 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex */ /*! - \qmlmethod int QtQuick::ListView::indexAt(int x, int y) + \qmlmethod int QtQuick::ListView::indexAt(real x, real y) Returns the index of the visible item containing the point \a x, \a y in content coordinates. If there is no item at the point specified, or the item is @@ -3331,7 +3331,7 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex */ /*! - \qmlmethod Item QtQuick::ListView::itemAt(int x, int y) + \qmlmethod Item QtQuick::ListView::itemAt(real x, real y) Returns the visible item containing the point \a x, \a y in content coordinates. If there is no item at the point specified, or the item is -- cgit v1.2.3 From 24e6df252dc6118541d064835cea6ee470985dae Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Wed, 17 Aug 2016 13:20:16 +0300 Subject: QQuickGridView: fix doc for itemAt() and indexAt() These methods have real arguments. Change-Id: I5362a407b8417b62bb27bb313dccce8611b5e316 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickgridview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 6e5710a97b..e78ad07548 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -2586,7 +2586,7 @@ bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) co */ /*! - \qmlmethod int QtQuick::GridView::indexAt(int x, int y) + \qmlmethod int QtQuick::GridView::indexAt(real x, real y) Returns the index of the visible item containing the point \a x, \a y in content coordinates. If there is no item at the point specified, or the item is @@ -2599,7 +2599,7 @@ bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) co */ /*! - \qmlmethod Item QtQuick::GridView::itemAt(int x, int y) + \qmlmethod Item QtQuick::GridView::itemAt(real x, real y) Returns the visible item containing the point \a x, \a y in content coordinates. If there is no item at the point specified, or the item is -- cgit v1.2.3 From 7b2d2d773594bde1b67ef068edef7a6ef920cf9c Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 16 Aug 2016 18:27:44 +0300 Subject: QQuickPathView: optimize releasing of cache items Release all cache items, and then clean container. Avoid quadratic complexity. Change-Id: I61f3e43aa070c0be074c3804a83f2ff1de9a398d Reviewed-by: Shawn Rutledge --- src/quick/items/qquickpathview.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index c061c8159a..660ed9db16 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -2152,8 +2152,9 @@ void QQuickPathView::refill() if (QQuickPathViewAttached *att = d->attached(d->highlightItem)) att->setOnPath(currentVisible); } - while (d->itemCache.count()) - d->releaseItem(d->itemCache.takeLast()); + for (QQuickItem *item : qAsConst(d->itemCache)) + d->releaseItem(item); + d->itemCache.clear(); d->inRefill = false; if (currentChanged) @@ -2237,8 +2238,9 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) d->items.clear(); if (!d->modelCount) { - while (d->itemCache.count()) - d->releaseItem(d->itemCache.takeLast()); + for (QQuickItem * item : qAsConst(d->itemCache)) + d->releaseItem(item); + d->itemCache.clear(); d->offset = 0; changedOffset = true; d->tl.reset(d->moveOffset); -- cgit v1.2.3 From a61dafa2f5cb09c4357dad3f804a16398bd69e31 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Mon, 15 Aug 2016 14:33:55 +0300 Subject: qmlimportscanner: use QStringLiteral more judiciously Replace it with QL1S or with QL1C in QStringBuilder expressions Change-Id: I894c546d2d010a713cc476d60b7966e77df996af Reviewed-by: Shawn Rutledge --- tools/qmlimportscanner/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 2371057878..e6c0aaae53 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -117,7 +117,7 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con // Read the qmldir file, extract a list of plugins by // parsing the "plugin" and "classname" lines. QVariantMap pluginsForModulePath(const QString &modulePath) { - QFile qmldirFile(modulePath + QStringLiteral("/qmldir")); + QFile qmldirFile(modulePath + QLatin1String("/qmldir")); if (!qmldirFile.exists()) return QVariantMap(); @@ -141,7 +141,7 @@ QVariantMap pluginsForModulePath(const QString &modulePath) { if (dep.length() != 3) std::cerr << "depends: expected 2 arguments: module identifier and version" << std::endl; else - dependencies << QString::fromUtf8(dep[1]) + QStringLiteral(" ") + QString::fromUtf8(dep[2]).simplified(); + dependencies << QString::fromUtf8(dep[1]) + QLatin1Char(' ') + QString::fromUtf8(dep[2]).simplified(); } } while (line.length() > 0); -- cgit v1.2.3 From 707b9be53cef30ea9b7029945ef70e3d769b4e78 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 8 Aug 2016 11:49:04 +0200 Subject: QML: Change storage types of QQmlPropertyRawData fields By getting rid of the unions, all "features" (like accessors and notify indices) can now be used for all properties or functions. Other fields are trimmed to the size as used in other parts of QML. Now the size of QQmlPropertyRawData is 24 bytes on 32bit platforms, and 32 bytes on 64bit platforms. Change-Id: Ie2f22eb60e6119c93e3d3ea32a2974a718d45e91 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache_p.h | 96 ++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 8a1f3810aa..480f1dcf40 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -168,20 +168,45 @@ public: bool isCloned() const { return _flags.isCloned; } bool isConstructor() const { return _flags.isConstructor; } - bool hasOverride() const { return !(_flags.hasAccessors) && - overrideIndex() >= 0; } - bool hasRevision() const { return !(_flags.hasAccessors) && revision() != 0; } + bool hasOverride() const { return overrideIndex() >= 0; } + bool hasRevision() const { return revision() != 0; } bool isFullyResolved() const { return !_flags.notFullyResolved; } int propType() const { Q_ASSERT(isFullyResolved()); return _propType; } - void setPropType(int pt) { _propType = pt; } + void setPropType(int pt) + { + Q_ASSERT(pt >= 0); + Q_ASSERT(pt <= std::numeric_limits::max()); + _propType = quint16(pt); + } int notifyIndex() const { return _notifyIndex; } - void setNotifyIndex(int idx) { _notifyIndex = idx; } + void setNotifyIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits::min()); + Q_ASSERT(idx <= std::numeric_limits::max()); + _notifyIndex = qint16(idx); + } - QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; } - void setArguments(QQmlPropertyCacheMethodArguments *args) { _arguments = args; } + bool overrideIndexIsProperty() const { return _flags.overrideIndexIsProperty; } + void setOverrideIndexIsProperty(bool onoff) { _flags.overrideIndexIsProperty = onoff; } + + int overrideIndex() const { return _overrideIndex; } + void setOverrideIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits::min()); + Q_ASSERT(idx <= std::numeric_limits::max()); + _overrideIndex = qint16(idx); + } + + int coreIndex() const { return _coreIndex; } + void setCoreIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits::min()); + Q_ASSERT(idx <= std::numeric_limits::max()); + _coreIndex = qint16(idx); + } int revision() const { return _revision; } void setRevision(int rev) @@ -191,6 +216,9 @@ public: _revision = qint16(rev); } + QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; } + void setArguments(QQmlPropertyCacheMethodArguments *args) { _arguments = args; } + int metaObjectOffset() const { return _metaObjectOffset; } void setMetaObjectOffset(int off) { @@ -199,51 +227,35 @@ public: _metaObjectOffset = qint16(off); } - bool overrideIndexIsProperty() const { return _flags.overrideIndexIsProperty; } - void setOverrideIndexIsProperty(bool onoff) { _flags.overrideIndexIsProperty = onoff; } - - int overrideIndex() const { return _overrideIndex; } - void setOverrideIndex(int idx) - { - Q_ASSERT(idx >= std::numeric_limits::min()); - Q_ASSERT(idx <= std::numeric_limits::max()); - _overrideIndex = idx; - } - QQmlAccessors *accessors() const { return _accessors; } void setAccessors(QQmlAccessors *acc) { _accessors = acc; } - int coreIndex() const { return _coreIndex; } - void setCoreIndex(int idx) { _coreIndex = idx; } - private: - int _propType; // When !NotFullyResolved - union { - // The notify index is in the range returned by QObjectPrivate::signalIndex(). - // This is different from QMetaMethod::methodIndex(). - int _notifyIndex; // When !IsFunction - QQmlPropertyCacheMethodArguments *_arguments; // When IsFunction && HasArguments - }; + Flags _flags; + qint16 _coreIndex; + quint16 _propType; - union { - struct { // When !HasAccessors - qint16 _revision; - qint16 _metaObjectOffset; + // The notify index is in the range returned by QObjectPrivate::signalIndex(). + // This is different from QMetaMethod::methodIndex(). + qint16 _notifyIndex; + qint16 _overrideIndex; - signed int _overrideIndex; // When !IsValueTypeVirtual - }; - struct { // When HasAccessors - QQmlAccessors *_accessors; - }; - }; + qint16 _revision; + qint16 _metaObjectOffset; - int _coreIndex; - Flags _flags; + QQmlPropertyCacheMethodArguments *_arguments; + QQmlAccessors *_accessors; friend class QQmlPropertyData; friend class QQmlPropertyCache; }; +#if QT_POINTER_SIZE == 4 +Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 24); +#else // QT_POINTER_SIZE == 8 +Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 32); +#endif + class QQmlPropertyData : public QQmlPropertyRawData { public: @@ -619,12 +631,14 @@ void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flag QQmlPropertyData::QQmlPropertyData() { + setCoreIndex(-1); setPropType(0); setNotifyIndex(-1); setOverrideIndex(-1); setRevision(0); setMetaObjectOffset(-1); - setCoreIndex(-1); + setArguments(nullptr); + setAccessors(nullptr); } QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d) -- cgit v1.2.3 From 31650bc49b64c8ee6e14094a4d869afe08c5c25d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 12 Aug 2016 16:57:29 +0200 Subject: Minor binding code cleanup Remove the QQmlBinding *binding argument as it is always the this pointer. Change-Id: I76ccf64a1d37ce32089c81f60466dce79b9fa5bf Reviewed-by: Lars Knoll --- src/qml/qml/qqmlbinding.cpp | 11 ++++++----- src/qml/qml/qqmlbinding_p.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 8ed7641610..d944857fc1 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -191,7 +191,7 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) flags.setFlag(QQmlPropertyData::BypassInterceptor); QQmlBindingProfiler prof(ep->profiler, this, f); - doUpdate(this, watcher, flags, scope, f); + doUpdate(watcher, flags, scope, f); if (!watcher.wasDeleted()) setUpdatingFlag(false); @@ -205,14 +205,15 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) class QQmlBindingBinding: public QQmlBinding { protected: - void doUpdate(QQmlBinding *binding, const DeleteWatcher &, + void doUpdate(const DeleteWatcher &, QQmlPropertyData::WriteFlags flags, QV4::Scope &, const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL { Q_ASSERT(!m_targetIndex.hasValueTypeIndex()); QQmlPropertyData *pd = nullptr; getPropertyData(&pd, nullptr); - pd->writeProperty(*m_target, &binding, flags); + QQmlBinding *thisPtr = this; + pd->writeProperty(*m_target, &thisPtr, flags); } }; @@ -221,7 +222,7 @@ protected: class QQmlNonbindingBinding: public QQmlBinding { protected: - void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, + void doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::WriteFlags flags, QV4::Scope &scope, const QV4::ScopedFunctionObject &f) Q_DECL_OVERRIDE Q_DECL_FINAL { @@ -231,7 +232,7 @@ protected: bool isUndefined = false; QV4::ScopedCallData callData(scope); - binding->QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); + QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); bool error = false; if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 1801c3040c..6d42a8ea8a 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -102,7 +102,7 @@ public: void expressionChanged() Q_DECL_OVERRIDE; protected: - virtual void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, + virtual void doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::WriteFlags flags, QV4::Scope &scope, const QV4::ScopedFunctionObject &f) = 0; -- cgit v1.2.3 From b35858d4b8371adb030bc17f4aa161ce60e5984e Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 9 Aug 2016 16:41:33 +0300 Subject: Quick: replace 'foreach' with 'range for' Change-Id: I3493b16a184fc811289db9e98eff37bb987113a3 Reviewed-by: Shawn Rutledge --- src/quick/accessible/qaccessiblequickitem.cpp | 4 ++-- .../designer/qquickdesignercustomobjectdata.cpp | 4 ++-- src/quick/designer/qquickdesignersupport.cpp | 7 ++++--- src/quick/designer/qquickdesignersupportitems.cpp | 14 ++++++++------ src/quick/items/qquickdroparea.cpp | 2 +- src/quick/items/qquickitem.cpp | 3 ++- src/quick/items/qquickitemview.cpp | 4 ++-- src/quick/items/qquickmultipointtoucharea.cpp | 20 ++++++++++---------- src/quick/items/qquickpathview.cpp | 13 ++++++++----- src/quick/items/qquickrepeater.cpp | 4 ++-- src/quick/items/qquickspriteengine.cpp | 19 ++++++++++--------- src/quick/items/qquickspriteengine_p.h | 2 +- src/quick/items/qquicktext.cpp | 11 ++++++----- src/quick/items/qquicktextdocument.cpp | 2 +- src/quick/items/qquicktextedit.cpp | 4 ++-- src/quick/items/qquickview.cpp | 12 ++++++------ src/quick/qtquick2.cpp | 2 +- src/quick/scenegraph/coreapi/qsgmaterial.cpp | 4 ++-- src/quick/util/qquickanimation.cpp | 2 +- src/quick/util/qquickanimatorcontroller.cpp | 20 ++++++++++---------- src/quick/util/qquickpath.cpp | 14 +++++++------- src/quick/util/qquickpixmapcache.cpp | 2 +- src/quick/util/qquickpropertychanges.cpp | 2 +- src/quick/util/qquickstate.cpp | 4 ++-- src/quick/util/qquicktransitionmanager.cpp | 10 +++++----- 25 files changed, 97 insertions(+), 88 deletions(-) diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp index a56d098717..acea958e20 100644 --- a/src/quick/accessible/qaccessiblequickitem.cpp +++ b/src/quick/accessible/qaccessiblequickitem.cpp @@ -160,9 +160,9 @@ int QAccessibleQuickItem::indexOfChild(const QAccessibleInterface *iface) const static void unignoredChildren(QQuickItem *item, QList *items, bool paintOrder) { - QList childItems = paintOrder ? QQuickItemPrivate::get(item)->paintOrderChildItems() + const QList childItems = paintOrder ? QQuickItemPrivate::get(item)->paintOrderChildItems() : item->childItems(); - Q_FOREACH (QQuickItem *child, childItems) { + for (QQuickItem *child : childItems) { if (QQuickItemPrivate::get(child)->isAccessible) { items->append(child); } else { diff --git a/src/quick/designer/qquickdesignercustomobjectdata.cpp b/src/quick/designer/qquickdesignercustomobjectdata.cpp index 3c8f4b281c..e37254d165 100644 --- a/src/quick/designer/qquickdesignercustomobjectdata.cpp +++ b/src/quick/designer/qquickdesignercustomobjectdata.cpp @@ -145,10 +145,10 @@ void QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(QObject *obje void QQuickDesignerCustomObjectData::populateResetHashes() { - QQuickDesignerSupport::PropertyNameList propertyNameList = + const QQuickDesignerSupport::PropertyNameList propertyNameList = QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object()); - Q_FOREACH (const QQuickDesignerSupport::PropertyName &propertyName, propertyNameList) { + for (const QQuickDesignerSupport::PropertyName &propertyName : propertyNameList) { QQmlProperty property(object(), QString::fromUtf8(propertyName), QQmlEngine::contextForObject(object())); QQmlAbstractBinding::Ptr binding = QQmlAbstractBinding::Ptr(QQmlPropertyPrivate::binding(property)); diff --git a/src/quick/designer/qquickdesignersupport.cpp b/src/quick/designer/qquickdesignersupport.cpp index f063cd3a81..44be12bb78 100644 --- a/src/quick/designer/qquickdesignersupport.cpp +++ b/src/quick/designer/qquickdesignersupport.cpp @@ -236,7 +236,8 @@ bool QQuickDesignerSupport::isAnchoredTo(QQuickItem *fromItem, QQuickItem *toIte bool QQuickDesignerSupport::areChildrenAnchoredTo(QQuickItem *fromItem, QQuickItem *toItem) { - Q_FOREACH (QQuickItem *childItem, fromItem->childItems()) { + const auto childItems = fromItem->childItems(); + for (QQuickItem *childItem : childItems) { if (childItem) { if (isAnchoredTo(childItem, toItem)) return true; @@ -392,10 +393,10 @@ void QQuickDesignerSupport::emitComponentCompleteSignalForAttachedProperty(QQuic QList QQuickDesignerSupport::statesForItem(QQuickItem *item) { QList objectList; - QList stateList = QQuickItemPrivate::get(item)->_states()->states(); + const QList stateList = QQuickItemPrivate::get(item)->_states()->states(); objectList.reserve(stateList.size()); - Q_FOREACH (QQuickState* state, stateList) + for (QQuickState* state : stateList) objectList.append(state); return objectList; diff --git a/src/quick/designer/qquickdesignersupportitems.cpp b/src/quick/designer/qquickdesignersupportitems.cpp index 544ca04754..2003b484ad 100644 --- a/src/quick/designer/qquickdesignersupportitems.cpp +++ b/src/quick/designer/qquickdesignersupportitems.cpp @@ -118,16 +118,16 @@ static void allSubObjects(QObject *object, QObjectList &objectList) } // search recursive in object children list - Q_FOREACH (QObject *childObject, object->children()) { + for (QObject *childObject : object->children()) { allSubObjects(childObject, objectList); } // search recursive in quick item childItems list QQuickItem *quickItem = qobject_cast(object); if (quickItem) { - Q_FOREACH (QQuickItem *childItem, quickItem->childItems()) { + const auto childItems = quickItem->childItems(); + for (QQuickItem *childItem : childItems) allSubObjects(childItem, objectList); - } } } @@ -135,7 +135,7 @@ void QQuickDesignerSupportItems::tweakObjects(QObject *object) { QObjectList objectList; allSubObjects(object, objectList); - Q_FOREACH (QObject* childObject, objectList) { + for (QObject* childObject : qAsConst(objectList)) { stopAnimation(childObject); if (fixResourcePathsForObjectCallBack) fixResourcePathsForObjectCallBack(childObject); @@ -254,7 +254,8 @@ QObject *QQuickDesignerSupportItems::createComponent(const QUrl &componentUrl, Q if (component.isError()) { qWarning() << "Error in:" << Q_FUNC_INFO << componentUrl; - Q_FOREACH (const QQmlError &error, component.errors()) + const auto errors = component.errors(); + for (const QQmlError &error : errors) qWarning() << error; } return object; @@ -282,7 +283,8 @@ void QQuickDesignerSupportItems::disableNativeTextRendering(QQuickItem *item) void QQuickDesignerSupportItems::disableTextCursor(QQuickItem *item) { - Q_FOREACH (QQuickItem *childItem, item->childItems()) + const auto childItems = item->childItems(); + for (QQuickItem *childItem : childItems) disableTextCursor(childItem); QQuickTextInput *textInput = qobject_cast(item); diff --git a/src/quick/items/qquickdroparea.cpp b/src/quick/items/qquickdroparea.cpp index 1701441240..8dcc13971e 100644 --- a/src/quick/items/qquickdroparea.cpp +++ b/src/quick/items/qquickdroparea.cpp @@ -235,7 +235,7 @@ bool QQuickDropAreaPrivate::hasMatchingKey(const QStringList &keys) const return true; QRegExp copy = keyRegExp; - foreach (const QString &key, keys) { + for (const QString &key : keys) { if (copy.exactMatch(key)) return true; } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 84f9b0f169..bb5ed60e68 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -4106,7 +4106,8 @@ bool QQuickItem::childMouseEventFilter(QQuickItem *item, QEvent *event) */ void QQuickItem::windowDeactivateEvent() { - foreach (QQuickItem* item, childItems()) { + const auto children = childItems(); + for (QQuickItem* item : children) { item->windowDeactivateEvent(); } } diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index d3f045f35c..fa874d631c 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -181,7 +181,7 @@ void QQuickItemViewChangeSet::applyChanges(const QQmlChangeSet &changeSet) int moveId = -1; int moveOffset = 0; - foreach (const QQmlChangeSet::Change &r, changeSet.removes()) { + for (const QQmlChangeSet::Change &r : changeSet.removes()) { itemCount -= r.count; if (moveId == -1 && newCurrentIndex >= r.index + r.count) { newCurrentIndex -= r.count; @@ -200,7 +200,7 @@ void QQuickItemViewChangeSet::applyChanges(const QQmlChangeSet &changeSet) currentChanged = true; } } - foreach (const QQmlChangeSet::Change &i, changeSet.inserts()) { + for (const QQmlChangeSet::Change &i : changeSet.inserts()) { if (moveId == -1) { if (itemCount && newCurrentIndex >= i.index) { newCurrentIndex += i.count; diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index d31807de7f..d68cb8e3f6 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -362,7 +362,7 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent) QQuickMultiPointTouchArea::~QQuickMultiPointTouchArea() { clearTouchLists(); - foreach (QObject *obj, _touchPoints) { + for (QObject *obj : qAsConst(_touchPoints)) { QQuickTouchPoint *dtp = static_cast(obj); if (!dtp->isQmlDefined()) delete dtp; @@ -524,7 +524,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) } int numTouchPoints = touchPoints.count(); //always remove released touches, and make sure we handle all releases before adds. - foreach (const QTouchEvent::TouchPoint &p, touchPoints) { + for (const QTouchEvent::TouchPoint &p : qAsConst(touchPoints)) { Qt::TouchPointState touchPointState = p.state(); int id = p.id(); if (touchPointState & Qt::TouchPointReleased) { @@ -539,7 +539,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) } } if (numTouchPoints >= _minimumTouchPoints && numTouchPoints <= _maximumTouchPoints) { - foreach (const QTouchEvent::TouchPoint &p, touchPoints) { + for (const QTouchEvent::TouchPoint &p : qAsConst(touchPoints)) { Qt::TouchPointState touchPointState = p.state(); int id = p.id(); if (touchPointState & Qt::TouchPointReleased) { @@ -565,7 +565,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) if (!_stealMouse /* !ignoring gesture*/) { bool offerGrab = false; const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); - foreach (const QTouchEvent::TouchPoint &p, touchPoints) { + for (const QTouchEvent::TouchPoint &p : qAsConst(touchPoints)) { if (p.state() == Qt::TouchPointReleased) continue; const QPointF ¤tPos = p.scenePos(); @@ -599,7 +599,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) void QQuickMultiPointTouchArea::clearTouchLists() { - foreach (QObject *obj, _releasedTouchPoints) { + for (QObject *obj : qAsConst(_releasedTouchPoints)) { QQuickTouchPoint *dtp = static_cast(obj); if (!dtp->isQmlDefined()) { _touchPoints.remove(dtp->pointId()); @@ -616,7 +616,7 @@ void QQuickMultiPointTouchArea::clearTouchLists() void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p) { QQuickTouchPoint *dtp = 0; - foreach (QQuickTouchPoint* tp, _touchPrototypes) { + for (QQuickTouchPoint* tp : qAsConst(_touchPrototypes)) { if (!tp->inUse()) { tp->setInUse(true); dtp = tp; @@ -636,7 +636,7 @@ void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p) void QQuickMultiPointTouchArea::addTouchPoint(const QMouseEvent *e) { QQuickTouchPoint *dtp = 0; - foreach (QQuickTouchPoint *tp, _touchPrototypes) + for (QQuickTouchPoint *tp : qAsConst(_touchPrototypes)) if (!tp->inUse()) { tp->setInUse(true); dtp = tp; @@ -782,11 +782,11 @@ void QQuickMultiPointTouchArea::ungrab() ungrabTouchPoints(); if (_touchPoints.count()) { - foreach (QObject *obj, _touchPoints) + for (QObject *obj : qAsConst(_touchPoints)) static_cast(obj)->setPressed(false); emit canceled(_touchPoints.values()); clearTouchLists(); - foreach (QObject *obj, _touchPoints) { + for (QObject *obj : qAsConst(_touchPoints)) { QQuickTouchPoint *dtp = static_cast(obj); if (!dtp->isQmlDefined()) delete dtp; @@ -901,7 +901,7 @@ bool QQuickMultiPointTouchArea::shouldFilter(QEvent *event) case QEvent::TouchUpdate: case QEvent::TouchEnd: { QTouchEvent *te = static_cast(event); - foreach (const QTouchEvent::TouchPoint &point, te->touchPoints()) { + for (const QTouchEvent::TouchPoint &point : te->touchPoints()) { if (contains(mapFromScene(point.scenePos()))) { containsPoint = true; break; diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 660ed9db16..af895570a8 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -191,7 +191,8 @@ void QQuickPathView::initItem(int index, QObject *object) att->m_view = this; qreal percent = d->positionOfIndex(index); if (percent < 1.0 && d->path) { - foreach (const QString &attr, d->path->attributes()) + const auto attributes = d->path->attributes(); + for (const QString &attr : attributes) att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent)); item->setZ(d->requestedZ); } @@ -230,7 +231,8 @@ QQmlOpenMetaObjectType *QQuickPathViewPrivate::attachedType() // pre-create one metatype to share with all attached objects attType = new QQmlOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(q)); if (path) { - foreach (const QString &attr, path->attributes()) + const auto attributes = path->attributes(); + for (const QString &attr : attributes) attType->createProperty(attr.toUtf8()); } } @@ -431,7 +433,8 @@ void QQuickPathViewPrivate::updateItem(QQuickItem *item, qreal percent) if (qFuzzyCompare(att->m_percent, percent)) return; att->m_percent = percent; - foreach (const QString &attr, path->attributes()) + const auto attributes = path->attributes(); + for (const QString &attr : attributes) att->setValue(attr.toUtf8(), path->attributeAt(attr, percent)); att->setOnPath(percent < 1.0); } @@ -2182,7 +2185,7 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) int moveOffset = 0; bool currentChanged = false; bool changedOffset = false; - foreach (const QQmlChangeSet::Change &r, changeSet.removes()) { + for (const QQmlChangeSet::Change &r : changeSet.removes()) { if (moveId == -1 && d->currentIndex >= r.index + r.count) { d->currentIndex -= r.count; currentChanged = true; @@ -2208,7 +2211,7 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) } d->modelCount -= r.count; } - foreach (const QQmlChangeSet::Change &i, changeSet.inserts()) { + for (const QQmlChangeSet::Change &i : changeSet.inserts()) { if (d->modelCount) { if (moveId == -1 && i.index <= d->currentIndex) { d->currentIndex += i.count; diff --git a/src/quick/items/qquickrepeater.cpp b/src/quick/items/qquickrepeater.cpp index 198573fda5..4f46f41b0d 100644 --- a/src/quick/items/qquickrepeater.cpp +++ b/src/quick/items/qquickrepeater.cpp @@ -461,7 +461,7 @@ void QQuickRepeater::modelUpdated(const QQmlChangeSet &changeSet, bool reset) int difference = 0; QHash > > moved; - foreach (const QQmlChangeSet::Change &remove, changeSet.removes()) { + for (const QQmlChangeSet::Change &remove : changeSet.removes()) { int index = qMin(remove.index, d->deletables.count()); int count = qMin(remove.index + remove.count, d->deletables.count()) - index; if (remove.isMove()) { @@ -483,7 +483,7 @@ void QQuickRepeater::modelUpdated(const QQmlChangeSet &changeSet, bool reset) difference -= remove.count; } - foreach (const QQmlChangeSet::Change &insert, changeSet.inserts()) { + for (const QQmlChangeSet::Change &insert : changeSet.inserts()) { int index = qMin(insert.index, d->deletables.count()); if (insert.isMove()) { QVector > items = moved.value(insert.moveId); diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index db04a83afc..10e0d51709 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -110,10 +110,10 @@ QQuickSpriteEngine::QQuickSpriteEngine(QObject *parent) { } -QQuickSpriteEngine::QQuickSpriteEngine(QList sprites, QObject *parent) +QQuickSpriteEngine::QQuickSpriteEngine(const QList &sprites, QObject *parent) : QQuickSpriteEngine(parent) { - foreach (QQuickSprite* sprite, sprites) + for (QQuickSprite* sprite : sprites) m_states << (QQuickStochasticState*)sprite; } @@ -329,7 +329,7 @@ QQuickPixmap::Status QQuickSpriteEngine::status()//Composed status of all Sprite return QQuickPixmap::Null; int null, loading, ready; null = loading = ready = 0; - foreach (QQuickSprite* s, m_sprites) { + for (QQuickSprite* s : qAsConst(m_sprites)) { switch (s->m_pix.status()) { // ### Maybe add an error message here, because this null shouldn't be reached but when it does, the image fails without an error message. case QQuickPixmap::Null : null++; break; @@ -358,7 +358,7 @@ void QQuickSpriteEngine::startAssemblingImage() QList removals; - foreach (QQuickStochasticState* s, m_states){ + for (QQuickStochasticState* s : qAsConst(m_states)) { QQuickSprite* sprite = qobject_cast(s); if (sprite) { m_sprites << sprite; @@ -367,7 +367,7 @@ void QQuickSpriteEngine::startAssemblingImage() qDebug() << "Error: Non-sprite in QQuickSpriteEngine"; } } - foreach (QQuickStochasticState* s, removals) + for (QQuickStochasticState* s : qAsConst(removals)) m_states.removeAll(s); m_startedImageAssembly = true; } @@ -376,7 +376,7 @@ QImage QQuickSpriteEngine::assembledImage(int maxSize) { QQuickPixmap::Status stat = status(); if (!m_errorsPrinted && stat == QQuickPixmap::Error) { - foreach (QQuickSprite* s, m_sprites) + for (QQuickSprite* s : qAsConst(m_sprites)) if (s->m_pix.isError()) qmlInfo(s) << s->m_pix.error(); m_errorsPrinted = true; @@ -390,7 +390,7 @@ QImage QQuickSpriteEngine::assembledImage(int maxSize) m_maxFrames = 0; m_imageStateCount = 0; - foreach (QQuickSprite* state, m_sprites){ + for (QQuickSprite* state : qAsConst(m_sprites)) { if (state->frames() > m_maxFrames) m_maxFrames = state->frames(); @@ -441,7 +441,7 @@ QImage QQuickSpriteEngine::assembledImage(int maxSize) image.fill(0); QPainter p(&image); int y = 0; - foreach (QQuickSprite* state, m_sprites){ + for (QQuickSprite* state : qAsConst(m_sprites)) { QImage img(state->m_pix.image()); int frameWidth = state->m_frameWidth; int frameHeight = state->m_frameHeight; @@ -665,7 +665,8 @@ uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a lis m_timeOffset = time; m_addAdvance = false; while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.constFirst().first){ - foreach (int idx, m_stateUpdates.constFirst().second) + const auto copy = m_stateUpdates.constFirst().second; + for (int idx : copy) advance(idx); m_stateUpdates.pop_front(); } diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h index 424fa18a54..485afc16e5 100644 --- a/src/quick/items/qquickspriteengine_p.h +++ b/src/quick/items/qquickspriteengine_p.h @@ -266,7 +266,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickSpriteEngine : public QQuickStochasticEngine Q_PROPERTY(QQmlListProperty sprites READ sprites) public: explicit QQuickSpriteEngine(QObject *parent = 0); - QQuickSpriteEngine(QList sprites, QObject *parent=0); + QQuickSpriteEngine(const QList &sprites, QObject *parent = 0); ~QQuickSpriteEngine(); QQmlListProperty sprites() { diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 9494a55c2d..14268b472e 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -322,7 +322,7 @@ void QQuickText::imageDownloadFinished() if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) { bool needToUpdateLayout = false; - foreach (QQuickStyledTextImgTag *img, d->extra->visibleImgTags) { + for (QQuickStyledTextImgTag *img : qAsConst(d->extra->visibleImgTags)) { if (!img->size.isValid()) { img->size = img->pix->implicitSize(); needToUpdateLayout = true; @@ -1115,7 +1115,7 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal QList imagesInLine; if (extra.isAllocated()) { - foreach (QQuickStyledTextImgTag *image, extra->imgTags) { + for (QQuickStyledTextImgTag *image : qAsConst(extra->imgTags)) { if (image->position >= line.textStart() && image->position < line.textStart() + line.textLength()) { @@ -1152,7 +1152,7 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal } } - foreach (QQuickStyledTextImgTag *image, imagesInLine) { + for (QQuickStyledTextImgTag *image : qAsConst(imagesInLine)) { totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height()); const int leadX = line.cursorToX(image->position); const int trailX = line.cursorToX(image->position, QTextLine::Trailing); @@ -2342,7 +2342,7 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data node->addTextLayout(QPointF(dx, dy), d->elideLayout, color, d->style, styleColor, linkColor); if (d->extra.isAllocated()) { - foreach (QQuickStyledTextImgTag *img, d->extra->visibleImgTags) { + for (QQuickStyledTextImgTag *img : qAsConst(d->extra->visibleImgTags)) { QQuickPixmap *pix = img->pix; if (pix && pix->isReady()) node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), pix->image()); @@ -2596,7 +2596,8 @@ QString QQuickTextPrivate::anchorAt(const QTextLayout *layout, const QPointF &mo QTextLine line = layout->lineAt(i); if (line.naturalTextRect().contains(mousePos)) { int charPos = line.xToCursor(mousePos.x(), QTextLine::CursorOnCharacter); - foreach (const QTextLayout::FormatRange &formatRange, layout->formats()) { + const auto formats = layout->formats(); + for (const QTextLayout::FormatRange &formatRange : formats) { if (formatRange.format.isAnchor() && charPos >= formatRange.start && charPos < formatRange.start + formatRange.length) { diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp index 3eacfd61bc..1dc54eb107 100644 --- a/src/quick/items/qquicktextdocument.cpp +++ b/src/quick/items/qquicktextdocument.cpp @@ -219,7 +219,7 @@ QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap( void QQuickTextDocumentWithImageResources::clearResources() { - foreach (QQuickPixmap *pixmap, m_resources) + for (QQuickPixmap *pixmap : qAsConst(m_resources)) pixmap->clear(this); qDeleteAll(m_resources); m_resources.clear(); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index d6d9d53a3b..c81544cbdb 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -2065,7 +2065,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * // Having nodes spanning across frame boundaries will break the current bookkeeping mechanism. We need to prevent that. QList frameBoundaries; frameBoundaries.reserve(frames.size()); - Q_FOREACH (QTextFrame *frame, frames) + for (QTextFrame *frame : qAsConst(frames)) frameBoundaries.append(frame->firstPosition()); std::sort(frameBoundaries.begin(), frameBoundaries.end()); @@ -2499,7 +2499,7 @@ void QQuickTextEdit::updateWholeDocument() { Q_D(QQuickTextEdit); if (!d->textNodeMap.isEmpty()) { - Q_FOREACH (TextNode* node, d->textNodeMap) + for (TextNode* node : qAsConst(d->textNodeMap)) node->setDirty(); } diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp index 8b74d26576..d79d8ba3cd 100644 --- a/src/quick/items/qquickview.cpp +++ b/src/quick/items/qquickview.cpp @@ -264,8 +264,8 @@ void QQuickView::setContent(const QUrl& url, QQmlComponent *component, QObject* d->component = component; if (d->component && d->component->isError()) { - QList errorList = d->component->errors(); - foreach (const QQmlError &error, errorList) { + const QList errorList = d->component->errors(); + for (const QQmlError &error : errorList) { QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning() << error; } @@ -475,8 +475,8 @@ void QQuickView::continueExecute() disconnect(d->component, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(continueExecute())); if (d->component->isError()) { - QList errorList = d->component->errors(); - foreach (const QQmlError &error, errorList) { + const QList errorList = d->component->errors(); + for (const QQmlError &error : errorList) { QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning() << error; } @@ -487,8 +487,8 @@ void QQuickView::continueExecute() QObject *obj = d->component->create(); if (d->component->isError()) { - QList errorList = d->component->errors(); - foreach (const QQmlError &error, errorList) { + const QList errorList = d->component->errors(); + for (const QQmlError &error : errorList) { QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning() << error; } diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp index c36adf56ec..9a50250ebb 100644 --- a/src/quick/qtquick2.cpp +++ b/src/quick/qtquick2.cpp @@ -133,7 +133,7 @@ void QQmlQtQuick2DebugStatesDelegate::updateBinding(QQmlContext *context, typedef QPointer QuickStatePointer; QObject *object = property.object(); QString propertyName = property.name(); - foreach (const QuickStatePointer& statePointer, m_allStates) { + for (const QuickStatePointer& statePointer : qAsConst(m_allStates)) { if (QQuickState *state = statePointer.data()) { // here we assume that the revert list on itself defines the base state if (state->isStateActive() && state->containsPropertyInRevertList(object, propertyName)) { diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp index 42a4c4abd3..4bec7b19f4 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -67,9 +67,9 @@ void qsg_set_material_failure() #ifndef QT_NO_OPENGL const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const { - QStringList files = m_sourceFiles[type]; + const QStringList files = m_sourceFiles[type]; QSGShaderSourceBuilder builder; - Q_FOREACH (const QString &file, files) + for (const QString &file : files) builder.appendSourceFile(file); m_sources[type] = builder.source(); return m_sources[type].constData(); diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp index d782f9309f..206b92eb81 100644 --- a/src/quick/util/qquickanimation.cpp +++ b/src/quick/util/qquickanimation.cpp @@ -2643,7 +2643,7 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA } if (!successfullyCreatedDefaultProperty) { - foreach (const QString &errorMessage, errorMessages) + for (const QString &errorMessage : qAsConst(errorMessages)) qmlInfo(this) << errorMessage; } } diff --git a/src/quick/util/qquickanimatorcontroller.cpp b/src/quick/util/qquickanimatorcontroller.cpp index eb902b2972..6d8167413e 100644 --- a/src/quick/util/qquickanimatorcontroller.cpp +++ b/src/quick/util/qquickanimatorcontroller.cpp @@ -69,14 +69,14 @@ QQuickAnimatorController::~QQuickAnimatorController() { // The proxy job might already have been deleted, in which case we // need to avoid calling functions on them. Then delete the job. - foreach (QAbstractAnimationJob *job, m_deleting) { + for (QAbstractAnimationJob *job : qAsConst(m_deleting)) { m_starting.take(job); m_stopping.take(job); m_animatorRoots.take(job); delete job; } - foreach (QQuickAnimatorProxyJob *proxy, m_animatorRoots) + for (QQuickAnimatorProxyJob *proxy : qAsConst(m_animatorRoots)) proxy->controllerWasDeleted(); for (auto it = m_animatorRoots.keyBegin(), end = m_animatorRoots.keyEnd(); it != end; ++it) delete *it; @@ -171,7 +171,7 @@ static void qquick_initialize_helper(QAbstractAnimationJob *job, QQuickAnimatorC void QQuickAnimatorController::beforeNodeSync() { - foreach (QAbstractAnimationJob *job, m_deleting) { + for (QAbstractAnimationJob *job : qAsConst(m_deleting)) { m_starting.take(job); m_stopping.take(job); m_animatorRoots.take(job); @@ -182,7 +182,7 @@ void QQuickAnimatorController::beforeNodeSync() if (m_starting.size()) m_window->update(); - foreach (QQuickAnimatorProxyJob *proxy, m_starting) { + for (QQuickAnimatorProxyJob *proxy : qAsConst(m_starting)) { QAbstractAnimationJob *job = proxy->job(); job->addAnimationChangeListener(this, QAbstractAnimationJob::Completion); qquick_initialize_helper(job, this, true); @@ -192,7 +192,7 @@ void QQuickAnimatorController::beforeNodeSync() } m_starting.clear(); - foreach (QQuickAnimatorProxyJob *proxy, m_stopping) { + for (QQuickAnimatorProxyJob *proxy : qAsConst(m_stopping)) { QAbstractAnimationJob *job = proxy->job(); job->stop(); } @@ -208,7 +208,7 @@ void QQuickAnimatorController::beforeNodeSync() m_nodesAreInvalid = false; } - foreach (QQuickAnimatorJob *job, m_activeLeafAnimations) { + for (QQuickAnimatorJob *job : qAsConst(m_activeLeafAnimations)) { if (!job->target()) continue; else if (m_deletedSinceLastFrame.contains(job->target())) @@ -218,7 +218,7 @@ void QQuickAnimatorController::beforeNodeSync() xform->transformHelper()->sync(); } } - foreach (QQuickItem *wiped, m_deletedSinceLastFrame) { + for (QQuickItem *wiped : qAsConst(m_deletedSinceLastFrame)) { QQuickTransformAnimatorJob::Helper *helper = m_transforms.take(wiped); // Helper will now already have been reset in all animators referencing it. delete helper; @@ -229,7 +229,7 @@ void QQuickAnimatorController::beforeNodeSync() void QQuickAnimatorController::afterNodeSync() { - foreach (QQuickAnimatorJob *job, m_activeLeafAnimations) { + for (QQuickAnimatorJob *job : qAsConst(m_activeLeafAnimations)) { if (job->target()) job->afterNodeSync(); } @@ -249,10 +249,10 @@ void QQuickAnimatorController::stopProxyJobs() // to be outside the lock. It is also safe because deletion of // proxies happens on the GUI thread, where this code is also executing. lock(); - QSet jobs = m_proxiesToStop; + const QSet jobs = m_proxiesToStop; m_proxiesToStop.clear(); unlock(); - foreach (QQuickAnimatorProxyJob *p, jobs) + for (QQuickAnimatorProxyJob *p : jobs) p->stop(); } diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index dfed4c1885..25a4433a9b 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -358,7 +358,7 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en bool usesPercent = false; int index = 0; - foreach (QQuickPathElement *pathElement, d->_pathElements) { + for (QQuickPathElement *pathElement : qAsConst(d->_pathElements)) { if (QQuickCurve *curve = qobject_cast(pathElement)) { QQuickPathData data; data.index = index; @@ -432,17 +432,17 @@ void QQuickPath::classBegin() void QQuickPath::disconnectPathElements() { - Q_D(QQuickPath); + Q_D(const QQuickPath); - foreach (QQuickPathElement *pathElement, d->_pathElements) + for (QQuickPathElement *pathElement : d->_pathElements) disconnect(pathElement, SIGNAL(changed()), this, SLOT(processPath())); } void QQuickPath::connectPathElements() { - Q_D(QQuickPath); + Q_D(const QQuickPath); - foreach (QQuickPathElement *pathElement, d->_pathElements) + for (QQuickPathElement *pathElement : d->_pathElements) connect(pathElement, SIGNAL(changed()), this, SLOT(processPath())); } @@ -453,7 +453,7 @@ void QQuickPath::gatherAttributes() QSet attributes; // First gather up all the attributes - foreach (QQuickPathElement *pathElement, d->_pathElements) { + for (QQuickPathElement *pathElement : qAsConst(d->_pathElements)) { if (QQuickCurve *curve = qobject_cast(pathElement)) d->_pathCurves.append(curve); else if (QQuickPathAttribute *attribute = qobject_cast(pathElement)) @@ -488,7 +488,7 @@ QStringList QQuickPath::attributes() const QSet attrs; // First gather up all the attributes - foreach (QQuickPathElement *pathElement, d->_pathElements) { + for (QQuickPathElement *pathElement : d->_pathElements) { if (QQuickPathAttribute *attribute = qobject_cast(pathElement)) attrs.insert(attribute->name()); diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 49956de822..9722bf544b 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -448,7 +448,7 @@ QQuickPixmapReader::~QQuickPixmapReader() mutex.lock(); // manually cancel all outstanding jobs. - foreach (QQuickPixmapReply *reply, jobs) { + for (QQuickPixmapReply *reply : qAsConst(jobs)) { if (reply->data && reply->data->reply == reply) reply->data->reply = 0; delete reply; diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index 555533a44e..37a910876e 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -256,7 +256,7 @@ void QQuickPropertyChangesPrivate::decode() if (decoded) return; - foreach (const QV4::CompiledData::Binding *binding, bindings) + for (const QV4::CompiledData::Binding *binding : qAsConst(bindings)) decodeBinding(QString(), compilationUnit->data, binding); bindings.clear(); diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp index 947a5b6e4e..2d3934cce8 100644 --- a/src/quick/util/qquickstate.cpp +++ b/src/quick/util/qquickstate.cpp @@ -334,7 +334,7 @@ QQuickStatePrivate::generateActionList() const } } - foreach(QQuickStateOperation *op, operations) + for (QQuickStateOperation *op : operations) applyList << op->actions(); inState = false; @@ -676,7 +676,7 @@ void QQuickState::apply(QQuickTransition *trans, QQuickState *revert) #ifndef QT_NO_DEBUG_STREAM // Output for debugging if (stateChangeDebug()) { - foreach(const QQuickStateAction &action, applyList) { + for (const QQuickStateAction &action : qAsConst(applyList)) { if (action.event) qWarning() << " QQuickStateAction event:" << action.event->type(); else diff --git a/src/quick/util/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp index 60f710549b..714e6d62b6 100644 --- a/src/quick/util/qquicktransitionmanager.cpp +++ b/src/quick/util/qquicktransitionmanager.cpp @@ -106,7 +106,7 @@ void QQuickTransitionManager::complete() void QQuickTransitionManagerPrivate::applyBindings() { - foreach(const QQuickStateAction &action, bindingsList) { + for (const QQuickStateAction &action : qAsConst(bindingsList)) { if (action.toBinding) { QQmlPropertyPrivate::setBinding(action.toBinding.data()); } else if (action.event) { @@ -133,7 +133,7 @@ void QQuickTransitionManager::transition(const QList &list, QQuickStateOperation::ActionList applyList = list; // Determine which actions are binding changes and disable any current bindings - foreach(const QQuickStateAction &action, applyList) { + for (const QQuickStateAction &action : qAsConst(applyList)) { if (action.toBinding) d->bindingsList << action; if (action.fromBinding) @@ -184,7 +184,7 @@ void QQuickTransitionManager::transition(const QList &list, } // Revert back to the original values - foreach(const QQuickStateAction &action, applyList) { + for (const QQuickStateAction &action : qAsConst(applyList)) { if (action.event) { if (action.event->isReversable()) { action.event->clearBindings(); @@ -239,7 +239,7 @@ void QQuickTransitionManager::transition(const QList &list, // be applied immediately. We skip applying bindings, as they are all // applied at the end in applyBindings() to avoid any nastiness mid // transition - foreach(const QQuickStateAction &action, applyList) { + for (const QQuickStateAction &action : qAsConst(applyList)) { if (action.event && !action.event->changesBindings()) { if (action.event->isReversable() && action.reverseEvent) action.event->reverse(); @@ -251,7 +251,7 @@ void QQuickTransitionManager::transition(const QList &list, } #ifndef QT_NO_DEBUG_STREAM if (stateChangeDebug()) { - foreach(const QQuickStateAction &action, applyList) { + for (const QQuickStateAction &action : qAsConst(applyList)) { if (action.event) qWarning() << " No transition for event:" << action.event->type(); else -- cgit v1.2.3 From 696e12d137fbe479f79a2b1132a7a5fb952e71d8 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Mon, 15 Aug 2016 12:10:18 +0300 Subject: qmlimportscanner: fix MSVC build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MSVC does not like QStringLiteral in operator[]. And the follow-up patch, which replaces 'foreach' with 'range for' triggers this MSVC bug. So this prequel patch resolves 2 issues: 1. fix MSVC build - the main issue. 2. de-duplicate QStringLiteral data (.rodata) - as drive-by issue. Change-Id: Ic6607edf324e9330d2b8dccd34561abb90f79cf1 Reviewed-by: Shawn Rutledge Reviewed-by: Jan Arve Sæther --- tools/qmlimportscanner/main.cpp | 72 +++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index e6c0aaae53..4a77623d28 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -55,6 +55,14 @@ QT_USE_NAMESPACE QStringList g_qmlImportPaths; +static inline QString typeLiteral() { return QStringLiteral("type"); } +static inline QString versionLiteral() { return QStringLiteral("version"); } +static inline QString nameLiteral() { return QStringLiteral("name"); } +static inline QString pluginsLiteral() { return QStringLiteral("plugins"); } +static inline QString pathLiteral() { return QStringLiteral("path"); } +static inline QString classnamesLiteral() { return QStringLiteral("classnames"); } +static inline QString dependenciesLiteral() { return QStringLiteral("dependencies"); } + static void printUsage(const QString &appNameIn) { const std::wstring appName = appNameIn.toStdWString(); @@ -84,14 +92,14 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con // handle directory imports if (!importNode->fileName.isEmpty()) { QString name = importNode->fileName.toString(); - import[QStringLiteral("name")] = name; + import[nameLiteral()] = name; if (name.endsWith(QLatin1String(".js"))) { - import[QStringLiteral("type")] = QStringLiteral("javascript"); + import[typeLiteral()] = QStringLiteral("javascript"); } else { - import[QStringLiteral("type")] = QStringLiteral("directory"); + import[typeLiteral()] = QStringLiteral("directory"); } - import[QStringLiteral("path")] = QDir::cleanPath(path + QLatin1Char('/') + name); + import[pathLiteral()] = QDir::cleanPath(path + QLatin1Char('/') + name); } else { // Walk the id chain ("Foo" -> "Bar" -> etc) QString name; @@ -103,9 +111,9 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con } name.chop(1); // remove trailing "." if (!name.isEmpty()) - import[QStringLiteral("name")] = name; - import[QStringLiteral("type")] = QStringLiteral("module"); - import[QStringLiteral("version")] = code.mid(importNode->versionToken.offset, importNode->versionToken.length); + import[nameLiteral()] = name; + import[typeLiteral()] = QStringLiteral("module"); + import[versionLiteral()] = code.mid(importNode->versionToken.offset, importNode->versionToken.length); } imports.append(import); @@ -147,10 +155,10 @@ QVariantMap pluginsForModulePath(const QString &modulePath) { } while (line.length() > 0); QVariantMap pluginInfo; - pluginInfo[QStringLiteral("plugins")] = plugins.simplified(); - pluginInfo[QStringLiteral("classnames")] = classnames.simplified(); + pluginInfo[pluginsLiteral()] = plugins.simplified(); + pluginInfo[classnamesLiteral()] = classnames.simplified(); if (dependencies.length()) - pluginInfo[QStringLiteral("dependencies")] = dependencies; + pluginInfo[dependenciesLiteral()] = dependencies; return pluginInfo; } @@ -210,25 +218,25 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) for (int i = 0; i < importsCopy.length(); ++i) { QVariantMap import = qvariant_cast(importsCopy[i]); - if (import[QStringLiteral("type")] == QLatin1String("module")) { - QString path = resolveImportPath(import.value(QStringLiteral("name")).toString(), import.value(QStringLiteral("version")).toString()); + if (import[typeLiteral()] == QLatin1String("module")) { + QString path = resolveImportPath(import.value(nameLiteral()).toString(), import.value(versionLiteral()).toString()); if (!path.isEmpty()) - import[QStringLiteral("path")] = path; - QVariantMap plugininfo = pluginsForModulePath(import.value(QStringLiteral("path")).toString()); - QString plugins = plugininfo.value(QStringLiteral("plugins")).toString(); - QString classnames = plugininfo.value(QStringLiteral("classnames")).toString(); + import[pathLiteral()] = path; + QVariantMap plugininfo = pluginsForModulePath(import.value(pathLiteral()).toString()); + QString plugins = plugininfo.value(pluginsLiteral()).toString(); + QString classnames = plugininfo.value(classnamesLiteral()).toString(); if (!plugins.isEmpty()) - import[QStringLiteral("plugin")] = plugins; + import.insert(QStringLiteral("plugin"), plugins); if (!classnames.isEmpty()) - import[QStringLiteral("classname")] = classnames; - if (plugininfo.contains(QStringLiteral("dependencies"))) { - QStringList dependencies = plugininfo.value(QStringLiteral("dependencies")).toStringList(); + import.insert(QStringLiteral("classname"), classnames); + if (plugininfo.contains(dependenciesLiteral())) { + QStringList dependencies = plugininfo.value(dependenciesLiteral()).toStringList(); foreach (const QString &line, dependencies) { const auto dep = line.splitRef(QLatin1Char(' ')); QVariantMap depImport; - depImport[QStringLiteral("type")] = QStringLiteral("module"); - depImport[QStringLiteral("name")] = dep[0].toString(); - depImport[QStringLiteral("version")] = dep[1].toString(); + depImport[typeLiteral()] = QStringLiteral("module"); + depImport[nameLiteral()] = dep[0].toString(); + depImport[versionLiteral()] = dep[1].toString(); importsCopy.append(depImport); } } @@ -277,8 +285,8 @@ struct ImportCollector : public QQmlJS::Directives virtual void importFile(const QString &jsfile, const QString &module, int line, int column) { QVariantMap entry; - entry[QLatin1String("type")] = QStringLiteral("javascript"); - entry[QLatin1String("path")] = jsfile; + entry[typeLiteral()] = QStringLiteral("javascript"); + entry[pathLiteral()] = jsfile; imports << entry; Q_UNUSED(module); @@ -290,12 +298,12 @@ struct ImportCollector : public QQmlJS::Directives { QVariantMap entry; if (uri.contains(QLatin1Char('/'))) { - entry[QLatin1String("type")] = QStringLiteral("directory"); - entry[QLatin1String("name")] = uri; + entry[typeLiteral()] = QStringLiteral("directory"); + entry[nameLiteral()] = uri; } else { - entry[QLatin1String("type")] = QStringLiteral("module"); - entry[QLatin1String("name")] = uri; - entry[QLatin1String("version")] = version; + entry[typeLiteral()] = QStringLiteral("module"); + entry[nameLiteral()] = uri; + entry[versionLiteral()] = version; } imports << entry; @@ -424,8 +432,8 @@ QSet importModulePaths(QVariantList imports) { QSet ret; foreach (const QVariant &importVariant, imports) { QVariantMap import = qvariant_cast(importVariant); - QString path = import.value(QStringLiteral("path")).toString(); - QString type = import.value(QStringLiteral("type")).toString(); + QString path = import.value(pathLiteral()).toString(); + QString type = import.value(typeLiteral()).toString(); if (type == QLatin1String("module") && !path.isEmpty()) ret.insert(QDir(path).canonicalPath()); } -- cgit v1.2.3 From 2a4f543ca2b0f90262184fa97f9c386737ec6dd1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 17 Aug 2016 14:58:40 +0200 Subject: QML: Do not register dependencies of deleted binding Because it can lead to a use-after-free. Change-Id: I6701b370c0ecee4967e5f749f673a6f9ee3d504c Reviewed-by: Simon Hausmann --- src/qml/qml/qqmljavascriptexpression.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 5938ebf5d7..cd2f120218 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -296,7 +296,7 @@ void QQmlPropertyCapture::registerQmlDependencies(QV4::ExecutionEngine *engine, if (!ep) return; QQmlPropertyCapture *capture = ep->propertyCapture; - if (!capture) + if (!capture || capture->watcher->wasDeleted()) return; QV4::Scope scope(engine); -- cgit v1.2.3 From 32a980a86257021247a742b2b8c0b4aafe6e9035 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 17 Aug 2016 11:02:28 +0200 Subject: Revert "localstorage: disable warning about tautological comparison" After 12c7f22, the comparison should now be done at compile time. This reverts commit a858ce0039cb63a6a0afdfabab80ad4adc98ce17. Task-number: QTBUG-53373 Change-Id: I79d2c8aaabba8a2676df6da64206fefc9cdef3b2 Reviewed-by: Marc Mutz Reviewed-by: Thiago Macieira --- src/imports/localstorage/plugin.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index fbcb6ddea5..a043af6b46 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -149,12 +149,7 @@ public: using namespace QV4; -QT_WARNING_PUSH -#if (Q_CC_GNU >= 600) -QT_WARNING_DISABLE_GCC("-Wtautological-compare") -#endif DEFINE_OBJECT_VTABLE(QV4::QQmlSqlDatabaseWrapper); -QT_WARNING_POP -- cgit v1.2.3 From b5f9c1e6e7f3a6881874e4ec2f43cea68107a06b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 8 Aug 2016 11:58:30 +0200 Subject: QML: Remove hasAccessors flag from QQmlPropertyRawData We can now always check if the pointer is null (no accessors) or not. Change-Id: Ie9abf2f8930ea1f75a6d637a47f7f9c4fbab1151 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache.cpp | 1 - src/qml/qml/qqmlpropertycache_p.h | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 7cb1425725..5c53e342f3 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -678,7 +678,6 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision() == 0)); if (accessorProperty) { - data->_flags.hasAccessors = true; data->setAccessors(accessorProperty->accessors); } else if (old) { data->markAsOverrideOf(old); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 480f1dcf40..63a9c63d90 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -108,7 +108,6 @@ public: unsigned isFinal : 1; // Has FINAL flag unsigned isOverridden : 1; // Is overridden by a extension property unsigned isDirect : 1; // Exists on a C++ QMetaObject - unsigned hasAccessors : 1; // Has property accessors unsigned type : 4; // stores an entry of Types @@ -127,7 +126,7 @@ public: unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved unsigned overrideIndexIsProperty: 1; - unsigned _padding : 9; // align to 32 bits + unsigned _padding : 10; // align to 32 bits inline Flags(); inline bool operator==(const Flags &other) const; @@ -147,7 +146,7 @@ public: bool isFinal() const { return _flags.isFinal; } bool isOverridden() const { return _flags.isOverridden; } bool isDirect() const { return _flags.isDirect; } - bool hasAccessors() const { return _flags.hasAccessors; } + bool hasAccessors() const { return accessors() != nullptr; } bool isFunction() const { return _flags.type == Flags::FunctionType; } bool isQObject() const { return _flags.type == Flags::QObjectDerivedType; } bool isEnum() const { return _flags.type == Flags::EnumType; } @@ -576,7 +575,6 @@ QQmlPropertyRawData::Flags::Flags() , isFinal(false) , isOverridden(false) , isDirect(false) - , hasAccessors(false) , type(OtherType) , isVMEFunction(false) , hasArguments(false) @@ -600,7 +598,6 @@ bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &ot isAlias == other.isAlias && isFinal == other.isFinal && isOverridden == other.isOverridden && - hasAccessors == other.hasAccessors && type == other.type && isVMEFunction == other.isVMEFunction && hasArguments == other.hasArguments && -- cgit v1.2.3 From 96bd2337a98c892b5def555163c491507dce7185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Palom=C3=A4ki?= Date: Wed, 21 Oct 2015 22:13:19 +0300 Subject: Flickable: Fixed rounding errors with contentX/Y contentX/Y are qreals, but they are rounded using qRound/qFloor/qCeil which will limit the values to 2^31 needlessly. This fix will use (std::)round, std::floor and std::ceil instead to allow bigger values for contentX and contentY. Change-Id: I35ad4bcfa3b8bbc21e90768d348d3002ca400081 Task-number: QTBUG-48018 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickflickable.cpp | 35 ++++++++++++++++------ .../auto/quick/qquickflickable/data/contentXY.qml | 6 ++++ .../quick/qquickflickable/tst_qquickflickable.cpp | 21 +++++++++++++ 3 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 tests/auto/quick/qquickflickable/data/contentXY.qml diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 31581c0f07..0333f6b9bd 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -55,6 +55,8 @@ #include #include "qplatformdefs.h" +#include + QT_BEGIN_NAMESPACE // FlickThreshold determines how far the "mouse" must have moved @@ -69,6 +71,21 @@ static const int RetainGrabVelocity = 100; static const int MovementEndingTimerInterval = 100; #endif +// Currently std::round can't be used on Android when using ndk g++, so +// use C version instead. We could just define two versions of Round, one +// for float and one for double, but then only one of them would be used +// and compiler would trigger a warning about unused function. +// +// See https://code.google.com/p/android/issues/detail?id=54418 +template +static T Round(T t) { + return round(t); +} +template<> +Q_DECL_UNUSED float Round(float f) { + return roundf(f); +} + static qreal EaseOvershoot(qreal t) { return qAtan(t); } @@ -351,7 +368,7 @@ bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExt qreal dist = v2 / (accel * 2.0); if (v > 0) dist = -dist; - qreal target = -qRound(-(data.move.value() - dist)); + qreal target = -Round(-(data.move.value() - dist)); dist = -target + data.move.value(); accel = v2 / (2.0f * qAbs(dist)); @@ -455,18 +472,18 @@ void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExt } else if (data.move.value() <= maxExtent) { resetTimeline(data); adjustContentPos(data, maxExtent); - } else if (-qRound(-data.move.value()) != data.move.value()) { + } else if (-Round(-data.move.value()) != data.move.value()) { // We could animate, but since it is less than 0.5 pixel it's probably not worthwhile. resetTimeline(data); qreal val = data.move.value(); - if (qAbs(-qRound(-val) - val) < 0.25) // round small differences - val = -qRound(-val); + if (std::abs(-Round(-val) - val) < 0.25) // round small differences + val = -Round(-val); else if (data.smoothVelocity.value() > 0) // continue direction of motion for larger - val = -qFloor(-val); + val = -std::floor(-val); else if (data.smoothVelocity.value() < 0) - val = -qCeil(-val); + val = -std::ceil(-val); else // otherwise round - val = -qRound(-val); + val = -Round(-val); timeline.set(data.move, val); } data.inOvershoot = false; @@ -1553,12 +1570,12 @@ void QQuickFlickablePrivate::replayDelayedPress() //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned. void QQuickFlickablePrivate::setViewportX(qreal x) { - contentItem->setX(pixelAligned ? -qRound(-x) : x); + contentItem->setX(pixelAligned ? -Round(-x) : x); } void QQuickFlickablePrivate::setViewportY(qreal y) { - contentItem->setY(pixelAligned ? -qRound(-y) : y); + contentItem->setY(pixelAligned ? -Round(-y) : y); } void QQuickFlickable::timerEvent(QTimerEvent *event) diff --git a/tests/auto/quick/qquickflickable/data/contentXY.qml b/tests/auto/quick/qquickflickable/data/contentXY.qml new file mode 100644 index 0000000000..8215976949 --- /dev/null +++ b/tests/auto/quick/qquickflickable/data/contentXY.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +Flickable { + width: 400; height: 400 + contentWidth: 1e11; contentHeight: 1e11 +} diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index e1678b9acd..a03e3b8170 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -91,6 +91,7 @@ private slots: void cleanup(); void contentSize(); void ratios_smallContent(); + void contentXYNotTruncatedToInt(); private: void flickWithTouch(QQuickWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to); @@ -1841,6 +1842,26 @@ void tst_qquickflickable::ratios_smallContent() QCOMPARE(obj->property("widthRatioIs").toDouble(), 1.); } +// QTBUG-48018 +void tst_qquickflickable::contentXYNotTruncatedToInt() +{ + QScopedPointer window(new QQuickView); + window->setSource(testFileUrl("contentXY.qml")); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + QQuickFlickable *flickable = qobject_cast(window->rootObject()); + QVERIFY(flickable); + + flickable->setContentX(1e10); + flick(window.data(), QPoint(200, 100), QPoint(100, 100), 50); + + // make sure we are not clipped at 2^31 + QVERIFY(flickable->contentX() > qreal(1e10)); +} QTEST_MAIN(tst_qquickflickable) -- cgit v1.2.3 From 4a9635b0bfdc6a189375bc48e68b00bd05e15c82 Mon Sep 17 00:00:00 2001 From: Frederik Schwarzer Date: Wed, 10 Aug 2016 15:49:43 +0200 Subject: Remove some double-meaning text from documentation Change-Id: I9b69dbe929947795bdfbff4e0e3a16a47fa94197 Reviewed-by: Simon Hausmann --- src/qml/doc/src/cppintegration/exposecppattributes.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc index c7e4930bfb..f5aea0b01a 100644 --- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc +++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc @@ -73,7 +73,7 @@ convert them as appropriately when used from QML. Additionally, C++ classes that are \l{Registering C++ types with the QML type system}{registered} with the QML type system can be can be used as data types, as can their enums if appropriately registered. See \l{qtqml-cppintegration-data.html}{Data Type -Conversion Between QML and C++} for details for further information. +Conversion Between QML and C++} for further information. Additionally, data ownership rules are taken into consideration when data is transferred from C++ to QML. See \l {Data Ownership} for more details. -- cgit v1.2.3 From 79cdb4bd45944cce9bb389bebf49917775898ca2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 18 Aug 2016 16:08:54 +0200 Subject: Remove unused delete watcher class Change-Id: I8ccb3ae9029d17dcb4d60239d225393b1da88993 Reviewed-by: Erik Verbruggen --- src/qml/qml/ftw/ftw.pri | 1 - src/qml/qml/ftw/qdeletewatcher_p.h | 111 ------------------------------------- src/qml/qml/qqmlexpression_p.h | 1 - 3 files changed, 113 deletions(-) delete mode 100644 src/qml/qml/ftw/qdeletewatcher_p.h diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri index 2d4a82e2f4..bb2374d1fa 100644 --- a/src/qml/qml/ftw/ftw.pri +++ b/src/qml/qml/ftw/ftw.pri @@ -8,7 +8,6 @@ HEADERS += \ $$PWD/qqmlthread_p.h \ $$PWD/qfinitestack_p.h \ $$PWD/qrecursionwatcher_p.h \ - $$PWD/qdeletewatcher_p.h \ $$PWD/qrecyclepool_p.h \ $$PWD/qflagpointer_p.h \ $$PWD/qlazilyallocated_p.h \ diff --git a/src/qml/qml/ftw/qdeletewatcher_p.h b/src/qml/qml/ftw/qdeletewatcher_p.h deleted file mode 100644 index d4c0c6dfb2..0000000000 --- a/src/qml/qml/ftw/qdeletewatcher_p.h +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 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$ -** -****************************************************************************/ - -#ifndef QDELETEWATCHER_P_H -#define QDELETEWATCHER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -QT_BEGIN_NAMESPACE - -class QDeleteWatchable -{ -public: - inline QDeleteWatchable(); - inline ~QDeleteWatchable(); -private: - friend class QDeleteWatcher; - bool *_w; -}; - -class QDeleteWatcher { -public: - inline QDeleteWatcher(QDeleteWatchable *data); - inline ~QDeleteWatcher(); - inline bool wasDeleted() const; -private: - void *operator new(size_t); - bool *_w; - bool _s; - QDeleteWatchable *m_d; -}; - -QDeleteWatchable::QDeleteWatchable() -: _w(0) -{ -} - -QDeleteWatchable::~QDeleteWatchable() -{ - if (_w) *_w = true; -} - -QDeleteWatcher::QDeleteWatcher(QDeleteWatchable *data) -: _s(false), m_d(data) -{ - if (!m_d->_w) - m_d->_w = &_s; - _w = m_d->_w; -} - -QDeleteWatcher::~QDeleteWatcher() -{ - if (false == *_w && &_s == m_d->_w) - m_d->_w = 0; -} - -bool QDeleteWatcher::wasDeleted() const -{ - return *_w; -} - -QT_END_NAMESPACE - -#endif // QDELETEWATCHER_P_H diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index 741c25e206..809a57b169 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -56,7 +56,6 @@ #include #include #include -#include #include QT_BEGIN_NAMESPACE -- cgit v1.2.3 From 863a76281cefd19d7e339a78a63c86dff0f9dcce Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 18 Aug 2016 13:42:40 -0700 Subject: Have proper OpenGL checks in QQuickFBO and image particles These use custom materials that can crash when running with the D3D12 backend. We prefer handling such situations gracefully, with the application surviving. Therefore check the backend in use, and skip creating a scenegraph node when the backend is not OpenGL. Task-number: QTBUG-55353 Change-Id: I0be326fd2eacb0be604a0f111fa916558376c75a Reviewed-by: Friedemann Kleint --- src/particles/qquickimageparticle.cpp | 10 ++++++++++ src/quick/items/qquickframebufferobject.cpp | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp index 07cbee1383..c68153aca8 100644 --- a/src/particles/qquickimageparticle.cpp +++ b/src/particles/qquickimageparticle.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -1469,8 +1470,17 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node) update(); } +static inline bool isOpenGL(QSGRenderContext *rc) +{ + QSGRendererInterface *rif = rc->sceneGraphContext()->rendererInterface(rc); + return !rif || rif->graphicsApi() == QSGRendererInterface::OpenGL; +} + QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) { + if (!node && !isOpenGL(QQuickItemPrivate::get(this)->sceneGraphRenderContext())) + return 0; + if (m_pleaseReset){ if (node) delete node; diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp index d8147a48a5..857cd44b15 100644 --- a/src/quick/items/qquickframebufferobject.cpp +++ b/src/quick/items/qquickframebufferobject.cpp @@ -44,6 +44,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -260,6 +261,12 @@ public: int devicePixelRatio; }; +static inline bool isOpenGL(QSGRenderContext *rc) +{ + QSGRendererInterface *rif = rc->sceneGraphContext()->rendererInterface(rc); + return !rif || rif->graphicsApi() == QSGRendererInterface::OpenGL; +} + /*! * \internal */ @@ -278,6 +285,8 @@ QSGNode *QQuickFramebufferObject::updatePaintNode(QSGNode *node, UpdatePaintNode Q_D(QQuickFramebufferObject); if (!n) { + if (!isOpenGL(d->sceneGraphRenderContext())) + return 0; if (!d->node) d->node = new QSGFramebufferObjectNode; n = d->node; @@ -360,6 +369,8 @@ QSGTextureProvider *QQuickFramebufferObject::textureProvider() const qWarning("QQuickFramebufferObject::textureProvider: can only be queried on the rendering thread of an exposed window"); return 0; } + if (!isOpenGL(d->sceneGraphRenderContext())) + return 0; if (!d->node) d->node = new QSGFramebufferObjectNode; return d->node; -- cgit v1.2.3 From be846890f3b34343c139baa99e10d0f9a25fcea3 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 19 Aug 2016 09:31:50 +0200 Subject: Adjust decarative after qtConfig changes in qtbase Most of the changes are optional, but cleanup our QT_CONFIG usage. Change-Id: I5253d53f72f6fb03c2cfedae1e17d94f424a6bbb Reviewed-by: Oswald Buddenhagen Reviewed-by: Simon Hausmann --- examples/quick/quick.pro | 2 +- examples/quick/scenegraph/scenegraph.pro | 2 +- src/imports/imports.pro | 6 ++---- src/qml/qml/ftw/ftw.pri | 2 +- src/quick/items/items.pri | 2 +- src/quick/quick.pro | 2 +- src/quick/scenegraph/scenegraph.pri | 10 ++++------ src/src.pro | 3 +-- tests/auto/auto.pro | 2 +- tests/auto/particles/particles.pro | 3 +-- tests/auto/qml/debugger/debugger.pro | 4 ++-- tests/auto/qml/qml.pro | 3 +-- tests/auto/qmldevtools/compile/compile.pro | 2 +- tests/auto/qmldevtools/qmldevtools.pro | 6 ++---- tests/auto/quick/quick.pro | 6 +++--- tests/benchmarks/benchmarks.pro | 4 ++-- tools/tools.pro | 2 +- 17 files changed, 26 insertions(+), 35 deletions(-) diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro index b164bf4f5b..445dfb0fab 100644 --- a/examples/quick/quick.pro +++ b/examples/quick/quick.pro @@ -27,7 +27,7 @@ SUBDIRS = quick-accessibility \ demos #OpenGL Support Required -contains(QT_CONFIG, opengl(es1|es2)?) { +qtConfig(opengl(es1|es2)?) { SUBDIRS += \ textureprovider \ rendercontrol diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro index 1015d7be3d..e13e8198b0 100644 --- a/examples/quick/scenegraph/scenegraph.pro +++ b/examples/quick/scenegraph/scenegraph.pro @@ -1,6 +1,6 @@ TEMPLATE = subdirs -contains(QT_CONFIG, opengl(es1|es2)?) { +qtConfig(opengl(es1|es2)?) { SUBDIRS += \ graph \ simplematerial \ diff --git a/src/imports/imports.pro b/src/imports/imports.pro index 9e3cdf3f42..affc1db2c0 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -16,10 +16,8 @@ qtHaveModule(quick) { window \ testlib - contains(QT_CONFIG, opengl(es1|es2)?) { - SUBDIRS += \ - particles - } + qtConfig(opengl(es1|es2)?): \ + SUBDIRS += particles } qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri index bb2374d1fa..87d80d04bc 100644 --- a/src/qml/qml/ftw/ftw.pri +++ b/src/qml/qml/ftw/ftw.pri @@ -21,4 +21,4 @@ SOURCES += \ # mirrors logic in $$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri # clock_gettime() is implemented in librt on these systems -contains(QT_CONFIG, clock-gettime):linux-*|hpux-*|solaris-*:LIBS_PRIVATE *= -lrt +qtConfig(clock-gettime):linux-*|hpux-*|solaris-*:LIBS_PRIVATE *= -lrt diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index beaf3540bc..d91705451e 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -147,7 +147,7 @@ SOURCES += \ $$PWD/qquickanimatedsprite.cpp # Items that depend on OpenGL Renderer -contains(QT_CONFIG, opengl(es1|es2)?) { +qtConfig(opengl(es1|es2)?) { SOURCES += \ $$PWD/qquickopenglinfo.cpp \ $$PWD/qquickopenglshadereffect.cpp \ diff --git a/src/quick/quick.pro b/src/quick/quick.pro index f74a554aa9..4909f7fce8 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -33,7 +33,7 @@ include(util/util.pri) include(scenegraph/scenegraph.pri) include(items/items.pri) include(designer/designer.pri) -contains(QT_CONFIG, accessibility) { +qtConfig(accessibility) { include(accessible/accessible.pri) } diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 4ef8b0f638..a659ca5e10 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -1,5 +1,3 @@ -!contains(QT_CONFIG, egl):DEFINES += QT_NO_EGL - # DEFINES += QSG_SEPARATE_INDEX_BUFFER # DEFINES += QSG_DISTANCEFIELD_CACHE_DEBUG @@ -29,7 +27,7 @@ SOURCES += \ $$PWD/coreapi/qsgrendernode.cpp \ $$PWD/coreapi/qsgrendererinterface.cpp -contains(QT_CONFIG, opengl(es1|es2)?) { +qtConfig(opengl(es1|es2)?) { HEADERS += \ $$PWD/coreapi/qsgbatchrenderer_p.h SOURCES += \ @@ -73,7 +71,7 @@ SOURCES += \ $$PWD/util/qsgimagenode.cpp \ $$PWD/util/qsgninepatchnode.cpp -contains(QT_CONFIG, opengl(es1|es2)?) { +qtConfig(opengl(es1|es2)?) { HEADERS += \ $$PWD/util/qsgdepthstencilbuffer_p.h \ $$PWD/util/qsgshadersourcebuilder_p.h \ @@ -104,7 +102,7 @@ SOURCES += \ $$PWD/qsgbasicglyphnode.cpp \ $$PWD/qsgrenderloop.cpp -contains(QT_CONFIG, opengl(es1|es2)?) { +qtConfig(opengl(es1|es2)?) { SOURCES += \ $$PWD/qsgdefaultglyphnode.cpp \ $$PWD/qsgdefaultglyphnode_p.cpp \ @@ -150,7 +148,7 @@ RESOURCES += \ $$PWD/scenegraph.qrc # OpenGL Shaders -contains(QT_CONFIG, opengl(es1|es2)?) { +qtConfig(opengl(es1|es2)?) { OTHER_FILES += \ $$PWD/shaders/24bittextmask.frag \ $$PWD/shaders/8bittextmask.frag \ diff --git a/src/src.pro b/src/src.pro index fbc4feaec4..fc1cea67cd 100644 --- a/src/src.pro +++ b/src/src.pro @@ -5,9 +5,8 @@ SUBDIRS += \ quick \ qmltest -qtHaveModule(gui):contains(QT_CONFIG, opengl(es1|es2)?) { +qtHaveModule(gui):qtConfig(opengl(es1|es2)?): \ SUBDIRS += particles -} qtHaveModule(gui): qtHaveModule(widgets): SUBDIRS += quickwidgets diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index b72a43d742..7b13cb2b6d 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -8,7 +8,7 @@ SUBDIRS=\ installed_cmake \ toolsupport -qtHaveModule(gui):contains(QT_CONFIG, opengl(es1|es2)?) { +qtHaveModule(gui):qtConfig(opengl(es1|es2)?) { SUBDIRS += particles qtHaveModule(widgets): SUBDIRS += quickwidgets diff --git a/tests/auto/particles/particles.pro b/tests/auto/particles/particles.pro index 265a1eabc7..6ee1290dbb 100644 --- a/tests/auto/particles/particles.pro +++ b/tests/auto/particles/particles.pro @@ -25,6 +25,5 @@ PRIVATETESTS += \ qquickturbulence \ qquickwander -contains(QT_CONFIG, private_tests) { +qtConfig(private_tests): \ SUBDIRS += $$PRIVATETESTS -} diff --git a/tests/auto/qml/debugger/debugger.pro b/tests/auto/qml/debugger/debugger.pro index ccb2d71c53..a50411e18b 100644 --- a/tests/auto/qml/debugger/debugger.pro +++ b/tests/auto/qml/debugger/debugger.pro @@ -20,6 +20,6 @@ PRIVATETESTS += \ SUBDIRS += $$PUBLICTESTS -contains(QT_CONFIG, private_tests) { +qtConfig(private_tests): \ SUBDIRS += $$PRIVATETESTS -} + diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index a1daa7a0c4..fae1b6f58b 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -79,9 +79,8 @@ SUBDIRS += $$METATYPETESTS SUBDIRS += qmllint } -contains(QT_CONFIG, private_tests) { +qtConfig(private_tests): \ SUBDIRS += $$PRIVATETESTS -} qtNomakeTools( \ qmlplugindump \ diff --git a/tests/auto/qmldevtools/compile/compile.pro b/tests/auto/qmldevtools/compile/compile.pro index 54430eb668..832700698f 100644 --- a/tests/auto/qmldevtools/compile/compile.pro +++ b/tests/auto/qmldevtools/compile/compile.pro @@ -5,7 +5,7 @@ force_bootstrap { !build_pass: CONFIG += release } else { QT = core - !build_pass:contains(QT_CONFIG, debug_and_release): CONFIG += release + !build_pass:qtConfig(debug_and_release): CONFIG += release } QT += qmldevtools-private macx:CONFIG -= app_bundle diff --git a/tests/auto/qmldevtools/qmldevtools.pro b/tests/auto/qmldevtools/qmldevtools.pro index a0ca1bff87..a9352d4df3 100644 --- a/tests/auto/qmldevtools/qmldevtools.pro +++ b/tests/auto/qmldevtools/qmldevtools.pro @@ -1,6 +1,4 @@ TEMPLATE = subdirs -contains(QT_CONFIG, private_tests) { - SUBDIRS += \ - compile -} +qtConfig(private_tests): \ + SUBDIRS += compile diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 70e5b8ef6a..c7ba4de86c 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -4,7 +4,7 @@ PUBLICTESTS += \ geometry \ qquickpixmapcache -contains(QT_CONFIG, opengl(es1|es2)?) { +qtConfig(opengl(es1|es2)?) { PUBLICTESTS += \ rendernode qtHaveModule(widgets): PUBLICTESTS += nodes @@ -88,9 +88,9 @@ QUICKTESTS = \ SUBDIRS += $$PUBLICTESTS -!contains(QT_CONFIG, accessibility):QUICKTESTS -= qquickaccessible +!qtConfig(accessibility):QUICKTESTS -= qquickaccessible -contains(QT_CONFIG, private_tests) { +qtConfig(private_tests) { SUBDIRS += $$PRIVATETESTS SUBDIRS += $$QUICKTESTS } diff --git a/tests/benchmarks/benchmarks.pro b/tests/benchmarks/benchmarks.pro index bd071ecf5c..5e6bc65815 100644 --- a/tests/benchmarks/benchmarks.pro +++ b/tests/benchmarks/benchmarks.pro @@ -1,5 +1,5 @@ TEMPLATE = subdirs SUBDIRS = qml script -contains(QT_CONFIG, private_tests) { - contains(QT_CONFIG, opengl(es1|es2)?):SUBDIRS += particles +qtConfig(private_tests) { + qtConfig(opengl(es1|es2)?):SUBDIRS += particles } diff --git a/tools/tools.pro b/tools/tools.pro index 18bfe28a8a..5f72588a7e 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -23,7 +23,7 @@ qmlimportscanner.CONFIG = host_build qtHaveModule(widgets): SUBDIRS += qmleasing } qtHaveModule(qmltest): SUBDIRS += qmltestrunner - contains(QT_CONFIG, private_tests): SUBDIRS += qmljs + qtConfig(private_tests): SUBDIRS += qmljs } qml.depends = qmlimportscanner -- cgit v1.2.3 From 01a9c006b0cb3f9ec7c140d25e6bfa3ca6250a4c Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 19 Aug 2016 10:01:30 +0200 Subject: TestCase::mouseDrag: set mouse move delay >= 1 ms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Infinite-speed drags do not work well with velocity-sensitive components like Flickable. As with change d04982dc on qtbase, adding a short delay helps to stabilize tests. To keep it flexible, we make QTest::defaultMouseDelay() available via the qtest_events.defaultMouseDelay property. So the delay can be increased by passing a larger delay value to mouseDrag, or by changing the QTEST_MOUSEEVENT_DELAY environment variable (as before). Task-number: QTBUG-55382 Change-Id: I8f8088758a206be104a439ee0d1832eeca574e8c Reviewed-by: Liang Qi Reviewed-by: Jan Arve Sæther --- src/imports/testlib/TestCase.qml | 9 +++++---- src/qmltest/quicktestevent.cpp | 13 +++++++++---- src/qmltest/quicktestevent_p.h | 2 ++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index c736a1a93a..46a395308d 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -1047,6 +1047,7 @@ Item { modifiers = Qt.NoModifier if (delay == undefined) delay = -1 + var moveDelay = Math.max(1, delay === -1 ? qtest_events.defaultMouseDelay : delay) // Divide dx and dy to have intermediate mouseMove while dragging // Fractions of dx/dy need be superior to the dragThreshold @@ -1060,12 +1061,12 @@ Item { mousePress(item, x, y, button, modifiers, delay) //trigger dragging - mouseMove(item, x + util.dragThreshold + 1, y + util.dragThreshold + 1, delay, button) + mouseMove(item, x + util.dragThreshold + 1, y + util.dragThreshold + 1, moveDelay, button) if (ddx > 0 || ddy > 0) { - mouseMove(item, x + ddx, y + ddy, delay, button) - mouseMove(item, x + 2*ddx, y + 2*ddy, delay, button) + mouseMove(item, x + ddx, y + ddy, moveDelay, button) + mouseMove(item, x + 2*ddx, y + 2*ddy, moveDelay, button) } - mouseMove(item, x + dx, y + dy, delay, button) + mouseMove(item, x + dx, y + dy, moveDelay, button) mouseRelease(item, x + dx, y + dy, button, modifiers, delay) } diff --git a/src/qmltest/quicktestevent.cpp b/src/qmltest/quicktestevent.cpp index cfa80c4f19..e71b4f8f54 100644 --- a/src/qmltest/quicktestevent.cpp +++ b/src/qmltest/quicktestevent.cpp @@ -39,6 +39,10 @@ QT_BEGIN_NAMESPACE +namespace QTest { + extern int Q_TESTLIB_EXPORT defaultMouseDelay(); +} + QuickTestEvent::QuickTestEvent(QObject *parent) : QObject(parent) { @@ -48,6 +52,11 @@ QuickTestEvent::~QuickTestEvent() { } +int QuickTestEvent::defaultMouseDelay() const +{ + return QTest::defaultMouseDelay(); +} + bool QuickTestEvent::keyPress(int key, int modifiers, int delay) { QWindow *window = activeWindow(); @@ -105,10 +114,6 @@ bool QuickTestEvent::keyClickChar(const QString &character, int modifiers, int d return true; } -namespace QTest { - extern int Q_TESTLIB_EXPORT defaultMouseDelay(); -}; - namespace QtQuickTest { enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDoubleClick, MouseMove, MouseDoubleClickSequence }; diff --git a/src/qmltest/quicktestevent_p.h b/src/qmltest/quicktestevent_p.h index 0cba644cba..0a08347db8 100644 --- a/src/qmltest/quicktestevent_p.h +++ b/src/qmltest/quicktestevent_p.h @@ -53,9 +53,11 @@ QT_BEGIN_NAMESPACE class Q_QUICK_TEST_EXPORT QuickTestEvent : public QObject { Q_OBJECT + Q_PROPERTY(int defaultMouseDelay READ defaultMouseDelay FINAL) public: QuickTestEvent(QObject *parent = 0); ~QuickTestEvent(); + int defaultMouseDelay() const; public Q_SLOTS: bool keyPress(int key, int modifiers, int delay); -- cgit v1.2.3 From b8f145547e5477cd67f464c2d834f60a84f4aad6 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 19 Aug 2016 14:42:31 +0200 Subject: Fix race-condition crash on shut-down in QtWebEngine If a QQuickWidget is somehow deleted below a QApplication post-routine it may end up being deleted after QQuickRenderControlPrivate::cleanup(), which means its QSGContext is deleted. Change-Id: I396d236f997b7b68a96f8fdddd7d6c3fe31c10b0 Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/qsgcontext_p.h | 3 ++- src/quick/scenegraph/qsgdefaultrendercontext.cpp | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 43cf1c28ab..899278843e 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -139,7 +139,8 @@ public Q_SLOTS: void textureFactoryDestroyed(QObject *o); protected: - QSGContext *m_sg; + // Hold m_sg with QPointer in the rare case it gets deleted before us. + QPointer m_sg; QMutex m_mutex; QHash m_textures; diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 4fcc81fb18..6f10611ba3 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -70,6 +70,9 @@ QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context) */ void QSGDefaultRenderContext::initialize(void *context) { + if (!m_sg) + return; + QOpenGLContext *openglContext = static_cast(context); QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); @@ -163,7 +166,8 @@ void QSGDefaultRenderContext::invalidate() m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant()); m_gl = 0; - m_sg->renderContextInvalidated(this); + if (m_sg) + m_sg->renderContextInvalidated(this); emit invalidated(); } -- cgit v1.2.3 From bf3b596066af733c04b5ed3ef2dc9ec753a41e79 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 19 Aug 2016 17:21:33 +0300 Subject: QQuickItemView: use reverse iterators more Since qt5.6 we can use reverse iterators. Change-Id: Ibf78b937e793c868ecc40710ef74c25308cc39bf Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitemview.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index fa874d631c..e017d6564a 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -1622,12 +1622,10 @@ qreal QQuickItemViewPrivate::contentStartOffset() const int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const { - if (visibleItems.count()) { - int i = visibleItems.count() - 1; - while (i > 0 && visibleItems.at(i)->index == -1) - --i; - if (visibleItems.at(i)->index != -1) - return visibleItems.at(i)->index; + for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) { + auto item = *it; + if (item->index != -1) + return item->index; } return defaultValue; } @@ -1658,9 +1656,10 @@ FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const { int QQuickItemViewPrivate::findLastIndexInView() const { const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size(); - for (int i=visibleItems.count() - 1; i>=0; i--) { - if (visibleItems.at(i)->position() <= viewEndPos && visibleItems.at(i)->index != -1) - return visibleItems.at(i)->index; + for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) { + auto item = *it; + if (item->index != -1 && item->position() <= viewEndPos) + return item->index; } return -1; } -- cgit v1.2.3 From 23527754d60780ac4830f1acd6a54d3487a2c362 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 1 Aug 2016 13:39:37 +0200 Subject: Fix crash in tst_qqmlextensionplugin on shutdown On shutdown the test will unload all the plugins it loaded. In the case of the QtQuick2 plugin we only loaded it but never called registerTypes, as the test merely sees if the plugin can be instantiated. Consequently the attempt at unregistering the value type providers will fail because it was previously never defined. Note: We can't just let the QQmlValueTypeProvider destructor take care of the deregistration, as we do not truly unload plugins anymore (thankfully). However the plugin object will be destroyed and along with it we should correctly de-register the things we registered earlier, otherwise when initializing the plugin again, we'll register handers multiple times. Change-Id: Id778890bcd3f1fab16eb312a01de7d423ea3aa22 Reviewed-by: Ulf Hermann --- src/imports/qtquick2/plugin.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/imports/qtquick2/plugin.cpp b/src/imports/qtquick2/plugin.cpp index e56027c1bb..d16467a5bb 100644 --- a/src/imports/qtquick2/plugin.cpp +++ b/src/imports/qtquick2/plugin.cpp @@ -61,13 +61,17 @@ public: { Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick")); Q_UNUSED(uri); + moduleDefined = true; QQmlQtQuick2Module::defineModule(); } ~QtQuick2Plugin() { - QQmlQtQuick2Module::undefineModule(); + if (moduleDefined) + QQmlQtQuick2Module::undefineModule(); } + + bool moduleDefined = false; }; //![class decl] -- cgit v1.2.3 From 25c2cfc143ffbee16acea55394c0d8752beb31ea Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Fri, 19 Aug 2016 00:42:59 -0700 Subject: Exclude tests that require features not present on UIKit platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I0398bf60fa150e9e03822938a2a6bedf280d7750 Reviewed-by: Oswald Buddenhagen Reviewed-by: Tor Arne Vestbø --- tests/auto/auto.pro | 3 +++ tests/auto/qml/qml.pro | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 7b13cb2b6d..556f5ddc7a 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -14,6 +14,9 @@ qtHaveModule(gui):qtConfig(opengl(es1|es2)?) { } +# console applications not supported +uikit: SUBDIRS -= qmltest + qmldevtools.CONFIG = host_build installed_cmake.depends = cmake diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index fae1b6f58b..68a2eace19 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -8,7 +8,6 @@ PUBLICTESTS += \ qjsvalueiterator \ qjsonbinding \ qmlmin \ - qmlplugindump \ qqmlcomponent \ qqmlconsole \ qqmlengine \ @@ -74,9 +73,9 @@ qtHaveModule(widgets) { SUBDIRS += $$PUBLICTESTS \ qqmlextensionplugin SUBDIRS += $$METATYPETESTS -!winrt { # no QProcess on winrt +!uikit:!winrt { # no QProcess on uikit/winrt !contains(QT_CONFIG, no-qml-debug): SUBDIRS += debugger - SUBDIRS += qmllint + SUBDIRS += qmllint qmlplugindump } qtConfig(private_tests): \ -- cgit v1.2.3 From 6ade9e9ed945fff4ff588be6fdd294912addcd2b Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 19 Aug 2016 11:14:31 +0300 Subject: benchmarks: replace 'foreach' with 'range for' Change-Id: Ibdb6daf0e09e01186478602287e2a6a143e09951 Reviewed-by: Shawn Rutledge --- tests/benchmarks/particles/affectors/tst_affectors.cpp | 4 ++-- tests/benchmarks/particles/emission/tst_emission.cpp | 2 +- tests/benchmarks/qml/animation/tst_animation.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/benchmarks/particles/affectors/tst_affectors.cpp b/tests/benchmarks/particles/affectors/tst_affectors.cpp index 475b8d28ec..99d175564b 100644 --- a/tests/benchmarks/particles/affectors/tst_affectors.cpp +++ b/tests/benchmarks/particles/affectors/tst_affectors.cpp @@ -86,7 +86,7 @@ void tst_affectors::test_basic() int stillAlive = 0; QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 1000, 10));//Small simulation variance is permissible. - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -126,7 +126,7 @@ void tst_affectors::test_filtered() int stillAlive = 0; QVERIFY(extremelyFuzzyCompare(system->groupData[1]->size(), 1000, 10));//Small simulation variance is permissible. - foreach (QQuickParticleData *d, system->groupData[1]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/benchmarks/particles/emission/tst_emission.cpp b/tests/benchmarks/particles/emission/tst_emission.cpp index b107120a28..fe08305f68 100644 --- a/tests/benchmarks/particles/emission/tst_emission.cpp +++ b/tests/benchmarks/particles/emission/tst_emission.cpp @@ -79,7 +79,7 @@ void tst_emission::test_basic() int stillAlive = 0; QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 1000, 10));//Small simulation variance is permissible. - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/benchmarks/qml/animation/tst_animation.cpp b/tests/benchmarks/qml/animation/tst_animation.cpp index 4f693f9fa8..59f5a57f5c 100644 --- a/tests/benchmarks/qml/animation/tst_animation.cpp +++ b/tests/benchmarks/qml/animation/tst_animation.cpp @@ -107,8 +107,8 @@ void tst_animation::animationelements_data() { QTest::addColumn("type"); - QSet types = QQmlMetaType::qmlTypeNames().toSet(); - foreach (const QString &type, types) { + const QSet types = QQmlMetaType::qmlTypeNames().toSet(); + for (const QString &type : types) { if (type.contains(QLatin1String("Animation"))) QTest::newRow(type.toLatin1()) << type; } -- cgit v1.2.3 From 19baf625b984b4fafa28c4fc9bb8e5f8f0390d71 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 19 Aug 2016 15:45:00 +0300 Subject: Imports: replace 'foreach' with 'range for' Change-Id: I283ce40b52de2371550dab7a54cdce89c78cdc68 Reviewed-by: Shawn Rutledge --- src/imports/folderlistmodel/fileinfothread.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/imports/folderlistmodel/fileinfothread.cpp b/src/imports/folderlistmodel/fileinfothread.cpp index 5d911eec1e..0b62935f87 100644 --- a/src/imports/folderlistmodel/fileinfothread.cpp +++ b/src/imports/folderlistmodel/fileinfothread.cpp @@ -260,14 +260,13 @@ void FileInfoThread::getFileInfos(const QString &path) sortFlags = sortFlags | QDir::DirsFirst; QDir currentDir(path, QString(), sortFlags); - QFileInfoList fileInfoList; QList filePropertyList; - fileInfoList = currentDir.entryInfoList(nameFilters, filter, sortFlags); + const QFileInfoList fileInfoList = currentDir.entryInfoList(nameFilters, filter, sortFlags); if (!fileInfoList.isEmpty()) { filePropertyList.reserve(fileInfoList.count()); - foreach (const QFileInfo &info, fileInfoList) { + for (const QFileInfo &info : fileInfoList) { //qDebug() << "Adding file : " << info.fileName() << "to list "; filePropertyList << FileProperty(info); } -- cgit v1.2.3 From e923ac48a5bfdef206f6fdf0e194db976f2af8ee Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 19 Aug 2016 13:28:16 +0300 Subject: Particles: fix incorrect usage of 'range for' with Qt containers Change-Id: Ibe750b068bc8d4c33272a65dafcc398239d7d591 Reviewed-by: Shawn Rutledge --- src/particles/qquickparticlepainter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/particles/qquickparticlepainter.cpp b/src/particles/qquickparticlepainter.cpp index d6303eb219..0c2521cd9e 100644 --- a/src/particles/qquickparticlepainter.cpp +++ b/src/particles/qquickparticlepainter.cpp @@ -103,7 +103,8 @@ void QQuickParticlePainter::recalculateGroupIds() const m_groupIdsNeedRecalculation = false; m_groupIds.clear(); - for (const QString &str : groups()) { + const auto groupList = groups(); + for (const QString &str : groupList) { QQuickParticleGroupData::ID groupId = m_system->groupIds.value(str, QQuickParticleGroupData::InvalidID); if (groupId == QQuickParticleGroupData::InvalidID) { // invalid data, not finished setting up, or whatever. Fallback: do not cache. -- cgit v1.2.3 From cd3380862b312a2d349bb72522f0751622448404 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 19 Aug 2016 13:23:37 +0300 Subject: QQuickWidget: fix incorrect usage of 'range for' with Qt containers Change-Id: Ifd95a2076c6f5330820dc0eb4890636f83b93794 Reviewed-by: Shawn Rutledge --- src/quickwidgets/qquickwidget.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index b8c5b900c9..c608697c94 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1603,7 +1603,8 @@ void QQuickWidget::paintEvent(QPaintEvent *event) painter.drawImage(rect(), d->softwareImage); } else { //Paint only the updated areas - for (auto targetRect : d->updateRegion.rects()) { + const auto rects = d->updateRegion.rects(); + for (auto targetRect : rects) { auto sourceRect = QRect(targetRect.topLeft() * devicePixelRatio(), targetRect.size() * devicePixelRatio()); painter.drawImage(targetRect, d->softwareImage, sourceRect); } -- cgit v1.2.3 From e9667491d82985f86ff98426b6948a65af94a611 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 19 Aug 2016 13:03:15 +0300 Subject: Quick: fix incorrect usage of 'range for' with Qt containers Also port remaining foreach to 'range for'. Change-Id: I20296bb3bb6d63f144ebddaba02cabeb16b7d734 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 3 ++- src/quick/items/qquickwindow.cpp | 14 ++++++++------ .../adaptations/software/qsgabstractsoftwarerenderer.cpp | 4 +--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index bb5ed60e68..4c6b0b4167 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -98,7 +98,8 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1) << item->hasActiveFocus() << item->isFocusScope() << item; - for (QQuickItem *child : item->childItems()) { + const auto childItems = item->childItems(); + for (QQuickItem *child : childItems) { debugFocusTree( child, item->isFocusScope() || !scope ? item : scope, diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index f958d1a087..a54d6daf9c 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -764,7 +764,8 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector ungrab; for (int i = 0; i < ids.count(); ++i) { // FIXME: deprecate this function, we need a device - for (auto device: QQuickPointerDevice::touchDevices()) { + const auto touchDevices = QQuickPointerDevice::touchDevices(); + for (auto device : touchDevices) { auto point = device->pointerEvent()->pointById(ids.at(i)); if (!point) continue; @@ -783,7 +784,7 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVectortouchUngrabEvent(); } @@ -791,7 +792,8 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to { Q_Q(QQuickWindow); if (Q_LIKELY(touch)) { - for (auto device: QQuickPointerDevice::touchDevices()) { + const auto touchDevices = QQuickPointerDevice::touchDevices(); + for (auto device : touchDevices) { auto pointerEvent = device->pointerEvent(); for (int i = 0; i < pointerEvent->pointCount(); ++i) { if (pointerEvent->point(i)->grabber() == grabber) { @@ -1477,7 +1479,7 @@ bool QQuickWindowPrivate::clearHover(ulong timestamp) QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint()); bool accepted = false; - foreach (QQuickItem* item, hoverItems) + for (QQuickItem* item : qAsConst(hoverItems)) accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted; hoverItems.clear(); return accepted; @@ -2199,9 +2201,9 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) // Deliver touch points to existing grabbers bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered) { - for (auto grabber: event->grabbers()) { + const auto grabbers = event->grabbers(); + for (auto grabber : grabbers) deliverMatchingPointsToItem(grabber, event, hasFiltered); - } return false; } diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp index a6cb99ae05..2ff180ea99 100644 --- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp @@ -67,9 +67,7 @@ QSGAbstractSoftwareRenderer::~QSGAbstractSoftwareRenderer() // Cleanup RenderableNodes delete m_background; - for (QSGSoftwareRenderableNode *node : m_nodes.values()) { - delete node; - } + qDeleteAll(m_nodes); delete m_nodeUpdater; } -- cgit v1.2.3 From c5218430ed419dc22582f38c0a82580fbcca8d42 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 18 Aug 2016 18:10:59 +0300 Subject: auto tests: Particles: replace 'foreach' with 'range for' Change-Id: I69c5e0d076fcb68261ea2b0fb5bee485751f6ddd Reviewed-by: Shawn Rutledge --- tests/auto/particles/qquickage/tst_qquickage.cpp | 8 ++++---- .../qquickangleddirection/tst_qquickangleddirection.cpp | 2 +- .../tst_qquickcumulativedirection.cpp | 2 +- .../qquickcustomaffector/tst_qquickcustomaffector.cpp | 4 ++-- .../qquickcustomparticle/tst_qquickcustomparticle.cpp | 2 +- .../qquickellipseextruder/tst_qquickellipseextruder.cpp | 4 ++-- tests/auto/particles/qquickfriction/tst_qquickfriction.cpp | 6 +++--- tests/auto/particles/qquickgravity/tst_qquickgravity.cpp | 2 +- tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp | 2 +- .../qquickimageparticle/tst_qquickimageparticle.cpp | 12 ++++++------ .../particles/qquickitemparticle/tst_qquickitemparticle.cpp | 2 +- .../particles/qquicklineextruder/tst_qquicklineextruder.cpp | 4 ++-- .../particles/qquickmaskextruder/tst_qquickmaskextruder.cpp | 2 +- .../qquickparticlegroup/tst_qquickparticlegroup.cpp | 2 +- .../qquickparticlesystem/tst_qquickparticlesystem.cpp | 2 +- .../qquickpointattractor/tst_qquickpointattractor.cpp | 2 +- .../qquickpointdirection/tst_qquickpointdirection.cpp | 2 +- .../qquickrectangleextruder/tst_qquickrectangleextruder.cpp | 4 ++-- .../auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp | 2 +- .../qquicktargetdirection/tst_qquicktargetdirection.cpp | 2 +- .../particles/qquicktrailemitter/tst_qquicktrailemitter.cpp | 4 ++-- .../auto/particles/qquickturbulence/tst_qquickturbulence.cpp | 2 +- tests/auto/particles/qquickwander/tst_qquickwander.cpp | 2 +- 23 files changed, 38 insertions(+), 38 deletions(-) diff --git a/tests/auto/particles/qquickage/tst_qquickage.cpp b/tests/auto/particles/qquickage/tst_qquickage.cpp index 04c6fbd9e4..a233b30043 100644 --- a/tests/auto/particles/qquickage/tst_qquickage.cpp +++ b/tests/auto/particles/qquickage/tst_qquickage.cpp @@ -61,7 +61,7 @@ void tst_qquickage::test_kill() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -86,7 +86,7 @@ void tst_qquickage::test_jump() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -112,7 +112,7 @@ void tst_qquickage::test_onceOff() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -138,7 +138,7 @@ void tst_qquickage::test_sustained() //TODO: Ensure some particles have lived to 0.4s point despite unified timer //Can't verify size, because particles never die. It will constantly grow. - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp b/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp index 4ff72f159d..b3fc01caa6 100644 --- a/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp +++ b/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp @@ -58,7 +58,7 @@ void tst_qquickangleddirection::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp b/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp index 795fcaf42e..360b222367 100644 --- a/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp +++ b/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp @@ -57,7 +57,7 @@ void tst_qquickcumulativedirection::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp b/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp index fb15af4dd1..ee05fb29c9 100644 --- a/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp +++ b/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp @@ -59,7 +59,7 @@ void tst_qquickcustomaffector::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused //in CI the whole simulation often happens at once, so dead particles end up missing out @@ -92,7 +92,7 @@ void tst_qquickcustomaffector::test_move() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused if (!d->stillAlive(system)) diff --git a/tests/auto/particles/qquickcustomparticle/tst_qquickcustomparticle.cpp b/tests/auto/particles/qquickcustomparticle/tst_qquickcustomparticle.cpp index a722508a3e..60c6a37899 100644 --- a/tests/auto/particles/qquickcustomparticle/tst_qquickcustomparticle.cpp +++ b/tests/auto/particles/qquickcustomparticle/tst_qquickcustomparticle.cpp @@ -60,7 +60,7 @@ void tst_qquickcustomparticle::test_basic() bool oneNonZero = false; QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp b/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp index ac8e2bac49..a1fe5b225d 100644 --- a/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp +++ b/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp @@ -74,7 +74,7 @@ void tst_qquickellipseextruder::test_basic() //Filled QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -91,7 +91,7 @@ void tst_qquickellipseextruder::test_basic() //Just border QCOMPARE(system->groupData[1]->size(), 500); - foreach (QQuickParticleData *d, system->groupData[1]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp b/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp index 70e0291330..ab682c591e 100644 --- a/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp +++ b/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp @@ -59,7 +59,7 @@ void tst_qquickfriction::test_basic() //Default is just slowed a little QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -76,7 +76,7 @@ void tst_qquickfriction::test_basic() //Nondefault comes to a complete stop within the first half of its life QCOMPARE(system->groupData[1]->size(), 500); - foreach (QQuickParticleData *d, system->groupData[1]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) { if (d->t == -1) continue; //Particle data unused @@ -103,7 +103,7 @@ void tst_qquickfriction::test_threshold() //Velocity capped at 50, but it might take a frame or two to get there QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1.0f) continue; //Particle data unused if (myFuzzyGEQ(d->t, ((qreal)system->timeInt/1000.0) - 0.1)) diff --git a/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp b/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp index 8ef075dc9b..34566b90eb 100644 --- a/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp +++ b/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp @@ -58,7 +58,7 @@ void tst_qquickgravity::test_basic() QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); float mag = 707.10678f; - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1 || !d->stillAlive(system)) continue; //Particle data unused or dead diff --git a/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp b/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp index fc270b991f..1228b4cc68 100644 --- a/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp +++ b/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp @@ -58,7 +58,7 @@ void tst_qquickgroupgoal::test_instantTransition() ensureAnimTime(600, system->m_animation); QVERIFY(system->groupData[0]->size() <= 500 && system->groupData[0]->size() >= 450); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp b/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp index cd684ec59b..7e07878d39 100644 --- a/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp +++ b/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp @@ -72,7 +72,7 @@ void tst_qquickimageparticle::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -116,7 +116,7 @@ void tst_qquickimageparticle::test_colored() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -160,7 +160,7 @@ void tst_qquickimageparticle::test_colorVariance() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -205,7 +205,7 @@ void tst_qquickimageparticle::test_deformed() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -249,7 +249,7 @@ void tst_qquickimageparticle::test_tabled() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -294,7 +294,7 @@ void tst_qquickimageparticle::test_sprite() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp index 98aac71e93..d9791cdb33 100644 --- a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp +++ b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp @@ -60,7 +60,7 @@ void tst_qquickitemparticle::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp b/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp index 1f36874f0f..6baf8539d3 100644 --- a/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp +++ b/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp @@ -57,7 +57,7 @@ void tst_qquicklineextruder::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -73,7 +73,7 @@ void tst_qquicklineextruder::test_basic() } QCOMPARE(system->groupData[1]->size(), 500); - foreach (QQuickParticleData *d, system->groupData[1]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp b/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp index a3d0988019..d8481efc47 100644 --- a/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp +++ b/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp @@ -57,7 +57,7 @@ void tst_qquickmaskextruder::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp b/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp index 8b7224623a..8791d19db6 100644 --- a/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp +++ b/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp @@ -58,7 +58,7 @@ void tst_qquickparticlegroup::test_instantTransition() //A frame or two worth of particles will be missed, the transition doesn't take effect on the frame it's spawned (QTBUG-21781) QVERIFY(system->groupData[0]->size() <= 500 && system->groupData[0]->size() >= 450); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp b/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp index 4a3c5cdc74..5c82b946e5 100644 --- a/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp +++ b/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp @@ -58,7 +58,7 @@ void tst_qquickparticlesystem::test_basic() QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); int stillAlive = 0; - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp b/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp index 99a8530d3a..c0d7d38512 100644 --- a/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp +++ b/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp @@ -57,7 +57,7 @@ void tst_qquickpointattractor::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp b/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp index 0d150fb86d..5cc23e0306 100644 --- a/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp +++ b/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp @@ -57,7 +57,7 @@ void tst_qquickpointdirection::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp b/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp index 24b30bf9ab..414e2d36f7 100644 --- a/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp +++ b/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp @@ -57,7 +57,7 @@ void tst_qquickrectangleextruder::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -76,7 +76,7 @@ void tst_qquickrectangleextruder::test_basic() } QCOMPARE(system->groupData[1]->size(), 500); - foreach (QQuickParticleData *d, system->groupData[1]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp b/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp index 8249159e43..ca5d9171f9 100644 --- a/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp +++ b/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp @@ -57,7 +57,7 @@ void tst_qquickspritegoal::test_instantTransition() ensureAnimTime(600, system->m_animation); QVERIFY(system->groupData[0]->size() <= 500 && system->groupData[0]->size() >= 450); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp b/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp index 8d9556fdac..2f45263c37 100644 --- a/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp +++ b/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp @@ -57,7 +57,7 @@ void tst_qquicktargetdirection::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp b/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp index 99ecd7a06a..27d9bf01c9 100644 --- a/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp +++ b/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp @@ -57,7 +57,7 @@ void tst_qquicktrailemitter::test_basic() ensureAnimTime(600, system->m_animation); QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused @@ -74,7 +74,7 @@ void tst_qquicktrailemitter::test_basic() } QVERIFY(extremelyFuzzyCompare(system->groupData[1]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[1]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp b/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp index c8024470e4..74fafdc1d2 100644 --- a/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp +++ b/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp @@ -59,7 +59,7 @@ void tst_qquickturbulence::test_basic() //Note that the noise image built-in provides the 'randomness', so this test should be stable so long as it and the size //of the Turbulence item remain the same QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10)); - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/particles/qquickwander/tst_qquickwander.cpp b/tests/auto/particles/qquickwander/tst_qquickwander.cpp index 6e37fc648c..51ef7a272e 100644 --- a/tests/auto/particles/qquickwander/tst_qquickwander.cpp +++ b/tests/auto/particles/qquickwander/tst_qquickwander.cpp @@ -61,7 +61,7 @@ void tst_qquickwander::test_basic() //the 500 was randomly changed from 0.0 in velocity bool vxChanged = false; bool vyChanged = false; - foreach (QQuickParticleData *d, system->groupData[0]->data) { + for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) { if (d->t == -1) continue; //Particle data unused -- cgit v1.2.3 From c754b71eb4a141536dfb3e6697fbd089f4cba8e9 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 19 Aug 2016 11:20:43 +0300 Subject: manual tests: replace 'foreach' with 'range for' Change-Id: I21d17b7770246888b1de71d54a6a1babd0726b8a Reviewed-by: Shawn Rutledge --- tests/manual/qmlplugindump/tst_qmlplugindump.cpp | 11 ++++++----- tests/manual/scenegraph_lancelot/scenegrabber/main.cpp | 4 ++-- .../manual/scenegraph_lancelot/scenegraph/tst_scenegraph.cpp | 6 ++++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/manual/qmlplugindump/tst_qmlplugindump.cpp b/tests/manual/qmlplugindump/tst_qmlplugindump.cpp index ed00682a83..1bedd4427b 100644 --- a/tests/manual/qmlplugindump/tst_qmlplugindump.cpp +++ b/tests/manual/qmlplugindump/tst_qmlplugindump.cpp @@ -92,7 +92,7 @@ public: { QRegularExpression re; QRegularExpressionMatch m; - Q_FOREACH (const QString &e, m_expected) { + for (const QString &e : m_expected) { re.setPattern(e); m = re.match(QString::fromLatin1(buffer)); if (!m.hasMatch()) { @@ -187,7 +187,8 @@ Test createTest(const QString &id, const QJsonObject &def) return Test(id); } QStringList patterns; - Q_FOREACH (const QJsonValue &x, expected.toArray()) { + const auto expectedArray = expected.toArray(); + for (const QJsonValue &x : expectedArray) { if (!x.isString()) { qWarning() << "Wrong definition for test: " << id << "."; return Test(id); @@ -331,7 +332,7 @@ void tst_qmlplugindump::plugin_data() QTest::addColumn("version"); QTest::addColumn("expected"); - Q_FOREACH (const Test &t, tests) { + for (const Test &t : qAsConst(tests)) { if (t.isNull()) QSKIP("Errors in test definition."); QTest::newRow(t.id.toLatin1().data()) << t.project << t.version << t.expected; @@ -357,9 +358,9 @@ void tst_qmlplugindump::plugin() void tst_qmlplugindump::cleanupTestCase() { QSet projects; - Q_FOREACH (const Test &t, tests) + for (const Test &t : qAsConst(tests)) projects.insert(t.project); - Q_FOREACH (const QString &p, projects) { + for (const QString &p : qAsConst(projects)) { if (!cleanUpSample(p)) qWarning() << "Error in cleaning up project" << p << "."; } diff --git a/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp b/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp index 5098d51134..886cfc6599 100644 --- a/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp +++ b/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp @@ -183,8 +183,8 @@ int main(int argc, char *argv[]) v.setSource(QUrl::fromLocalFile(ifile)); if (noText) { - QList items = v.rootObject()->findChildren(); - foreach (QQuickItem *item, items) { + const QList items = v.rootObject()->findChildren(); + for (QQuickItem *item : items) { if (QByteArray(item->metaObject()->className()).contains("Text")) item->setVisible(false); } diff --git a/tests/manual/scenegraph_lancelot/scenegraph/tst_scenegraph.cpp b/tests/manual/scenegraph_lancelot/scenegraph/tst_scenegraph.cpp index 426e06ccc2..3f28d90e7b 100644 --- a/tests/manual/scenegraph_lancelot/scenegraph/tst_scenegraph.cpp +++ b/tests/manual/scenegraph_lancelot/scenegraph/tst_scenegraph.cpp @@ -156,7 +156,7 @@ void tst_Scenegraph::setupTestSuite(const QByteArray& filter) } std::sort(itemFiles.begin(), itemFiles.end()); - foreach (const QString &filePath, itemFiles) { + for (const QString &filePath : qAsConst(itemFiles)) { QByteArray itemName = filePath.mid(testSuitePath.length() + 1).toLatin1(); QBaselineTest::newRow(itemName, checksumFileOrDir(filePath)) << filePath; numItems++; @@ -238,7 +238,9 @@ quint16 tst_Scenegraph::checksumFileOrDir(const QString &path) if (fi.isDir()) { static const QStringList nameFilters = QStringList() << "*.qml" << "*.cpp" << "*.png" << "*.jpg"; quint16 cs = 0; - foreach (QString item, QDir(fi.filePath()).entryList(nameFilters, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) + const auto entryList = QDir(fi.filePath()).entryList(nameFilters, + QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + for (const QString &item : entryList) cs ^= checksumFileOrDir(path + QLatin1Char('/') + item); return cs; } -- cgit v1.2.3 From 3ef4fac9ff3f785d3ccbda4b28ec2c0ea2ee1b59 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 11 Aug 2016 13:08:00 +0300 Subject: tools: replace 'foreach' with 'range for' Mark some local variables or parameters as const to prevent detach()'ing. Use qAsConst where is not possible mark as const. Change-Id: I0a777c3bd855abd3bb1ad0907152360cf4a1050e Reviewed-by: Shawn Rutledge --- tools/qml/main.cpp | 8 ++--- tools/qmleasing/splineeditor.cpp | 2 +- tools/qmlimportscanner/main.cpp | 18 +++++------ tools/qmljs/qmljs.cpp | 4 +-- tools/qmlmin/main.cpp | 2 +- tools/qmlplugindump/main.cpp | 48 ++++++++++++++-------------- tools/qmlplugindump/qmlstreamwriter.cpp | 2 +- tools/qmlprofiler/qmlprofilerapplication.cpp | 4 +-- tools/qmlprofiler/qmlprofilerdata.cpp | 2 +- tools/qmlscene/main.cpp | 16 +++++----- 10 files changed, 53 insertions(+), 53 deletions(-) diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp index d718067616..be62500858 100644 --- a/tools/qml/main.cpp +++ b/tools/qml/main.cpp @@ -192,7 +192,7 @@ public Q_SLOTS: checkForWindow(o); haveOne = true; if (conf && qae) - foreach (PartialScene *ps, conf->completers) + for (PartialScene *ps : qAsConst(conf->completers)) if (o->inherits(ps->itemType().toUtf8().constData())) contain(o, ps->container()); } @@ -413,8 +413,8 @@ static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory) QObject *dummyData = comp.create(); if (comp.isError()) { - QList errors = comp.errors(); - foreach (const QQmlError &error, errors) + const QList errors = comp.errors(); + for (const QQmlError &error : errors) qWarning() << error; } @@ -566,7 +566,7 @@ int main(int argc, char *argv[]) if (!dummyDir.isEmpty() && QFileInfo (dummyDir).isDir()) loadDummyDataFiles(e, dummyDir); - foreach (const QString &path, files) { + for (const QString &path : qAsConst(files)) { //QUrl::fromUserInput doesn't treat no scheme as relative file paths #ifndef QT_NO_REGULAREXPRESSION QRegularExpression urlRe("[[:word:]]+://.*"); diff --git a/tools/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp index ee55931a46..6fee013c62 100644 --- a/tools/qmleasing/splineeditor.cpp +++ b/tools/qmleasing/splineeditor.cpp @@ -288,7 +288,7 @@ QHash SplineEditor::presets() const QString SplineEditor::generateCode() { QString s = QLatin1String("["); - foreach (const QPointF &point, m_controlPoints) { + for (const QPointF &point : qAsConst(m_controlPoints)) { s += QString::number(point.x(), 'g', 2) + QLatin1Char(',') + QString::number(point.y(), 'g', 3) + QLatin1Char(','); } diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 4a77623d28..f7f5a5e4e4 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -171,7 +171,7 @@ QString resolveImportPath(const QString &uri, const QString &version) QString ver = version; while (true) { - foreach (const QString &qmlImportPath, g_qmlImportPaths) { + for (const QString &qmlImportPath : qAsConst(g_qmlImportPaths)) { // Search for the most specific version first, and search // also for the version in parent modules. For example: // - qml/QtQml/Models.2.0 @@ -230,8 +230,8 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) if (!classnames.isEmpty()) import.insert(QStringLiteral("classname"), classnames); if (plugininfo.contains(dependenciesLiteral())) { - QStringList dependencies = plugininfo.value(dependenciesLiteral()).toStringList(); - foreach (const QString &line, dependencies) { + const QStringList dependencies = plugininfo.value(dependenciesLiteral()).toStringList(); + for (const QString &line : dependencies) { const auto dep = line.splitRef(QLatin1Char(' ')); QVariantMap depImport; depImport[typeLiteral()] = QStringLiteral("module"); @@ -362,7 +362,7 @@ QVariantList findQmlImportsInFile(const QString &filePath) QVariantList mergeImports(const QVariantList &a, const QVariantList &b) { QVariantList merged = a; - foreach (const QVariant &variant, b) { + for (const QVariant &variant : b) { if (!merged.contains(variant)) merged.append(variant); } @@ -421,16 +421,16 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir) continue; } - foreach (const QFileInfo &x, entries) + for (const QFileInfo &x : entries) if (x.isFile()) ret = mergeImports(ret, findQmlImportsInFile(x.absoluteFilePath())); } return ret; } -QSet importModulePaths(QVariantList imports) { +QSet importModulePaths(const QVariantList &imports) { QSet ret; - foreach (const QVariant &importVariant, imports) { + for (const QVariant &importVariant : imports) { QVariantMap import = qvariant_cast(importVariant); QString path = import.value(pathLiteral()).toString(); QString type = import.value(typeLiteral()).toString(); @@ -448,13 +448,13 @@ QVariantList findQmlImportsRecursively(const QStringList &qmlDirs, const QString QVariantList ret; // scan all app root qml directories for imports - foreach (const QString &qmlDir, qmlDirs) { + for (const QString &qmlDir : qmlDirs) { QVariantList imports = findQmlImportsInDirectory(qmlDir); ret = mergeImports(ret, imports); } // scan app qml files for imports - foreach (const QString &file, scanFiles) { + for (const QString &file : scanFiles) { QVariantList imports = findQmlImportsInFile(file); ret = mergeImports(ret, imports); } diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp index 4f79546bcc..964afc265b 100644 --- a/tools/qmljs/qmljs.cpp +++ b/tools/qmljs/qmljs.cpp @@ -118,7 +118,7 @@ static void showException(QV4::ExecutionContext *ctx, const QV4::Value &exceptio std::cerr << "Uncaught exception: " << qPrintable(message->toQStringNoThrow()) << std::endl; } - foreach (const QV4::StackFrame &frame, trace) { + for (const QV4::StackFrame &frame : trace) { std::cerr << " at " << qPrintable(frame.function) << " (" << qPrintable(frame.source); if (frame.line >= 0) std::cerr << ':' << frame.line; @@ -188,7 +188,7 @@ int main(int argc, char *argv[]) QV4::ScopedObject gc(scope, vm.memoryManager->allocObject(ctx)); vm.globalObject->put(QV4::ScopedString(scope, vm.newIdentifier(QStringLiteral("gc"))).getPointer(), gc); - foreach (const QString &fn, args) { + for (const QString &fn : qAsConst(args)) { QFile file(fn); if (file.open(QFile::ReadOnly)) { const QString code = QString::fromUtf8(file.readAll()); diff --git a/tools/qmlmin/main.cpp b/tools/qmlmin/main.cpp index 2b18a8442b..d2bad9d0d5 100644 --- a/tools/qmlmin/main.cpp +++ b/tools/qmlmin/main.cpp @@ -120,7 +120,7 @@ protected: static QString quote(const QString &string) { QString quotedString; - foreach (const QChar &ch, string) { + for (const QChar &ch : string) { if (ch == QLatin1Char('"')) quotedString += QLatin1String("\\\""); else { diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 0e5d3646c2..ceffebf14b 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -248,15 +248,15 @@ QSet collectReachableMetaObjects(QQmlEngine *engine, QSet baseExports = qmlTypesByCppName.value(it.key()); const QSet extensionCppNames = it.value(); - foreach (const QByteArray &extensionCppName, extensionCppNames) { + for (const QByteArray &extensionCppName : extensionCppNames) { const QSet extensionExports = qmlTypesByCppName.value(extensionCppName); // remove extension exports from base imports // unfortunately the QQmlType pointers don't match, so can't use QSet::subtract QSet newBaseExports; - foreach (const QQmlType *baseExport, baseExports) { + for (const QQmlType *baseExport : qAsConst(baseExports)) { bool match = false; - foreach (const QQmlType *extensionExport, extensionExports) { + for (const QQmlType *extensionExport : extensionExports) { if (baseExport->qmlTypeName() == extensionExport->qmlTypeName() && baseExport->majorVersion() == extensionExport->majorVersion() && baseExport->minorVersion() == extensionExport->minorVersion()) { @@ -469,7 +469,7 @@ public: void dumpComposite(QQmlEngine *engine, const QSet &compositeType, QSet &defaultReachableNames) { - foreach (const QQmlType *type, compositeType) + for (const QQmlType *type : compositeType) dumpCompositeItem(engine, type, defaultReachableNames); } @@ -518,7 +518,7 @@ public: } } - foreach (const QMetaObject *meta, objectsToMerge) + for (const QMetaObject *meta : qAsConst(objectsToMerge)) writeMetaContent(meta, &knownAttributes); qml->writeEndObject(); @@ -542,11 +542,11 @@ public: if (meta->superClass()) qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass()))); - QSet qmlTypes = qmlTypesByCppName.value(meta->className()); + const QSet qmlTypes = qmlTypesByCppName.value(meta->className()); if (!qmlTypes.isEmpty()) { QHash exports; - foreach (const QQmlType *qmlTy, qmlTypes) { + for (const QQmlType *qmlTy : qmlTypes) { const QString exportString = getExportString(qmlTy->qmlTypeName(), qmlTy->majorVersion(), qmlTy->minorVersion()); exports.insert(exportString, qmlTy); } @@ -564,7 +564,7 @@ public: // write meta object revisions QStringList metaObjectRevisions; - foreach (const QString &exportString, exportStrings) { + for (const QString &exportString : qAsConst(exportStrings)) { int metaObjectRevision = exports[exportString]->metaObjectRevision(); metaObjectRevisions += QString::number(metaObjectRevision); } @@ -749,7 +749,7 @@ static bool readDependenciesData(QString dependenciesFile, const QByteArray &fil if (verbose) { std::cerr << "parsing " << qPrintable( dependenciesFile ) << " skipping"; - foreach (const QString &uriToSkip, urisToSkip) + for (const QString &uriToSkip : urisToSkip) std::cerr << ' ' << qPrintable(uriToSkip); std::cerr << std::endl; } @@ -763,13 +763,13 @@ static bool readDependenciesData(QString dependenciesFile, const QByteArray &fil return false; } if (doc.isArray()) { - QStringList requiredKeys = QStringList() << QStringLiteral("name") - << QStringLiteral("type") - << QStringLiteral("version"); + const QStringList requiredKeys = QStringList() << QStringLiteral("name") + << QStringLiteral("type") + << QStringLiteral("version"); foreach (const QJsonValue &dep, doc.array()) { if (dep.isObject()) { QJsonObject obj = dep.toObject(); - foreach (const QString &requiredKey, requiredKeys) + for (const QString &requiredKey : requiredKeys) if (!obj.contains(requiredKey) || obj.value(requiredKey).isString()) continue; if (obj.value(QStringLiteral("type")).toString() != QLatin1String("module")) @@ -850,7 +850,7 @@ static bool getDependencies(const QQmlEngine &engine, const QString &pluginImpor if (!importScanner.waitForFinished()) { std::cerr << "failure to start " << qPrintable(command); - foreach (const QString &arg, commandArgs) + for (const QString &arg : qAsConst(commandArgs)) std::cerr << ' ' << qPrintable(arg); std::cerr << std::endl; return false; @@ -863,7 +863,7 @@ static bool getDependencies(const QQmlEngine &engine, const QString &pluginImpor } QStringList aux; - foreach (const QString &str, *dependencies) { + for (const QString &str : qAsConst(*dependencies)) { if (!str.startsWith("Qt.test.qtestroot")) aux += str; } @@ -1124,7 +1124,7 @@ int main(int argc, char *argv[]) // load the QtQml builtins and the dependencies { QByteArray code(qtQmlImportString.toUtf8()); - foreach (const QString &moduleToImport, dependencies) { + for (const QString &moduleToImport : qAsConst(dependencies)) { code.append("\nimport "); code.append(moduleToImport.toUtf8()); } @@ -1154,7 +1154,7 @@ int main(int argc, char *argv[]) QSet metas; if (action == Builtins) { - foreach (const QMetaObject *m, defaultReachable) { + for (const QMetaObject *m : qAsConst(defaultReachable)) { if (m->className() == QLatin1String("Qt")) { metas.insert(m); break; @@ -1175,7 +1175,7 @@ int main(int argc, char *argv[]) return EXIT_INVALIDARGUMENTS; } metas = defaultReachable; - foreach (const QMetaObject *m, defaultReachable) { + for (const QMetaObject *m : qAsConst(defaultReachable)) { if (m->className() == QLatin1String("Qt")) { metas.remove(m); break; @@ -1196,7 +1196,7 @@ int main(int argc, char *argv[]) QString::number(qtObjectType->minorVersion())).toUtf8(); } // avoid importing dependencies? - foreach (const QString &moduleToImport, dependencies) { + for (const QString &moduleToImport : qAsConst(dependencies)) { importCode.append("\nimport "); importCode.append(moduleToImport.toUtf8()); } @@ -1231,9 +1231,9 @@ int main(int argc, char *argv[]) // Also eliminate meta objects with the same classname. // This is required because extended objects seem not to share // a single meta object instance. - foreach (const QMetaObject *mo, defaultReachable) + for (const QMetaObject *mo : qAsConst(defaultReachable)) defaultReachableNames.insert(QByteArray(mo->className())); - foreach (const QMetaObject *mo, candidates) { + for (const QMetaObject *mo : qAsConst(candidates)) { if (!defaultReachableNames.contains(mo->className())) metas.insert(mo); } @@ -1265,19 +1265,19 @@ int main(int argc, char *argv[]) compactDependencies(&dependencies); QStringList quotedDependencies; - foreach (const QString &dep, dependencies) + for (const QString &dep : qAsConst(dependencies)) quotedDependencies << enquote(dep); qml.writeArrayBinding("dependencies", quotedDependencies); // put the metaobjects into a map so they are always dumped in the same order QMap nameToMeta; - foreach (const QMetaObject *meta, metas) + for (const QMetaObject *meta : qAsConst(metas)) nameToMeta.insert(convertToId(meta), meta); Dumper dumper(&qml); if (relocatable) dumper.setRelocatableModuleUri(pluginImportUri); - foreach (const QMetaObject *meta, nameToMeta) { + for (const QMetaObject *meta : qAsConst(nameToMeta)) { dumper.dump(QQmlEnginePrivate::get(&engine), meta, uncreatableMetas.contains(meta), singletonMetas.contains(meta)); } diff --git a/tools/qmlplugindump/qmlstreamwriter.cpp b/tools/qmlplugindump/qmlstreamwriter.cpp index dd3c188fe4..3632cee60d 100644 --- a/tools/qmlplugindump/qmlstreamwriter.cpp +++ b/tools/qmlplugindump/qmlstreamwriter.cpp @@ -179,7 +179,7 @@ void QmlStreamWriter::flushPotentialLinesWithNewlines() { if (m_maybeOneline) m_stream->write("\n"); - foreach (const QByteArray &line, m_pendingLines) { + for (const QByteArray &line : qAsConst(m_pendingLines)) { writeIndent(); m_stream->write(line); m_stream->write("\n"); diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp index 45c32487a2..033492b516 100644 --- a/tools/qmlprofiler/qmlprofilerapplication.cpp +++ b/tools/qmlprofiler/qmlprofilerapplication.cpp @@ -274,8 +274,8 @@ quint64 QmlProfilerApplication::parseFeatures(const QStringList &featureList, co bool exclude) { quint64 features = exclude ? std::numeric_limits::max() : 0; - QStringList givenFeatures = values.split(QLatin1Char(',')); - foreach (const QString &f, givenFeatures) { + const QStringList givenFeatures = values.split(QLatin1Char(',')); + for (const QString &f : givenFeatures) { int index = featureList.indexOf(f); if (index < 0) { logError(tr("Unknown feature '%1'").arg(f)); diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp index b651b2724f..048c92bb93 100644 --- a/tools/qmlprofiler/qmlprofilerdata.cpp +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -572,7 +572,7 @@ bool QmlProfilerData::save(const QString &filename) stream.writeEndElement(); // eventData stream.writeStartElement(QStringLiteral("profilerDataModel")); - foreach (const QmlRangeEventStartInstance &event, d->startInstanceList) { + for (const QmlRangeEventStartInstance &event : qAsConst(d->startInstanceList)) { stream.writeStartElement(QStringLiteral("range")); stream.writeAttribute(QStringLiteral("startTime"), QString::number(event.startTime)); if (event.duration >= 0) diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp index 0e542ab0c8..9e1002166d 100644 --- a/tools/qmlscene/main.cpp +++ b/tools/qmlscene/main.cpp @@ -175,10 +175,10 @@ QFileInfoList findQmlFiles(const QString &dirName) QFileInfoList ret; if (dir.exists()) { - QFileInfoList fileInfos = dir.entryInfoList(QStringList() << "*.qml", - QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot); + const QFileInfoList fileInfos = dir.entryInfoList(QStringList() << "*.qml", + QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot); - foreach (QFileInfo fileInfo, fileInfos) { + for (const QFileInfo &fileInfo : fileInfos) { if (fileInfo.isDir()) ret += findQmlFiles(fileInfo.filePath()); else if (fileInfo.fileName().length() > 0 && fileInfo.fileName().at(0).isLower()) @@ -196,9 +196,9 @@ static int displayOptionsDialog(Options *options) QFormLayout *layout = new QFormLayout(&dialog); QComboBox *qmlFileComboBox = new QComboBox(&dialog); - QFileInfoList fileInfos = findQmlFiles(":/bundle") + findQmlFiles("./qmlscene-resources"); + const QFileInfoList fileInfos = findQmlFiles(":/bundle") + findQmlFiles("./qmlscene-resources"); - foreach (QFileInfo fileInfo, fileInfos) + for (const QFileInfo &fileInfo : fileInfos) qmlFileComboBox->addItem(fileInfo.dir().dirName() + QLatin1Char('/') + fileInfo.fileName(), QVariant::fromValue(fileInfo)); QCheckBox *originalCheckBox = new QCheckBox(&dialog); @@ -319,8 +319,8 @@ static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory) QObject *dummyData = comp.create(); if(comp.isError()) { - QList errors = comp.errors(); - foreach (const QQmlError &error, errors) + const QList errors = comp.errors(); + for (const QQmlError &error : errors) fprintf(stderr, "%s\n", qPrintable(error.toString())); } @@ -457,7 +457,7 @@ int main(int argc, char ** argv) options.applicationAttributes.append(Qt::AA_DisableHighDpiScaling); } - foreach (Qt::ApplicationAttribute a, options.applicationAttributes) + for (Qt::ApplicationAttribute a : qAsConst(options.applicationAttributes)) QCoreApplication::setAttribute(a); #ifdef QT_WIDGETS_LIB QApplication app(argc, argv); -- cgit v1.2.3 From 909d6498edbf31fcc21a137b7244c580afdbf749 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 11 Aug 2016 13:37:27 +0300 Subject: tools: replace 'foreach' with 'range for' Catch rvalues to prevent detach()'ing. Change-Id: I7be159a405c994429c0eee10805bfe96fcf4c806 Reviewed-by: Marc Mutz Reviewed-by: Shawn Rutledge --- tools/qmleasing/mainwindow.cpp | 3 ++- tools/qmlimportscanner/main.cpp | 6 ++++-- tools/qmllint/main.cpp | 9 +++++---- tools/qmlplugindump/main.cpp | 24 +++++++++++++++--------- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/tools/qmleasing/mainwindow.cpp b/tools/qmleasing/mainwindow.cpp index 5a5f651396..d36ab5fd75 100644 --- a/tools/qmleasing/mainwindow.cpp +++ b/tools/qmleasing/mainwindow.cpp @@ -73,7 +73,8 @@ MainWindow::MainWindow(QWidget *parent) : quickView.rootContext()->setContextProperty(QLatin1String("spinBox"), ui_properties.spinBox); - foreach (const QString &name, splineEditor->presetNames()) + const auto presetNames = splineEditor->presetNames(); + for (const QString &name : presetNames) ui_properties.comboBox->addItem(name); connect(ui_properties.comboBox, SIGNAL(currentIndexChanged(QString)), splineEditor, SLOT(setPreset(QString))); diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index f7f5a5e4e4..3c4dccf806 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -256,7 +256,8 @@ static QVariantList findQmlImportsInQmlCode(const QString &filePath, const QStri if (!parser.parse() || !parser.diagnosticMessages().isEmpty()) { // Extract errors from the parser - foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { + const auto diagnosticMessages = parser.diagnosticMessages(); + for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { std::cerr << QDir::toNativeSeparators(filePath).toStdString() << ':' << m.loc.startLine << ':' << m.message.toStdString() << std::endl; } @@ -334,7 +335,8 @@ QVariantList findQmlImportsInJavascriptFile(const QString &filePath) QQmlJS::Parser parser(&ee); parser.parseProgram(); - foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) + const auto diagnosticMessages = parser.diagnosticMessages(); + for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) if (m.isError()) return QVariantList(); diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp index 211427cc64..99a53110a8 100644 --- a/tools/qmllint/main.cpp +++ b/tools/qmllint/main.cpp @@ -59,7 +59,8 @@ static bool lint_file(const QString &filename, bool silent) bool success = isJavaScript ? parser.parseProgram() : parser.parse(); if (!success && !silent) { - foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { + const auto diagnosticMessages = parser.diagnosticMessages(); + for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { qWarning("%s:%d : %s", qPrintable(filename), m.loc.startLine, qPrintable(m.message)); } } @@ -82,15 +83,15 @@ int main(int argv, char *argc[]) parser.process(app); - if (parser.positionalArguments().isEmpty()) { + const auto positionalArguments = parser.positionalArguments(); + if (positionalArguments.isEmpty()) { parser.showHelp(-1); } bool silent = parser.isSet(silentOption); bool success = true; - foreach (const QString &filename, parser.positionalArguments()) { + for (const QString &filename : positionalArguments) success &= lint_file(filename, silent); - } return success ? 0 : -1; } diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index ceffebf14b..a5f825907a 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -205,7 +205,8 @@ QByteArray convertToId(const QMetaObject *mo) // Collect all metaobjects for types registered with qmlRegisterType() without parameters void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet& metas ) { - foreach (const QQmlType *ty, QQmlMetaType::qmlAllTypes()) { + const auto qmlAllTypes = QQmlMetaType::qmlAllTypes(); + for (const QQmlType *ty : qmlAllTypes) { if ( ! metas.contains(ty->metaObject()) ) { if (!ty->isComposite()) { collectReachableMetaObjects(engine, ty, &metas); @@ -225,7 +226,8 @@ QSet collectReachableMetaObjects(QQmlEngine *engine, metas.insert(FriendlyQObject::qtMeta()); QHash > extensions; - foreach (const QQmlType *ty, QQmlMetaType::qmlTypes()) { + const auto qmlTypes = QQmlMetaType::qmlTypes(); + for (const QQmlType *ty : qmlTypes) { if (!ty->isCreatable()) noncreatables.insert(ty->metaObject()); if (ty->isSingleton()) @@ -275,7 +277,7 @@ QSet collectReachableMetaObjects(QQmlEngine *engine, if (creatable) { // find even more QMetaObjects by instantiating QML types and running // over the instances - foreach (QQmlType *ty, QQmlMetaType::qmlTypes()) { + for (QQmlType *ty : qmlTypes) { if (skip.contains(ty)) continue; if (ty->isExtendedType()) @@ -766,7 +768,8 @@ static bool readDependenciesData(QString dependenciesFile, const QByteArray &fil const QStringList requiredKeys = QStringList() << QStringLiteral("name") << QStringLiteral("type") << QStringLiteral("version"); - foreach (const QJsonValue &dep, doc.array()) { + const auto deps = doc.array(); + for (const QJsonValue &dep : deps) { if (dep.isObject()) { QJsonObject obj = dep.toObject(); for (const QString &requiredKey : requiredKeys) @@ -833,7 +836,8 @@ static bool getDependencies(const QQmlEngine &engine, const QString &pluginImpor QStringList commandArgs = QStringList() << QLatin1String("-qmlFiles") << QLatin1String("-"); - foreach (const QString &path, engine.importPathList()) + const auto importPathList = engine.importPathList(); + for (const QString &path : importPathList) commandArgs << QLatin1String("-importPath") << path; QProcess importScanner; @@ -1132,8 +1136,9 @@ int main(int argc, char *argv[]) QQmlComponent c(&engine); c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/loaddependencies.qml")); c.create(); - if (!c.errors().isEmpty()) { - foreach (const QQmlError &error, c.errors()) + const auto errors = c.errors(); + if (!errors.isEmpty()) { + for (const QQmlError &error : errors) std::cerr << qPrintable( error.toString() ) << std::endl; return EXIT_IMPORTERROR; } @@ -1218,8 +1223,9 @@ int main(int argc, char *argv[]) c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/typelist.qml")); c.create(); - if (!c.errors().isEmpty()) { - foreach (const QQmlError &error, c.errors()) + const auto errors = c.errors(); + if (!errors.isEmpty()) { + for (const QQmlError &error : errors) std::cerr << qPrintable( error.toString() ) << std::endl; return EXIT_IMPORTERROR; } -- cgit v1.2.3 From 97c4ae41f268b3a128285761858e0eb9f6fec27d Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 19 Aug 2016 17:45:00 +0300 Subject: QQuickItemView: de-duplicate calls and cache results Change-Id: If4b95446fcd0d84a6bb5e285e770450b966d5bc0 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitemview.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index e017d6564a..8f6096a75f 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -935,9 +935,11 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode) return; applyPendingChanges(); - int idx = qMax(qMin(index, model->count()-1), 0); + const int modelCount = model->count(); + int idx = qMax(qMin(index, modelCount - 1), 0); - qreal pos = isContentFlowReversed() ? -position() - size() : position(); + const auto viewSize = size(); + qreal pos = isContentFlowReversed() ? -position() - viewSize : position(); FxViewItem *item = visibleItem(idx); qreal maxExtent = calculatedMaxExtent(); if (!item) { @@ -961,22 +963,22 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode) pos -= headerSize(); break; case QQuickItemView::Center: - pos = itemPos - (size() - item->size())/2; + pos = itemPos - (viewSize - item->size())/2; break; case QQuickItemView::End: - pos = itemPos - size() + item->size(); - if (footer && (index >= model->count() || hasStickyFooter())) + pos = itemPos - viewSize + item->size(); + if (footer && (index >= modelCount || hasStickyFooter())) pos += footerSize(); break; case QQuickItemView::Visible: - if (itemPos > pos + size()) - pos = itemPos - size() + item->size(); + if (itemPos > pos + viewSize) + pos = itemPos - viewSize + item->size(); else if (item->endPosition() <= pos) pos = itemPos; break; case QQuickItemView::Contain: - if (item->endPosition() >= pos + size()) - pos = itemPos - size() + item->size(); + if (item->endPosition() >= pos + viewSize) + pos = itemPos - viewSize + item->size(); if (itemPos < pos) pos = itemPos; break; @@ -1787,10 +1789,11 @@ void QQuickItemViewPrivate::animationFinished(QAbstractAnimationJob *) void QQuickItemViewPrivate::refill() { qreal s = qMax(size(), qreal(0.)); + const auto pos = position(); if (isContentFlowReversed()) - refill(-position()-displayMarginBeginning-s, -position()+displayMarginEnd); + refill(-pos - displayMarginBeginning-s, -pos + displayMarginEnd); else - refill(position()-displayMarginBeginning, position()+displayMarginEnd+s); + refill(pos - displayMarginBeginning, pos + displayMarginEnd+s); } void QQuickItemViewPrivate::refill(qreal from, qreal to) -- cgit v1.2.3 From 457bb7a465fc51995fc5ffaa4a1d8a341528d0c4 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Mon, 15 Aug 2016 13:30:48 +0300 Subject: tools: use const (and const APIs) more For CoW types, prefer const methods to avoid needless detach()ing. Change-Id: If9018391c001eba3b4b2061d06c4caa8136811ab Reviewed-by: Simon Hausmann --- tools/qml/main.cpp | 2 +- tools/qmleasing/splineeditor.cpp | 4 ++-- tools/qmlimportscanner/main.cpp | 6 +++--- tools/qmljs/qmljs.cpp | 8 ++++---- tools/qmlplugindump/main.cpp | 26 +++++++++++++------------- tools/qmlprofiler/qmlprofilerdata.cpp | 10 +++++----- 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp index be62500858..fdfc66f3a6 100644 --- a/tools/qml/main.cpp +++ b/tools/qml/main.cpp @@ -463,7 +463,7 @@ int main(int argc, char *argv[]) QString dummyDir; //Handle main arguments - QStringList argList = app->arguments(); + const QStringList argList = app->arguments(); for (int i = 1; i < argList.count(); i++) { const QString &arg = argList[i]; if (arg == QLatin1String("-quiet")) diff --git a/tools/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp index 6fee013c62..d54a101b69 100644 --- a/tools/qmleasing/splineeditor.cpp +++ b/tools/qmleasing/splineeditor.cpp @@ -620,7 +620,7 @@ void SplineEditor::mouseMoveEvent(QMouseEvent *e) if (indexIsRealPoint(m_activeControlPoint)) { //move also the tangents QPointF targetPoint = p; - QPointF distance = targetPoint - m_controlPoints[m_activeControlPoint]; + QPointF distance = targetPoint - m_controlPoints.at(m_activeControlPoint); m_controlPoints[m_activeControlPoint] = targetPoint; m_controlPoints[m_activeControlPoint - 1] += distance; m_controlPoints[m_activeControlPoint + 1] += distance; @@ -629,7 +629,7 @@ void SplineEditor::mouseMoveEvent(QMouseEvent *e) m_controlPoints[m_activeControlPoint] = p; } else { QPointF targetPoint = p; - QPointF distance = targetPoint - m_controlPoints[m_activeControlPoint]; + QPointF distance = targetPoint - m_controlPoints.at(m_activeControlPoint); m_controlPoints[m_activeControlPoint] = p; if ((m_activeControlPoint > 1) && (m_activeControlPoint % 3) == 0) { //right control point diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 3c4dccf806..8f476649e9 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -145,7 +145,7 @@ QVariantMap pluginsForModulePath(const QString &modulePath) { classnames += QString::fromUtf8(line.split(' ').at(1)); classnames += QLatin1Char(' '); } else if (line.startsWith("depends")) { - QList dep = line.split(' '); + const QList dep = line.split(' '); if (dep.length() != 3) std::cerr << "depends: expected 2 arguments: module identifier and version" << std::endl; else @@ -217,8 +217,8 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) QVariantList importsCopy(imports); for (int i = 0; i < importsCopy.length(); ++i) { - QVariantMap import = qvariant_cast(importsCopy[i]); - if (import[typeLiteral()] == QLatin1String("module")) { + QVariantMap import = qvariant_cast(importsCopy.at(i)); + if (import.value(typeLiteral()) == QLatin1String("module")) { QString path = resolveImportPath(import.value(nameLiteral()).toString(), import.value(versionLiteral()).toString()); if (!path.isEmpty()) import[pathLiteral()] = path; diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp index 964afc265b..44ff37b925 100644 --- a/tools/qmljs/qmljs.cpp +++ b/tools/qmljs/qmljs.cpp @@ -145,22 +145,22 @@ int main(int argc, char *argv[]) bool runAsQml = false; if (!args.isEmpty()) { - if (args.first() == QLatin1String("--jit")) { + if (args.constFirst() == QLatin1String("--jit")) { mode = use_masm; args.removeFirst(); } - if (args.first() == QLatin1String("--interpret")) { + if (args.constFirst() == QLatin1String("--interpret")) { mode = use_moth; args.removeFirst(); } - if (args.first() == QLatin1String("--qml")) { + if (args.constFirst() == QLatin1String("--qml")) { runAsQml = true; args.removeFirst(); } - if (args.first() == QLatin1String("--help")) { + if (args.constFirst() == QLatin1String("--help")) { std::cerr << "Usage: qmljs [|--jit|--interpret|--qml] file..." << std::endl; return EXIT_SUCCESS; } diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index a5f825907a..de8d238578 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -881,20 +881,20 @@ bool compactDependencies(QStringList *dependencies) if (dependencies->isEmpty()) return false; dependencies->sort(); - QStringList oldDep = dependencies->first().split(QLatin1Char(' ')); + QStringList oldDep = dependencies->constFirst().split(QLatin1Char(' ')); Q_ASSERT(oldDep.size() == 2); int oldPos = 0; for (int idep = 1; idep < dependencies->size(); ++idep) { QString depStr = dependencies->at(idep); const QStringList newDep = depStr.split(QLatin1Char(' ')); Q_ASSERT(newDep.size() == 2); - if (newDep.first() != oldDep.first()) { + if (newDep.constFirst() != oldDep.constFirst()) { if (++oldPos != idep) dependencies->replace(oldPos, depStr); oldDep = newDep; } else { - QStringList v1 = oldDep.last().split(QLatin1Char('.')); - QStringList v2 = newDep.last().split(QLatin1Char('.')); + const QStringList v1 = oldDep.constLast().split(QLatin1Char('.')); + const QStringList v2 = newDep.constLast().split(QLatin1Char('.')); Q_ASSERT(v1.size() == 2); Q_ASSERT(v2.size() == 2); bool ok; @@ -903,9 +903,9 @@ bool compactDependencies(QStringList *dependencies) int major2 = v2.first().toInt(&ok); Q_ASSERT(ok); if (major1 != major2) { - std::cerr << "Found a dependency on " << qPrintable(oldDep.first()) - << " with two major versions:" << qPrintable(oldDep.last()) - << " and " << qPrintable(newDep.last()) + std::cerr << "Found a dependency on " << qPrintable(oldDep.constFirst()) + << " with two major versions:" << qPrintable(oldDep.constLast()) + << " and " << qPrintable(newDep.constLast()) << " which is unsupported, discarding smaller version" << std::endl; if (major1 < major2) dependencies->replace(oldPos, depStr); @@ -1061,18 +1061,18 @@ int main(int argc, char *argv[]) std::cerr << "Incorrect number of positional arguments" << std::endl; return EXIT_INVALIDARGUMENTS; } - pluginImportUri = positionalArgs[1]; + pluginImportUri = positionalArgs.at(1); pluginImportVersion = positionalArgs[2]; if (positionalArgs.size() >= 4) - pluginImportPath = positionalArgs[3]; + pluginImportPath = positionalArgs.at(3); } else if (action == Path) { if (positionalArgs.size() != 2 && positionalArgs.size() != 3) { std::cerr << "Incorrect number of positional arguments" << std::endl; return EXIT_INVALIDARGUMENTS; } - pluginImportPath = QDir::fromNativeSeparators(positionalArgs[1]); + pluginImportPath = QDir::fromNativeSeparators(positionalArgs.at(1)); if (positionalArgs.size() == 3) - pluginImportVersion = positionalArgs[2]; + pluginImportVersion = positionalArgs.at(2); } else if (action == Builtins) { if (positionalArgs.size() != 1) { std::cerr << "Incorrect number of positional arguments" << std::endl; @@ -1094,7 +1094,7 @@ int main(int argc, char *argv[]) QStringList mergeDependencies; QString mergeComponents; if (!mergeFile.isEmpty()) { - QStringList merge = readQmlTypes(mergeFile); + const QStringList merge = readQmlTypes(mergeFile); if (!merge.isEmpty()) { QRegularExpression re("(\\w+\\.*\\w*\\s*\\d+\\.\\d+)"); QRegularExpressionMatchIterator i = re.globalMatch(merge[1]); @@ -1167,7 +1167,7 @@ int main(int argc, char *argv[]) } } else if (pluginImportUri == QLatin1String("QtQml")) { bool ok = false; - const uint major = pluginImportVersion.split('.')[0].toUInt(&ok, 10); + const uint major = pluginImportVersion.splitRef('.').at(0).toUInt(&ok, 10); if (!ok) { std::cerr << "Malformed version string \""<< qPrintable(pluginImportVersion) << "\"." << std::endl; diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp index 048c92bb93..668cb3ce2d 100644 --- a/tools/qmlprofiler/qmlprofilerdata.cpp +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -403,23 +403,23 @@ void QmlProfilerData::computeQmlTime() int level = minimumLevel; for (int i = 0; i < d->startInstanceList.count(); i++) { - qint64 st = d->startInstanceList[i].startTime; + qint64 st = d->startInstanceList.at(i).startTime; - if (d->startInstanceList[i].data->rangeType == QQmlProfilerDefinitions::Painting) { + if (d->startInstanceList.at(i).data->rangeType == QQmlProfilerDefinitions::Painting) { continue; } // general level - if (endtimesPerLevel[level] > st) { + if (endtimesPerLevel.value(level) > st) { level++; } else { while (level > minimumLevel && endtimesPerLevel[level-1] <= st) level--; } - endtimesPerLevel[level] = st + d->startInstanceList[i].duration; + endtimesPerLevel[level] = st + d->startInstanceList.at(i).duration; if (level == minimumLevel) { - d->qmlMeasuredTime += d->startInstanceList[i].duration; + d->qmlMeasuredTime += d->startInstanceList.at(i).duration; } } } -- cgit v1.2.3 From fd57e1449dc974417f3409a4da7338d7e8a6e7c8 Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Wed, 17 Aug 2016 12:12:36 -0700 Subject: allow quick test events to directly target a Window QuickTestEvent::eventWindow() is used to determine the window a QObject-based item cast to a QQuickItem is associated with. The window is then used to dispatch simulated events and calculate item positions. This change allows a quick Window or other QWindow-based object to be used directly. Change-Id: I85866ca4c79d5b6a48bac554608cad348aeb6e98 Reviewed-by: Shawn Rutledge Reviewed-by: Mitch Curtis --- src/qmltest/quicktestevent.cpp | 4 ++++ tests/auto/qmltest/events/tst_events.qml | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/qmltest/quicktestevent.cpp b/src/qmltest/quicktestevent.cpp index f722fab6c4..966a60671d 100644 --- a/src/qmltest/quicktestevent.cpp +++ b/src/qmltest/quicktestevent.cpp @@ -317,6 +317,10 @@ bool QuickTestEvent::mouseMove QWindow *QuickTestEvent::eventWindow(QObject *item) { + QWindow * window = qobject_cast(item); + if (window) + return window; + QQuickItem *quickItem = qobject_cast(item); if (quickItem) return quickItem->window(); diff --git a/tests/auto/qmltest/events/tst_events.qml b/tests/auto/qmltest/events/tst_events.qml index e655c26c7d..d9868a316c 100644 --- a/tests/auto/qmltest/events/tst_events.qml +++ b/tests/auto/qmltest/events/tst_events.qml @@ -27,6 +27,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.Window 2.0 import QtTest 1.1 Rectangle { @@ -56,6 +57,16 @@ Rectangle { signalName: "doubleClickSignalHelper" } + Window { + id: sub + visible: true + property bool clicked: false + MouseArea { + anchors.fill: parent + onClicked: sub.clicked = true + } + } + MouseArea { anchors.fill: parent onClicked: { @@ -89,6 +100,11 @@ Rectangle { tryCompare(top, "mouseHasBeenClicked", true, 10000) } + function test_mouse_click_subwindow() { + mouseClick(sub) + tryCompare(sub, "clicked", true, 10000) + } + function test_mouse_doubleclick() { doubleClickSpy.clear() mouseDoubleClickSequence(top, 25, 30) -- cgit v1.2.3 From 34c82d54b70409202b36c8ec51442fa56014ba8a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 19 Aug 2016 11:33:22 +0200 Subject: V4: Replace a QSet with a QVector in calculateOptionalJumps Instead of storing a bunch of statement IDs in a QSet, the parent basic block of the terminator statement (specifically: the jump) is used as an index into a bit vector. If the bit is set, this indicates that the jump can be omitted. This reduces the number of allocations from 1 hash node per optional jump, to 1 per function. The allocation will also be smaller then a hash node. Change-Id: Ia34468534b96dd9cefa837523bf89ad233de92e8 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4isel_moth.cpp | 4 ++-- src/qml/compiler/qv4isel_moth_p.h | 3 ++- src/qml/compiler/qv4ssa.cpp | 36 ++++++++++++++++++++++++++---------- src/qml/compiler/qv4ssa_p.h | 3 ++- src/qml/jit/qv4isel_masm.cpp | 4 ++-- src/qml/jit/qv4isel_masm_p.h | 3 ++- src/qml/jsruntime/qv4util_p.h | 6 ++++++ 7 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index fda5b4cdd0..d54d939e93 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -209,7 +209,7 @@ void InstructionSelection::run(int functionIndex) ConvertTemps().toStackSlots(_function); } - QSet removableJumps = opt.calculateOptionalJumps(); + BitVector removableJumps = opt.calculateOptionalJumps(); qSwap(_removableJumps, removableJumps); IR::Stmt *cs = 0; @@ -948,7 +948,7 @@ void InstructionSelection::visitJump(IR::Jump *s) { if (s->target == _nextBlock) return; - if (_removableJumps.contains(s)) + if (_removableJumps.at(_block->index())) return; addDebugInstruction(); diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index 2d2bb91228..1d9befd7ff 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include "qv4instr_moth_p.h" @@ -197,7 +198,7 @@ private: uchar *_codeNext; uchar *_codeEnd; - QSet _removableJumps; + BitVector _removableJumps; IR::Stmt *_currentStatement; QScopedPointer compilationUnit; diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 283fb24897..6d55b43354 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -5463,14 +5463,30 @@ LifeTimeIntervals::Ptr Optimizer::lifeTimeIntervals() const return lifeRanges.intervals(); } -QSet Optimizer::calculateOptionalJumps() +static int countPhis(BasicBlock *bb) { - QSet optional; - QSet reachableWithoutJump; + int count = 0; + for (Stmt *s : bb->statements()) { + if (s->isa()) + ++count; + else + break; + } + return count; +} + +// Basic blocks can have only 1 terminator. This function returns a bit vector, where a 1 on a +// certain index indicates that the terminator (jump) at the end of the basic block with that index +// can be omitted. +BitVector Optimizer::calculateOptionalJumps() +{ const int maxSize = function->basicBlockCount(); - optional.reserve(maxSize); - reachableWithoutJump.reserve(maxSize); + BitVector optional(maxSize, false); + if (maxSize < 2) + return optional; + + BitVector reachableWithoutJump(maxSize, false); for (int i = maxSize - 1; i >= 0; --i) { BasicBlock *bb = function->basicBlock(i); @@ -5478,17 +5494,17 @@ QSet Optimizer::calculateOptionalJumps() continue; if (Jump *jump = bb->statements().last()->asJump()) { - if (reachableWithoutJump.contains(jump->target)) { - if (bb->statements().size() > 1) + if (reachableWithoutJump.at(jump->target->index())) { + if (bb->statements().size() - countPhis(bb)> 1) reachableWithoutJump.clear(); - optional.insert(jump); - reachableWithoutJump.insert(bb); + optional.setBit(bb->index()); + reachableWithoutJump.setBit(bb->index()); continue; } } reachableWithoutJump.clear(); - reachableWithoutJump.insert(bb); + reachableWithoutJump.setBit(bb->index()); } return optional; diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index 386990a9f5..0f8f9a401e 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -53,6 +53,7 @@ #include "qv4jsir_p.h" #include "qv4isel_util_p.h" +#include "qv4util_p.h" #include QT_BEGIN_NAMESPACE @@ -235,7 +236,7 @@ public: LifeTimeIntervals::Ptr lifeTimeIntervals() const; - QSet calculateOptionalJumps(); + BitVector calculateOptionalJumps(); static void showMeTheCode(Function *function, const char *marker); diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 9759b72794..0baf7580da 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -294,7 +294,7 @@ void InstructionSelection::run(int functionIndex) IR::Optimizer::showMeTheCode(_function, "After stack slot allocation"); calculateRegistersToSave(Assembler::getRegisterInfo()); // FIXME: this saves all registers. We can probably do with a subset: those that are not used by the register allocator. } - QSet removableJumps = opt.calculateOptionalJumps(); + BitVector removableJumps = opt.calculateOptionalJumps(); qSwap(_removableJumps, removableJumps); Assembler* oldAssembler = _as; @@ -1390,7 +1390,7 @@ void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, I void InstructionSelection::visitJump(IR::Jump *s) { - if (!_removableJumps.contains(s)) + if (!_removableJumps.at(_block->index())) _as->jumpToBlock(_block, s->target); } diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 5bca879a77..0e909820e7 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -54,6 +54,7 @@ #include "private/qv4jsir_p.h" #include "private/qv4isel_p.h" #include "private/qv4isel_util_p.h" +#include "private/qv4util_p.h" #include "private/qv4value_p.h" #include "private/qv4lookup_p.h" @@ -272,7 +273,7 @@ private: } IR::BasicBlock *_block; - QSet _removableJumps; + BitVector _removableJumps; Assembler* _as; QScopedPointer compilationUnit; diff --git a/src/qml/jsruntime/qv4util_p.h b/src/qml/jsruntime/qv4util_p.h index 59c12c5e46..2669a3e4bf 100644 --- a/src/qml/jsruntime/qv4util_p.h +++ b/src/qml/jsruntime/qv4util_p.h @@ -90,6 +90,9 @@ public: : bits(size, value) {} + void clear() + { bits = std::vector(bits.size(), false); } + void reserve(int size) { bits.reserve(size); } @@ -153,6 +156,9 @@ public: : bits(size, value) {} + void clear() + { bits = QBitArray(bits.size(), false); } + void reserve(int size) { Q_UNUSED(size); } -- cgit v1.2.3 From 48ccbc4286a490351433a64f59123c24ce88491f Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 23 Aug 2016 12:15:22 +0300 Subject: optimize string usage: use prepend() less Replace prepend() method with QStringBuilder Change-Id: I2bf262d98d1f98e37ba19ba8a4725cabd5a0a288 Reviewed-by: Shawn Rutledge Reviewed-by: Ulf Hermann --- src/qml/jit/qv4isel_masm.cpp | 17 +++++------------ src/quick/items/qquicktextcontrol.cpp | 4 ++-- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 0baf7580da..2cb3596c33 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -113,8 +113,7 @@ static void printDisassembledOutputWithCalls(QByteArray processedOutput, const Q { for (QHash::ConstIterator it = functions.begin(), end = functions.end(); it != end; ++it) { - QByteArray ptrString = QByteArray::number(quintptr(it.key()), 16); - ptrString.prepend("0x"); + const QByteArray ptrString = "0x" + QByteArray::number(quintptr(it.key()), 16); int idx = processedOutput.indexOf(ptrString); if (idx < 0) continue; @@ -197,11 +196,8 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) WTF::setDataFile(new QIODevicePrintStream(&buf)); name = _function->name->toUtf8(); - if (name.isEmpty()) { - name = QByteArray::number(quintptr(_function), 16); - name.prepend("IR::Function(0x"); - name.append(')'); - } + if (name.isEmpty()) + name = "IR::Function(0x" + QByteArray::number(quintptr(_function), 16) + ')'; codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); WTF::setDataFile(stderr); @@ -238,11 +234,8 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) // this may have been pre-populated, if QV4_SHOW_ASM was on if (name.isEmpty()) { name = _function->name->toUtf8(); - if (name.isEmpty()) { - name = QByteArray::number(quintptr(_function), 16); - name.prepend("IR::Function(0x"); - name.append(')'); - } + if (name.isEmpty()) + name = "IR::Function(0x" + QByteArray::number(quintptr(_function), 16) + ')'; } fprintf(pmap, "%llx %x %.*s\n", diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index ef7485a8e9..fe29249934 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -1664,8 +1664,8 @@ void QQuickTextControl::insertFromMimeData(const QMimeData *source) #ifndef QT_NO_TEXTHTMLPARSER if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) { // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore). - QString richtext = QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext"))); - richtext.prepend(QLatin1String("")); + const QString richtext = QLatin1String("") + + QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext"))); fragment = QTextDocumentFragment::fromHtml(richtext, d->doc); hasData = true; } else if (source->hasHtml() && d->acceptRichText) { -- cgit v1.2.3 From 857ff3b187621b86b3177313f3cfc88f050c8a5f Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 23 Aug 2016 14:59:21 +0300 Subject: DynamicRoleModelNode: mark getValue() method as const This method does not modify the object. Change-Id: I300b65123011024b7dc427527d4af706937c18fb Reviewed-by: Ulf Hermann --- src/qml/types/qqmllistmodel_p_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index d3ff032ae9..3628372f50 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -88,7 +88,7 @@ public: void updateValues(const QVariantMap &object, QVector &roles); - QVariant getValue(const QString &name) + QVariant getValue(const QString &name) const { return m_meta->value(name.toUtf8()); } -- cgit v1.2.3 From 8b7dbfe1e0c0d4b8d0984fb381f1a0bb6b566e76 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 19 Aug 2016 10:13:45 +0200 Subject: V4: Correctly format on Number.toPrecision(n) The ecmascript standard mandates that we add trailing zeroes if the given precision is greater than the number of digits available. The only way to request this from QLocale is currently QString::asprintf(), which adds a few other incompatibilities that are easier to 'fix' by editing the resulting string. Thus we use it as a stop gap measure until we can expose better API from qtbase. Task-number: QTBUG-55358 Change-Id: Iafc11f21abb341fbe458ad75b46b4222ae5bc1db Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4numberobject.cpp | 22 ++++++++++++++++++++-- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 9 +++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index ab3e03b183..0e653c18cb 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -263,12 +263,30 @@ ReturnedValue NumberPrototype::method_toPrecision(CallContext *ctx) if (!ctx->argc() || ctx->args()[0].isUndefined()) return RuntimeHelpers::toString(scope.engine, v); - double precision = ctx->args()[0].toInt32(); + int precision = ctx->args()[0].toInt32(); if (precision < 1 || precision > 21) { ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range"))); return ctx->engine()->throwRangeError(error); } - QString result = NumberLocale::instance()->toString(v->asDouble(), 'g', precision); + // TODO: Once we get a NumberOption to retain trailing zeroes, replace the code below with: + // QString result = NumberLocale::instance()->toString(v->asDouble(), 'g', precision); + QByteArray format = "%#." + QByteArray::number(precision) + "g"; + QString result = QString::asprintf(format.constData(), v->asDouble()); + if (result.endsWith(QLatin1Char('.'))) { + // This is 'f' notation, not 'e'. + result.chop(1); + } else { + int ePos = result.indexOf(QLatin1Char('e')); + if (ePos != -1) { + Q_ASSERT(ePos + 2 < result.length()); // always '+' or '-', and number, after 'e' + Q_ASSERT(ePos > 0); // 'e' is not the first character + + if (result.at(ePos + 2) == QLatin1Char('0')) // Drop leading zeroes in exponent + result = result.remove(ePos + 2, 1); + if (result.at(ePos - 1) == QLatin1Char('.')) // Drop trailing dots before 'e' + result = result.remove(ePos - 1, 1); + } + } return scope.engine->newString(result)->asReturnedValue(); } diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index b1d19b5796..0001a22103 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -2003,6 +2003,15 @@ void tst_QJSEngine::jsNumberClass() QJSValue ret = eng.evaluate("new Number(123).toPrecision()"); QVERIFY(ret.isString()); QCOMPARE(ret.toString(), QString::fromLatin1("123")); + ret = eng.evaluate("new Number(42).toPrecision(1)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("4e+1")); + ret = eng.evaluate("new Number(42).toPrecision(2)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("42")); + ret = eng.evaluate("new Number(42).toPrecision(3)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("42.0")); } } -- cgit v1.2.3 From 9e5053f2d78c6c0fab5e43840e1b7253234c93c7 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 19 Aug 2016 12:06:17 +0200 Subject: V4: Sort unprocessed ranges in reverse order This prevents the copy overhead that a removeFirst() would impose. Change-Id: I4d3507784792e9bc3c4347f43ea6fdb087c6f201 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4ssa.cpp | 9 --------- src/qml/compiler/qv4ssa_p.h | 11 +++++++++++ src/qml/jit/qv4regalloc.cpp | 21 +++++++++++++-------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 283fb24897..90e9833791 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -5181,15 +5181,6 @@ void LifeTimeInterval::dump(QTextStream &out) const { out << " (register " << _reg << ")"; } -bool LifeTimeInterval::lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2) { - if (r1->_ranges.first().start == r2->_ranges.first().start) { - if (r1->isSplitFromInterval() == r2->isSplitFromInterval()) - return r1->_ranges.last().end < r2->_ranges.last().end; - else - return r1->isSplitFromInterval(); - } else - return r1->_ranges.first().start < r2->_ranges.first().start; -} bool LifeTimeInterval::lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2) { diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index 386990a9f5..b5f7728f14 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -146,6 +146,17 @@ public: } }; +inline bool LifeTimeInterval::lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2) +{ + if (r1->_ranges.first().start == r2->_ranges.first().start) { + if (r1->isSplitFromInterval() == r2->isSplitFromInterval()) + return r1->_ranges.last().end < r2->_ranges.last().end; + else + return r1->isSplitFromInterval(); + } else + return r1->_ranges.first().start < r2->_ranges.first().start; +} + class LifeTimeIntervals { Q_DISABLE_COPY(LifeTimeIntervals) diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index deef719b67..a0df6a26f5 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -814,7 +814,7 @@ class ResolutionPhase Q_DISABLE_COPY(ResolutionPhase) LifeTimeIntervals::Ptr _intervals; - QVector _unprocessed; + QVector _unprocessedReverseOrder; IR::Function *_function; const std::vector &_assignedSpillSlots; QHash _intervalForTemp; @@ -829,20 +829,20 @@ class ResolutionPhase QHash > _liveAtEnd; public: - ResolutionPhase(const QVector &unprocessed, + ResolutionPhase(QVector &&unprocessedReversedOrder, const LifeTimeIntervals::Ptr &intervals, IR::Function *function, const std::vector &assignedSpillSlots, const QVector &intRegs, const QVector &fpRegs) : _intervals(intervals) + , _unprocessedReverseOrder(unprocessedReversedOrder) , _function(function) , _assignedSpillSlots(assignedSpillSlots) , _intRegs(intRegs) , _fpRegs(fpRegs) , _currentStmt(0) { - _unprocessed = unprocessed; _liveAtStart.reserve(function->basicBlockCount()); _liveAtEnd.reserve(function->basicBlockCount()); } @@ -953,8 +953,8 @@ private: if (position == Stmt::InvalidId) return; - while (!_unprocessed.isEmpty()) { - const LifeTimeInterval *i = _unprocessed.constFirst(); + while (!_unprocessedReverseOrder.isEmpty()) { + const LifeTimeInterval *i = _unprocessedReverseOrder.constLast(); if (i->start() > position) break; @@ -962,7 +962,7 @@ private: _intervalForTemp[i->temp()] = i; // qDebug() << "-- Activating interval for temp" << i->temp().index; - _unprocessed.removeFirst(); + _unprocessedReverseOrder.removeLast(); } } @@ -1314,8 +1314,13 @@ void RegisterAllocator::run(IR::Function *function, const Optimizer &opt) if (DebugRegAlloc) dump(function); - std::sort(_handled.begin(), _handled.end(), LifeTimeInterval::lessThan); - ResolutionPhase(_handled, _lifeTimeIntervals, function, _assignedSpillSlots, _normalRegisters, _fpRegisters).run(); + // sort the ranges in reverse order, so the ResolutionPhase can take from the end (and thereby + // prevent the copy overhead that taking from the beginning would give). + std::sort(_handled.begin(), _handled.end(), + [](const LifeTimeInterval *r1, const LifeTimeInterval *r2) -> bool { + return LifeTimeInterval::lessThan(r2, r1); + }); + ResolutionPhase(std::move(_handled), _lifeTimeIntervals, function, _assignedSpillSlots, _normalRegisters, _fpRegisters).run(); function->tempCount = *std::max_element(_assignedSpillSlots.begin(), _assignedSpillSlots.end()) + 1; -- cgit v1.2.3 From 7e14b531b294650733a61a9365eb9ef74f8a7a90 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 19 Aug 2016 12:37:05 +0200 Subject: V4: Store live intervals in an array instead of a QHash Change-Id: Ibc175e853817231f36abf335bdb41faa9abe887a Reviewed-by: Simon Hausmann --- src/qml/jit/qv4regalloc.cpp | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index a0df6a26f5..a93e852d46 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -817,7 +817,7 @@ class ResolutionPhase QVector _unprocessedReverseOrder; IR::Function *_function; const std::vector &_assignedSpillSlots; - QHash _intervalForTemp; + std::vector _liveIntervals; const QVector &_intRegs; const QVector &_fpRegs; @@ -825,8 +825,8 @@ class ResolutionPhase std::vector _loads; std::vector _stores; - QHash > _liveAtStart; - QHash > _liveAtEnd; + QHash > _liveAtStart; + QHash > _liveAtEnd; public: ResolutionPhase(QVector &&unprocessedReversedOrder, @@ -883,7 +883,7 @@ private: cleanOldIntervals(_intervals->startPosition(bb)); addNewIntervals(_intervals->startPosition(bb)); - _liveAtStart[bb] = _intervalForTemp.values(); + _liveAtStart[bb] = _liveIntervals; for (int i = 0, ei = statements.size(); i != ei; ++i) { _currentStmt = statements.at(i); @@ -905,14 +905,14 @@ private: } cleanOldIntervals(_intervals->endPosition(bb)); - _liveAtEnd[bb] = _intervalForTemp.values(); + _liveAtEnd[bb] = _liveIntervals; if (DebugRegAlloc) { QBuffer buf; buf.open(QIODevice::WriteOnly); QTextStream os(&buf); os << "Intervals live at the start of L" << bb->index() << ":" << endl; - if (_liveAtStart[bb].isEmpty()) + if (_liveAtStart[bb].empty()) os << "\t(none)" << endl; for (const LifeTimeInterval *i : _liveAtStart.value(bb)) { os << "\t"; @@ -920,7 +920,7 @@ private: os << endl; } os << "Intervals live at the end of L" << bb->index() << ":" << endl; - if (_liveAtEnd[bb].isEmpty()) + if (_liveAtEnd[bb].empty()) os << "\t(none)" << endl; for (const LifeTimeInterval *i : _liveAtEnd.value(bb)) { os << "\t"; @@ -935,9 +935,19 @@ private: } + const LifeTimeInterval *findLiveInterval(Temp *t) const + { + for (const LifeTimeInterval *lti : _liveIntervals) { + if (lti->temp() == *t) + return lti; + } + + return nullptr; + } + void maybeGenerateSpill(Temp *t) { - const LifeTimeInterval *i = _intervalForTemp[*t]; + const LifeTimeInterval *i = findLiveInterval(t); if (i->reg() == LifeTimeInterval::InvalidRegister) return; @@ -959,7 +969,7 @@ private: break; Q_ASSERT(!i->isFixedInterval()); - _intervalForTemp[i->temp()] = i; + _liveIntervals.push_back(i); // qDebug() << "-- Activating interval for temp" << i->temp().index; _unprocessedReverseOrder.removeLast(); @@ -968,11 +978,12 @@ private: void cleanOldIntervals(int position) { - QMutableHashIterator it(_intervalForTemp); - while (it.hasNext()) { - const LifeTimeInterval *i = it.next().value(); - if (i->end() < position || i->isFixedInterval()) - it.remove(); + for (size_t it = 0; it != _liveIntervals.size(); ) { + const LifeTimeInterval *lti = _liveIntervals.at(it); + if (lti->end() < position || lti->isFixedInterval()) + _liveIntervals.erase(_liveIntervals.begin() + it); + else + ++it; } } @@ -1198,7 +1209,7 @@ private: if (t->kind != Temp::VirtualRegister) return; - const LifeTimeInterval *i = _intervalForTemp[*t]; + const LifeTimeInterval *i = findLiveInterval(t); Q_ASSERT(i->isValid()); if (_currentStmt != 0 && i->start() == usePosition(_currentStmt)) { -- cgit v1.2.3 From 358fb253eafd809e440066b526168513fce56bef Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Wed, 24 Aug 2016 10:49:13 +0300 Subject: Replace QStringLiteral with QL1S in QStringBuilder Saves some text size. Change-Id: Ib521959784071a79462cf7962657d8158cf130a2 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 3 ++- src/qml/jsruntime/qv4engine.cpp | 2 +- src/qml/jsruntime/qv4qobjectwrapper.cpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 31b964897f..1cc0dcc189 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1018,7 +1018,8 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST CompiledFunctionOrExpression *expr = New(); expr->node = statement; - expr->nameIndex = registerString(QStringLiteral("expression for ") + stringAt(binding->propertyNameIndex)); + expr->nameIndex = registerString(QLatin1String("expression for ") + + stringAt(binding->propertyNameIndex)); expr->disableAcceleratedLookups = false; const int index = bindingsTarget()->functionsAndExpressions->append(expr); binding->value.compiledScriptIndex = index; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index f8110d29f6..a9ffda56f1 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1021,7 +1021,7 @@ ReturnedValue ExecutionEngine::throwURIError(const Value &msg) ReturnedValue ExecutionEngine::throwUnimplemented(const QString &message) { Scope scope(this); - ScopedValue v(scope, newString(QStringLiteral("Unimplemented ") + message)); + ScopedValue v(scope, newString(QLatin1String("Unimplemented ") + message)); v = newErrorObject(v); return throwError(v); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 624c37a694..633c5f60c9 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1964,8 +1964,8 @@ ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const { ExecutionEngine *v4 = engine(); const QMetaObject* mo = d()->metaObject; if (d()->constructors.isEmpty()) { - return v4->throwTypeError(QStringLiteral("%1 has no invokable constructor") - .arg(QLatin1String(mo->className()))); + return v4->throwTypeError(QLatin1String(mo->className()) + + QLatin1String(" has no invokable constructor")); } Scope scope(v4); -- cgit v1.2.3 From 3b08205d2dea5a324b52308ed36da36b1989ab78 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 19 Aug 2016 12:47:29 +0200 Subject: V4: Store per-block interval info in a vector instead of a QHash The basic blocks have an index, starting at 0 and going up monotonously. So this can be used as an index into a vector. This removes a number of hash node allocations. Change-Id: If69523d8ad44f6b8c7895111ddfa9787fef0ddc1 Reviewed-by: Simon Hausmann --- src/qml/jit/qv4regalloc.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index a93e852d46..350ab7e0c6 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -825,8 +825,8 @@ class ResolutionPhase std::vector _loads; std::vector _stores; - QHash > _liveAtStart; - QHash > _liveAtEnd; + std::vector > _liveAtStart; + std::vector > _liveAtEnd; public: ResolutionPhase(QVector &&unprocessedReversedOrder, @@ -843,8 +843,8 @@ public: , _fpRegs(fpRegs) , _currentStmt(0) { - _liveAtStart.reserve(function->basicBlockCount()); - _liveAtEnd.reserve(function->basicBlockCount()); + _liveAtStart.resize(function->basicBlockCount()); + _liveAtEnd.resize(function->basicBlockCount()); } void run() { @@ -883,7 +883,7 @@ private: cleanOldIntervals(_intervals->startPosition(bb)); addNewIntervals(_intervals->startPosition(bb)); - _liveAtStart[bb] = _liveIntervals; + _liveAtStart[bb->index()] = _liveIntervals; for (int i = 0, ei = statements.size(); i != ei; ++i) { _currentStmt = statements.at(i); @@ -905,24 +905,24 @@ private: } cleanOldIntervals(_intervals->endPosition(bb)); - _liveAtEnd[bb] = _liveIntervals; + _liveAtEnd[bb->index()] = _liveIntervals; if (DebugRegAlloc) { QBuffer buf; buf.open(QIODevice::WriteOnly); QTextStream os(&buf); os << "Intervals live at the start of L" << bb->index() << ":" << endl; - if (_liveAtStart[bb].empty()) + if (_liveAtStart[bb->index()].empty()) os << "\t(none)" << endl; - for (const LifeTimeInterval *i : _liveAtStart.value(bb)) { + for (const LifeTimeInterval *i : _liveAtStart.at(bb->index())) { os << "\t"; i->dump(os); os << endl; } os << "Intervals live at the end of L" << bb->index() << ":" << endl; - if (_liveAtEnd[bb].empty()) + if (_liveAtEnd[bb->index()].empty()) os << "\t(none)" << endl; - for (const LifeTimeInterval *i : _liveAtEnd.value(bb)) { + for (const LifeTimeInterval *i : _liveAtEnd.at(bb->index())) { os << "\t"; i->dump(os); os << endl; @@ -1030,7 +1030,7 @@ private: int successorStart = _intervals->startPosition(successor); Q_ASSERT(successorStart > 0); - for (const LifeTimeInterval *it : _liveAtStart.value(successor)) { + for (const LifeTimeInterval *it : _liveAtStart.at(successor->index())) { bool isPhiTarget = false; Expr *moveFrom = 0; @@ -1044,7 +1044,7 @@ private: Temp *t = opd->asTemp(); Q_ASSERT(t); - for (const LifeTimeInterval *it2 : _liveAtEnd.value(predecessor)) { + for (const LifeTimeInterval *it2 : _liveAtEnd.at(predecessor->index())) { if (it2->temp() == *t && it2->reg() != LifeTimeInterval::InvalidRegister && it2->covers(predecessorEnd)) { @@ -1059,7 +1059,7 @@ private: } } } else { - for (const LifeTimeInterval *predIt : _liveAtEnd.value(predecessor)) { + for (const LifeTimeInterval *predIt : _liveAtEnd.at(predecessor->index())) { if (predIt->temp() == it->temp()) { if (predIt->reg() != LifeTimeInterval::InvalidRegister && predIt->covers(predecessorEnd)) { -- cgit v1.2.3 From 92debe6b2c69c959cb2bc27bf788edcef5e9f292 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 19 Aug 2016 13:27:33 +0200 Subject: V4: Replace foreach with for in masm Change-Id: Idb01fb9272ccbe69263d1152996ab18c72a393fe Reviewed-by: Simon Hausmann --- src/qml/jit/qv4isel_masm.cpp | 14 +++++++------- src/qml/jit/qv4targetplatform_p.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 9759b72794..8211dab0b3 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -151,7 +151,7 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) IR::BasicBlock *block = it.key(); Label target = _addrs.value(block); Q_ASSERT(target.isSet()); - foreach (Jump jump, it.value()) + for (Jump jump : qAsConst(it.value())) jump.linkTo(target, this); } } @@ -159,11 +159,11 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) JSC::JSGlobalData dummy(_executableAllocator); JSC::LinkBuffer linkBuffer(dummy, this, 0); - foreach (const DataLabelPatch &p, _dataLabelPatches) + for (const DataLabelPatch &p : qAsConst(_dataLabelPatches)) linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); // link exception handlers - foreach(Jump jump, exceptionPropagationJumps) + for (Jump jump : qAsConst(exceptionPropagationJumps)) linkBuffer.link(jump, linkBuffer.locationOf(exceptionReturnLabel)); { @@ -173,7 +173,7 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) IR::BasicBlock *block = it.key(); Label target = _addrs.value(block); Q_ASSERT(target.isSet()); - foreach (DataLabelPtr label, it.value()) + for (DataLabelPtr label : qAsConst(it.value())) linkBuffer.patch(label, linkBuffer.locationOf(target)); } } @@ -188,7 +188,7 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) if (showCode) { QHash functions; #ifndef QT_NO_DEBUG - foreach (CallInfo call, _callInfos) + for (CallInfo call : qAsConst(_callInfos)) functions[linkBuffer.locationOf(call.label).dataLocation()] = call.functionName; #endif @@ -344,7 +344,7 @@ void InstructionSelection::run(int functionIndex) continue; _as->registerBlock(_block, nextBlock); - foreach (IR::Stmt *s, _block->statements()) { + for (IR::Stmt *s : _block->statements()) { if (s->location.isValid()) { if (int(s->location.startLine) != lastLine) { _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); @@ -1689,7 +1689,7 @@ void InstructionSelection::calculateRegistersToSave(const RegisterInformation &u regularRegistersToSave.clear(); fpRegistersToSave.clear(); - foreach (const RegisterInfo &ri, Assembler::getRegisterInfo()) { + for (const RegisterInfo &ri : Assembler::getRegisterInfo()) { #if defined(RESTORE_EBX_ON_CALL) if (ri.isRegularRegister() && ri.reg() == JSC::X86Registers::ebx) { regularRegistersToSave.append(ri); diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h index 6f0a7374c3..7e265258d5 100644 --- a/src/qml/jit/qv4targetplatform_p.h +++ b/src/qml/jit/qv4targetplatform_p.h @@ -563,7 +563,7 @@ public: #endif // Linux on MIPS (32 bit) public: // utility functions - static RegisterInformation getRegisterInfo() + static const RegisterInformation getRegisterInfo() { static const RegisterInformation info = getPlatformRegisterInfo(); -- cgit v1.2.3 From d9c19faccda88dac54c6737f71b8e54ef2aec9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Martins?= Date: Fri, 12 Aug 2016 18:49:14 +0100 Subject: macOS: Check if sRGB is supported before activating the shader Doing it in QSG24BitTextMaskShader::initialize() assumed that the FBO didn't change afterwards, but FBO can change (due to ShaderEffectSource or item.grabToImage()), resulting in qt_sRGB_to_linear_RGB() getting called for the case of the FBO not supporting sRGB. The work done in 1e18a4f985f6ec is still a good idea (enabling sRGB for all FBOs), and needed for exact rendering but this patch fixes an orthogonal issue. Change-Id: I98b12347e9ef60f46d8bcb20ac5d0d2d7b0c6f57 Task-Id: QTBUG-52906 Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/qsgdefaultglyphnode_p.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp index 5f4e54b218..3eba29ba41 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -203,6 +204,7 @@ public: void activate(); void deactivate(); + bool useSRGB() const; uint m_useSRGB : 1; }; @@ -222,11 +224,26 @@ void QSG24BitTextMaskShader::initialize() } } +bool QSG24BitTextMaskShader::useSRGB() const +{ +#ifdef Q_OS_MACOS + if (!m_useSRGB) + return false; + + // m_useSRGB is true, but if some QOGLFBO was bound check it's texture format: + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QOpenGLFramebufferObject *qfbo = QOpenGLContextPrivate::get(ctx)->qgl_current_fbo; + return !qfbo || qfbo->format().internalTextureFormat() == GL_SRGB8_ALPHA8_EXT; +#else + return m_useSRGB; +#endif +} + void QSG24BitTextMaskShader::activate() { QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); funcs->glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR); - if (m_useSRGB) + if (useSRGB()) funcs->glEnable(GL_FRAMEBUFFER_SRGB); } @@ -234,7 +251,7 @@ void QSG24BitTextMaskShader::deactivate() { QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - if (m_useSRGB) + if (useSRGB()) funcs->glDisable(GL_FRAMEBUFFER_SRGB); } @@ -259,7 +276,7 @@ void QSG24BitTextMaskShader::updateState(const RenderState &state, QSGMaterial * if (oldMaterial == 0 || material->color() != oldMaterial->color() || state.isOpacityDirty()) { QVector4D color = material->color(); - if (m_useSRGB) + if (useSRGB()) color = qt_sRGB_to_linear_RGB(color); QOpenGLContext::currentContext()->functions()->glBlendColor(color.x(), color.y(), color.z(), color.w()); color = qsg_premultiply(color, state.opacity()); -- cgit v1.2.3 From 4e9fc0129d6b4326c2159e4fafa42a3df9d35e0a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 8 Aug 2016 13:25:15 +0200 Subject: QML: Switch from using accessors to static_metacall Change-Id: I7a781eb3cb0af9c68b385af5752555bd9cb313d4 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache.cpp | 34 ++++++++++---------- src/qml/qml/qqmlpropertycache_p.h | 67 +++++++++++++++++++++++++++------------ 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 5c53e342f3..ed00eddb30 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -150,7 +150,7 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p) Q_ASSERT(p.revision() <= Q_INT16_MAX); setRevision(p.revision()); - _flags = fastFlagsForProperty(p); + setFlags(fastFlagsForProperty(p)); int type = static_cast(p.type()); if (type == QMetaType::QObjectStar) { @@ -171,7 +171,7 @@ void QQmlPropertyData::load(const QMetaProperty &p, QQmlEngine *engine) setPropType(p.userType()); setCoreIndex(p.propertyIndex()); setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); - _flags = fastFlagsForProperty(p); + setFlags(fastFlagsForProperty(p)); flagsForPropertyType(propType(), engine, _flags); Q_ASSERT(p.revision() <= Q_INT16_MAX); setRevision(p.revision()); @@ -344,7 +344,7 @@ void QQmlPropertyCache::appendProperty(const QString &name, QQmlPropertyData::Fl data.setPropType(propType); data.setCoreIndex(coreIndex); data.setNotifyIndex(notifyIndex); - data._flags = flags; + data.setFlags(flags); QQmlPropertyData *old = findNamedProperty(name); if (old) @@ -363,7 +363,7 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag QQmlPropertyData data; data.setPropType(QVariant::Invalid); data.setCoreIndex(coreIndex); - data._flags = flags; + data.setFlags(flags); data.setArguments(nullptr); QQmlPropertyData handler = data; @@ -409,7 +409,7 @@ void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flag args->argumentsValid = true; data.setArguments(args); - data._flags = flags; + data.setFlags(flags); QQmlPropertyData *old = findNamedProperty(name); if (old) @@ -568,9 +568,9 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QQmlPropertyData *sigdata = 0; if (m.methodType() == QMetaMethod::Signal) - data->_flags = signalFlags; + data->setFlags(signalFlags); else - data->_flags = methodFlags; + data->setFlags(methodFlags); data->lazyLoad(m); data->_flags.isDirect = !dynamicMetaObject; @@ -650,7 +650,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart]; - data->_flags = propertyFlags; + data->setFlags(propertyFlags); data->lazyLoad(p); data->_flags.isDirect = !dynamicMetaObject; @@ -672,16 +672,18 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, setNamedProperty(propName, ii, data, (old != 0)); } - QQmlAccessorProperties::Property *accessorProperty = accessorProperties.property(str); - - // Fast properties may not be overrides or revisioned - Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision() == 0)); + bool isGadget = true; + for (const QMetaObject *it = metaObject; it != nullptr; it = it->superClass()) { + if (it == &QObject::staticMetaObject) + isGadget = false; + } - if (accessorProperty) { - data->setAccessors(accessorProperty->accessors); - } else if (old) { + if (isGadget) // always dispatch over a 'normal' meta-call so the QQmlValueType can intercept + data->_flags.isDirect = false; + else + data->trySetStaticMetaCallFunction(metaObject->d.static_metacall, ii - propOffset); + if (old) data->markAsOverrideOf(old); - } } } diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 63a9c63d90..f28b2d0599 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -86,6 +86,8 @@ template class QQmlPropertyCacheAliasCreator; class QQmlPropertyRawData { public: + typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction; + struct Flags { enum Types { OtherType = 0, @@ -100,6 +102,14 @@ public: QVariantType = 9 // Property is a QVariant }; + // The _otherBits (which "pad" the Flags struct to align it nicely) are used + // to store the relative property index. It will only get used when said index fits. See + // trySetStaticMetaCallFunction for details. + // (Note: this padding is done here, because certain compilers have surprising behavior + // when an enum is declared in-between two bit fields.) + enum { BitsLeftInFlags = 10 }; + unsigned _otherBits : BitsLeftInFlags; // align to 32 bits + // Can apply to all properties, except IsFunction unsigned isConstant : 1; // Has CONST flag unsigned isWritable : 1; // Has WRITE function @@ -126,15 +136,18 @@ public: unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved unsigned overrideIndexIsProperty: 1; - unsigned _padding : 10; // align to 32 bits - inline Flags(); inline bool operator==(const Flags &other) const; inline void copyPropertyTypeFlags(Flags from); }; Flags flags() const { return _flags; } - void setFlags(Flags f) { _flags = f; } + void setFlags(Flags f) + { + unsigned otherBits = _flags._otherBits; + _flags = f; + _flags._otherBits = otherBits; + } bool isValid() const { return coreIndex() != -1; } @@ -147,6 +160,7 @@ public: bool isOverridden() const { return _flags.isOverridden; } bool isDirect() const { return _flags.isDirect; } bool hasAccessors() const { return accessors() != nullptr; } + bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; } bool isFunction() const { return _flags.type == Flags::FunctionType; } bool isQObject() const { return _flags.type == Flags::QObjectDerivedType; } bool isEnum() const { return _flags.type == Flags::EnumType; } @@ -226,8 +240,17 @@ public: _metaObjectOffset = qint16(off); } - QQmlAccessors *accessors() const { return _accessors; } - void setAccessors(QQmlAccessors *acc) { _accessors = acc; } + QQmlAccessors *accessors() const { return nullptr; } // TODO: remove in subsequent patch + + StaticMetaCallFunction staticMetaCallFunction() const { return _staticMetaCallFunction; } + void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex) + { + if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) { + _flags._otherBits = relativePropertyIndex; + _staticMetaCallFunction = f; + } + } + quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return _flags._otherBits; } private: Flags _flags; @@ -243,7 +266,7 @@ private: qint16 _metaObjectOffset; QQmlPropertyCacheMethodArguments *_arguments; - QQmlAccessors *_accessors; + StaticMetaCallFunction _staticMetaCallFunction; friend class QQmlPropertyData; friend class QQmlPropertyCache; @@ -286,22 +309,24 @@ public: inline void readPropertyWithArgs(QObject *target, void *args[]) const { - if (hasAccessors()) { - accessors()->read(target, args[0]); - } else { + if (hasStaticMetaCallFunction()) + staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args); + else if (isDirect()) + target->qt_metacall(QMetaObject::ReadProperty, coreIndex(), args); + else QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args); - } } bool writeProperty(QObject *target, void *value, WriteFlags flags) const { - if (flags.testFlag(BypassInterceptor) && hasAccessors() && accessors()->write) { - accessors()->write(target, value); - } else { - int status = -1; - void *argv[] = { value, 0, &status, &flags }; + int status = -1; + void *argv[] = { value, 0, &status, &flags }; + if (flags.testFlag(BypassInterceptor) && hasStaticMetaCallFunction()) + staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv); + else if (flags.testFlag(BypassInterceptor) && isDirect()) + target->qt_metacall(QMetaObject::WriteProperty, coreIndex(), argv); + else QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv); - } return true; } @@ -568,7 +593,8 @@ public: }; QQmlPropertyRawData::Flags::Flags() - : isConstant(false) + : _otherBits(0) + , isConstant(false) , isWritable(false) , isResettable(false) , isAlias(false) @@ -587,7 +613,6 @@ QQmlPropertyRawData::Flags::Flags() , isConstructor(false) , notFullyResolved(false) , overrideIndexIsProperty(false) - , _padding(0) {} bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &other) const @@ -635,7 +660,7 @@ QQmlPropertyData::QQmlPropertyData() setRevision(0); setMetaObjectOffset(-1); setArguments(nullptr); - setAccessors(nullptr); + trySetStaticMetaCallFunction(nullptr, 0); } QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d) @@ -645,7 +670,7 @@ QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d) bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other) { - return _flags == other._flags && + return flags() == other.flags() && propType() == other.propType() && coreIndex() == other.coreIndex() && notifyIndex() == other.notifyIndex() && @@ -752,7 +777,7 @@ QQmlPropertyCache::overrideData(QQmlPropertyData *data) const bool QQmlPropertyCache::isAllowedInRevision(QQmlPropertyData *data) const { - return (data->hasAccessors() || (data->metaObjectOffset() == -1 && data->revision() == 0)) || + return (data->metaObjectOffset() == -1 && data->revision() == 0) || (allowedRevisionCache[data->metaObjectOffset()] >= data->revision()); } -- cgit v1.2.3 From 651ecf9ae413478af622761e89347bb4e6243e0c Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 24 Aug 2016 12:40:23 +0200 Subject: Fix mouse area pressed after removal of pre-grab Qt Quick used to actively "grab" the mouse before delivering events, making the code more complex than necessary. Some items came to rely on the behavior and now lack the ungrab event. This is triggered when inside the delivery of a mouse event, some other item grabs the mouse. If the mouse grab changed to an item that was not the target of the original delivery, the grab must have been stolen, so call mouseUngrabEvent in this case. Test case by J-P Nurmi. Task-number: QTBUG-55325 Change-Id: I2f0ac9d8aed1415662196070fb763f2752004d22 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 6 +++++- .../auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index a54d6daf9c..c72565b2f4 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2247,8 +2247,12 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo me->accept(); q->sendEvent(item, me); if (me->isAccepted()) { - if (!q->mouseGrabberItem()) + auto mouseGrabber = q->mouseGrabberItem(); + if (mouseGrabber && mouseGrabber != item) { + item->mouseUngrabEvent(); + } else { item->grabMouse(); + } point->setAccepted(true); } return me->isAccepted(); diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 7baff93574..b69c6eedf8 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -125,6 +125,7 @@ private slots: void containsPress_data(); void containsPress(); void ignoreBySource(); + void notPressedAfterStolenGrab(); private: int startDragDistance() const { @@ -1987,6 +1988,23 @@ void tst_QQuickMouseArea::ignoreBySource() QCOMPARE(flickable->contentY(), 0.); } +void tst_QQuickMouseArea::notPressedAfterStolenGrab() +{ + QQuickWindow window; + window.resize(200, 200); + window.show(); + QTest::qWaitForWindowExposed(&window); + + QQuickMouseArea *ma = new QQuickMouseArea(window.contentItem()); + ma->setSize(window.size()); + QObject::connect(ma, + static_cast(&QQuickMouseArea::pressed), + [&]() { window.contentItem()->grabMouse(); }); + + QTest::mouseClick(&window, Qt::LeftButton); + QVERIFY(!ma->pressed()); +} + QTEST_MAIN(tst_QQuickMouseArea) #include "tst_qquickmousearea.moc" -- cgit v1.2.3 From 4cdaf0064a4481cda08f4c0762cfc90bb850f6d1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 19 Aug 2016 13:42:18 +0200 Subject: V4: Replace 3 QHashes with std::vectors All keyed on basic block index. Change-Id: I98d46a36896d4b1ab45882ea9f38d0539ffb319e Reviewed-by: Simon Hausmann --- src/qml/jit/qv4assembler.cpp | 13 ++++++++----- src/qml/jit/qv4assembler_p.h | 10 ++++------ src/qml/jit/qv4isel_masm.cpp | 18 ++++++------------ 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 9c839936c2..5e05f14192 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -152,11 +152,14 @@ Assembler::Assembler(InstructionSelection *isel, IR::Function* function, QV4::Ex , _executableAllocator(executableAllocator) , _isel(isel) { + _addrs.resize(_function->basicBlockCount()); + _patches.resize(_function->basicBlockCount()); + _labelPatches.resize(_function->basicBlockCount()); } void Assembler::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock) { - _addrs[block] = label(); + _addrs[block->index()] = label(); catchBlock = block->catchBlock; _nextBlock = nextBlock; } @@ -166,12 +169,12 @@ void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) Q_UNUSED(current); if (target != _nextBlock) - _patches[target].append(jump()); + _patches[target->index()].push_back(jump()); } void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) { - _patches[targetBlock].append(targetJump); + _patches[targetBlock->index()].push_back(targetJump); } void Assembler::addPatch(DataLabelPtr patch, Label target) @@ -179,12 +182,12 @@ void Assembler::addPatch(DataLabelPtr patch, Label target) DataLabelPatch p; p.dataLabel = patch; p.target = target; - _dataLabelPatches.append(p); + _dataLabelPatches.push_back(p); } void Assembler::addPatch(DataLabelPtr patch, IR::BasicBlock *target) { - _labelPatches[target].append(patch); + _labelPatches[target->index()].push_back(patch); } void Assembler::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 2ef0db78c0..7d4d90882a 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -58,8 +58,6 @@ #include "private/qv4lookup_p.h" #include "qv4targetplatform_p.h" -#include -#include #include #include @@ -1073,8 +1071,8 @@ public: private: QScopedPointer _stackLayout; IR::Function *_function; - QHash _addrs; - QHash > _patches; + std::vector