diff options
-rw-r--r-- | src/quick/items/qquickitem.cpp | 177 | ||||
-rw-r--r-- | src/quick/items/qquickitem.h | 13 | ||||
-rw-r--r-- | src/quick/items/qquickitem_p.h | 17 | ||||
-rw-r--r-- | tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp | 2 | ||||
-rw-r--r-- | tests/auto/quick/qquickitem2/CMakeLists.txt | 2 | ||||
-rw-r--r-- | tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 33 | ||||
-rw-r--r-- | tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp | 12 |
8 files changed, 208 insertions, 52 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 836cc17758..6776685dc2 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3102,6 +3102,15 @@ Motifies \a t with this items local transform relative to its parent. */ void QQuickItemPrivate::itemToParentTransform(QTransform &t) const { + /* Read the current x and y values. As this is an internal method, + we don't care about it being usable in bindings. Instead, we + care about performance here, and thus we read the value with + valueBypassingBindings. This avoids any checks whether we are + in a binding (which sholdn't be too expensive, but can add up). + */ + + qreal x = this->x.valueBypassingBindings(); + qreal y = this->y.valueBypassingBindings(); if (x || y) t.translate(x, y); @@ -3784,14 +3793,16 @@ void QQuickItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeom } } + // The notify method takes care of emitting the signal, and also notifies any + // property observers. if (change.xChange()) - emit xChanged(); + d->x.notify(); if (change.yChange()) - emit yChanged(); + d->y.notify(); if (change.widthChange()) - emit widthChanged(); + d->width.notify(); if (change.heightChange()) - emit heightChanged(); + d->height.notify(); #if QT_CONFIG(accessibility) if (QAccessible::isActive()) { if (QObject *acc = QQuickAccessibleAttached::findAccessible(this)) { @@ -6767,6 +6778,16 @@ QPointF QQuickItem::position() const void QQuickItem::setX(qreal v) { Q_D(QQuickItem); + /* There are two ways in which this function might be called: + a) Either directly by the user, or + b) when a binding has evaluated to a new value and it writes + the value back + In the first case, we want to remove an existing binding, in + the second case, we don't want to remove the binding which + just wrote the value. + removeBindingUnlessInWrapper takes care of this. + */ + d->x.removeBindingUnlessInWrapper(); if (qt_is_nan(v)) return; if (d->x == v) @@ -6777,13 +6798,14 @@ void QQuickItem::setX(qreal v) d->dirty(QQuickItemPrivate::Position); - geometryChange(QRectF(d->x, d->y, d->width, d->height), + geometryChange(QRectF(v, d->y, d->width, d->height), QRectF(oldx, d->y, d->width, d->height)); } void QQuickItem::setY(qreal v) { Q_D(QQuickItem); + d->y.removeBindingUnlessInWrapper(); if (qt_is_nan(v)) return; if (d->y == v) @@ -6794,7 +6816,9 @@ void QQuickItem::setY(qreal v) d->dirty(QQuickItemPrivate::Position); - geometryChange(QRectF(d->x, d->y, d->width, d->height), + // we use v instead of d->y, as that avoid a method call + // and we have v anyway in scope + geometryChange(QRectF(d->x, v, d->width, d->height), QRectF(d->x, oldy, d->width, d->height)); } @@ -6810,8 +6834,21 @@ void QQuickItem::setPosition(const QPointF &pos) qreal oldx = d->x; qreal oldy = d->y; - d->x = pos.x(); - d->y = pos.y(); + /* This preserves the bindings, because that was what the code used to do + The effect of this is that you can have + Item { + Rectangle { + x: someValue; y: someValue + DragHandler {} + } + } + and you can move the rectangle around; once someValue changes, the position gets + reset again (even when a drag is currently ongoing). + Whether we want this is up to discussion. + */ + + d->x.setValueBypassingBindings(pos.x()); //TODO: investigate whether to break binding here or not + d->y.setValueBypassingBindings(pos.y()); d->dirty(QQuickItemPrivate::Position); @@ -6819,6 +6856,19 @@ void QQuickItem::setPosition(const QPointF &pos) QRectF(oldx, oldy, d->width, d->height)); } +/* The bindable methods return an object which supports inspection (hasBinding) and + modification (setBinding, removeBinding) of the properties bindable state. +*/ +QBindable<qreal> QQuickItem::bindableX() +{ + return QBindable<qreal>(&d_func()->x); +} + +QBindable<qreal> QQuickItem::bindableY() +{ + return QBindable<qreal>(&d_func()->y); +} + /*! \property QQuickItem::width @@ -6833,6 +6883,7 @@ qreal QQuickItem::width() const void QQuickItem::setWidth(qreal w) { Q_D(QQuickItem); + d->width.removeBindingUnlessInWrapper(); if (qt_is_nan(w)) return; @@ -6845,13 +6896,14 @@ void QQuickItem::setWidth(qreal w) d->dirty(QQuickItemPrivate::Size); - geometryChange(QRectF(d->x, d->y, d->width, d->height), + geometryChange(QRectF(d->x, d->y, w, d->height), QRectF(d->x, d->y, oldWidth, d->height)); } void QQuickItem::resetWidth() { Q_D(QQuickItem); + d->width.takeBinding(); d->widthValid = false; setImplicitWidth(implicitWidth()); } @@ -6883,6 +6935,11 @@ qreal QQuickItem::implicitWidth() const return d->getImplicitWidth(); } +QBindable<qreal> QQuickItem::bindableWidth() +{ + return QBindable<qreal>(&d_func()->width); +} + /*! \qmlproperty real QtQuick::Item::implicitWidth \qmlproperty real QtQuick::Item::implicitHeight @@ -6956,21 +7013,27 @@ void QQuickItem::setImplicitWidth(qreal w) Q_D(QQuickItem); bool changed = w != d->implicitWidth; d->implicitWidth = w; - if (d->width == w || widthValid()) { + // this uses valueBypassingBindings simply to avoid repeated "am I in a binding" checks + if (d->width.valueBypassingBindings() == w || widthValid()) { if (changed) d->implicitWidthChanged(); - if (d->width == w || widthValid()) + if (d->width.valueBypassingBindings() == w || widthValid()) return; changed = false; } - qreal oldWidth = d->width; + qreal oldWidth = d->width.valueBypassingBindings(); + Q_ASSERT(!d->width.hasBinding()); d->width = w; d->dirty(QQuickItemPrivate::Size); - geometryChange(QRectF(d->x, d->y, d->width, d->height), - QRectF(d->x, d->y, oldWidth, d->height)); + qreal x = d->x.valueBypassingBindings(); + qreal y = d->y.valueBypassingBindings(); + qreal width = w; + qreal height = d->height.valueBypassingBindings(); + geometryChange(QRectF(x, y, width, height), + QRectF(x, y, oldWidth, height)); if (changed) d->implicitWidthChanged(); @@ -6982,7 +7045,24 @@ void QQuickItem::setImplicitWidth(qreal w) bool QQuickItem::widthValid() const { Q_D(const QQuickItem); - return d->widthValid; + /* Logic: The width is valid if we assigned a value + or a binding to it. Note that a binding evaluation to + undefined (and thus calling resetWidth) is detached [1]; + hasBinding will thus return false for it, which is + what we want here, as resetting width should mean that + width is invalid (until the binding evaluates to a + non-undefined value again). + + [1]: A detached binding is a binding which is not set on a property. + In the case of QQmlPropertyBinding and resettable properties, it + still gets reevaluated when it was detached due to the binding + returning undefined, and it gets re-attached, once the binding changes + to a non-undefined value (unless another binding has beenset in the + meantime). + See QQmlPropertyBinding::isUndefined and handleUndefinedAssignment + */ + + return d->widthValid || d->width.hasBinding(); } /*! @@ -6999,6 +7079,10 @@ qreal QQuickItem::height() const void QQuickItem::setHeight(qreal h) { Q_D(QQuickItem); + // Note that we call removeUnlessInWrapper before returning in the + // NaN and equal value cases; that ensures that an explicit setHeight + // always removes the binding + d->height.removeBindingUnlessInWrapper(); if (qt_is_nan(h)) return; @@ -7011,13 +7095,17 @@ void QQuickItem::setHeight(qreal h) d->dirty(QQuickItemPrivate::Size); - geometryChange(QRectF(d->x, d->y, d->width, d->height), + geometryChange(QRectF(d->x, d->y, d->width, h), QRectF(d->x, d->y, d->width, oldHeight)); } void QQuickItem::resetHeight() { Q_D(QQuickItem); + // using takeBinding, we remove any existing binding from the + // property, but preserve the existing value (and avoid some overhead + // compared to calling setHeight(height()) + d->height.takeBinding(); d->heightValid = false; setImplicitHeight(implicitHeight()); } @@ -7047,26 +7135,36 @@ qreal QQuickItem::implicitHeight() const return d->getImplicitHeight(); } +QBindable<qreal> QQuickItem::bindableHeight() +{ + return QBindable<qreal>(&d_func()->height); +} + void QQuickItem::setImplicitHeight(qreal h) { Q_D(QQuickItem); bool changed = h != d->implicitHeight; d->implicitHeight = h; - if (d->height == h || heightValid()) { + if (d->height.valueBypassingBindings() == h || heightValid()) { if (changed) d->implicitHeightChanged(); - if (d->height == h || heightValid()) + if (d->height.valueBypassingBindings() == h || heightValid()) return; changed = false; } - qreal oldHeight = d->height; + qreal oldHeight = d->height.valueBypassingBindings(); + Q_ASSERT(!d->height.hasBinding()); d->height = h; d->dirty(QQuickItemPrivate::Size); - geometryChange(QRectF(d->x, d->y, d->width, d->height), - QRectF(d->x, d->y, d->width, oldHeight)); + qreal x = d->x.valueBypassingBindings(); + qreal y = d->y.valueBypassingBindings(); + qreal width = d->width.valueBypassingBindings(); + qreal height = d->height.valueBypassingBindings(); + geometryChange(QRectF(x, y, width, height), + QRectF(x, y, width, oldHeight)); if (changed) d->implicitHeightChanged(); @@ -7086,32 +7184,40 @@ void QQuickItem::setImplicitSize(qreal w, qreal h) bool wDone = false; bool hDone = false; - if (d->width == w || widthValid()) { + qreal width = d->width.valueBypassingBindings(); + qreal height = d->height.valueBypassingBindings(); + if (width == w || widthValid()) { if (wChanged) d->implicitWidthChanged(); - wDone = d->width == w || widthValid(); + wDone = width == w || widthValid(); wChanged = false; } - if (d->height == h || heightValid()) { + if (height == h || heightValid()) { if (hChanged) d->implicitHeightChanged(); - hDone = d->height == h || heightValid(); + hDone = height == h || heightValid(); hChanged = false; } if (wDone && hDone) return; - qreal oldWidth = d->width; - qreal oldHeight = d->height; - if (!wDone) + qreal oldWidth = width; + qreal oldHeight = height; + if (!wDone) { + width = w; d->width = w; - if (!hDone) + } + if (!hDone) { + height = h; d->height = h; + } d->dirty(QQuickItemPrivate::Size); - geometryChange(QRectF(d->x, d->y, d->width, d->height), - QRectF(d->x, d->y, oldWidth, oldHeight)); + qreal x = d->x.valueBypassingBindings(); + qreal y = d->y.valueBypassingBindings(); + geometryChange(QRectF(x, y, width, height), + QRectF(x, y, oldWidth, oldHeight)); if (!wDone && wChanged) d->implicitWidthChanged(); @@ -7125,7 +7231,7 @@ void QQuickItem::setImplicitSize(qreal w, qreal h) bool QQuickItem::heightValid() const { Q_D(const QQuickItem); - return d->heightValid; + return d->heightValid || d->height.hasBinding(); } /*! @@ -7147,6 +7253,9 @@ QSizeF QQuickItem::size() const \since 5.10 Sets the size of the item to \a size. + This methods preserves any existing binding on width and height; + thus any change that triggers the binding to execute again will + override the set values. \sa size, setWidth, setHeight */ @@ -7161,8 +7270,8 @@ void QQuickItem::setSize(const QSizeF &size) qreal oldHeight = d->height; qreal oldWidth = d->width; - d->height = size.height(); - d->width = size.width(); + d->height.setValueBypassingBindings(size.height()); + d->width.setValueBypassingBindings(size.width()); d->dirty(QQuickItemPrivate::Size); diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index fd600f79d0..6a3a5db921 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -46,6 +46,7 @@ #include <QtCore/QObject> #include <QtCore/QList> +#include <QtCore/qproperty.h> #include <QtGui/qevent.h> #include <QtGui/qfont.h> #include <QtGui/qaccessible.h> @@ -105,11 +106,11 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQmlListProperty<QObject> resources READ resources DESIGNABLE false) Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQmlListProperty<QQuickItem> children READ children NOTIFY childrenChanged DESIGNABLE false) - Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged FINAL) - Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged FINAL) + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged BINDABLE bindableX FINAL) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged BINDABLE bindableY FINAL) Q_PROPERTY(qreal z READ z WRITE setZ NOTIFY zChanged FINAL) - Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged RESET resetWidth FINAL) - Q_PROPERTY(qreal height READ height WRITE setHeight NOTIFY heightChanged RESET resetHeight FINAL) + Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged RESET resetWidth BINDABLE bindableWidth FINAL) + Q_PROPERTY(qreal height READ height WRITE setHeight NOTIFY heightChanged RESET resetHeight BINDABLE bindableHeight FINAL) Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) @@ -233,18 +234,22 @@ public: void setX(qreal); void setY(qreal); void setPosition(const QPointF &); + QBindable<qreal> bindableX(); + QBindable<qreal> bindableY(); qreal width() const; void setWidth(qreal); void resetWidth(); void setImplicitWidth(qreal); qreal implicitWidth() const; + QBindable<qreal> bindableWidth(); qreal height() const; void setHeight(qreal); void resetHeight(); void setImplicitHeight(qreal); qreal implicitHeight() const; + QBindable<qreal> bindableHeight(); QSizeF size() const; void setSize(const QSizeF &size); diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 99cd3a3269..de6bb4aedd 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -59,6 +59,7 @@ #include <QtQuick/private/qquickstate_p.h> #include <QtQuick/private/qquickpaletteproviderprivatebase_p.h> #include <QtQuick/private/qquickwindow_p.h> +#include <QtCore/private/qproperty_p.h> #if QT_CONFIG(quick_shadereffect) #include <QtQuick/private/qquickshadereffectsource_p.h> @@ -562,10 +563,18 @@ public: static bool canAcceptTabFocus(QQuickItem *item); - qreal x; - qreal y; - qreal width; - qreal height; + void setX(qreal x) {q_func()->setX(x);} + void xChanged() {q_func()->xChanged();} + Q_OBJECT_COMPAT_PROPERTY(QQuickItemPrivate, qreal, x, &QQuickItemPrivate::setX, &QQuickItemPrivate::xChanged); + void setY(qreal y) {q_func()->setY(y);} + void yChanged() {q_func()->yChanged();} + Q_OBJECT_COMPAT_PROPERTY(QQuickItemPrivate, qreal, y, &QQuickItemPrivate::setY, &QQuickItemPrivate::yChanged); + void setWidth(qreal width) {q_func()->setWidth(width);} + void widthChanged() {q_func()->widthChanged();} + Q_OBJECT_COMPAT_PROPERTY(QQuickItemPrivate, qreal, width, &QQuickItemPrivate::setWidth, &QQuickItemPrivate::widthChanged); + void setHeight(qreal height) {q_func()->setHeight(height);} + void heightChanged() {q_func()->heightChanged();} + Q_OBJECT_COMPAT_PROPERTY(QQuickItemPrivate, qreal, height, &QQuickItemPrivate::setHeight, &QQuickItemPrivate::heightChanged); qreal implicitWidth; qreal implicitHeight; diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp index bc1d86980f..c0c96ca6e5 100644 --- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp +++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp @@ -262,8 +262,8 @@ void tst_qqmlbinding::restoreBindingWithLoop() QCOMPARE(myItem->x(), qreal(88)); //original binding restored - QString warning = c.url().toString() + QLatin1String(":9:5: QML Rectangle: Binding loop detected for property \"x\""); - QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + QString warning = c.url().toString() + QLatin1String(R"(:\d+:\d+: QML Rectangle: Binding loop detected for property "x")"); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(warning)); rect->setProperty("activateBinding", false); QCOMPARE(myItem->x(), qreal(88 + 100)); //if loop handling changes this could be 90 + 100 diff --git a/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp b/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp index bb96ba319c..7b7a12ee1b 100644 --- a/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp +++ b/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp @@ -242,7 +242,7 @@ void tst_qqmlinfo::attachedObject() QVERIFY(warningSpy.isValid()); const QString qmlBindingLoopMessage = "QML Rectangle: Binding loop detected for property \"width\""; - const QString qmlBindingLoopMessageFull = component.url().toString() + ":7:5: " + qmlBindingLoopMessage; + const QString qmlBindingLoopMessageFull = component.url().toString() + ":8:9: " + qmlBindingLoopMessage; QTest::ignoreMessage(QtWarningMsg, qPrintable(qmlBindingLoopMessageFull)); const QString cppBindingLoopMessage = "QML AttachedObject (parent or ancestor of Attached): Binding loop detected for property \"a\""; diff --git a/tests/auto/quick/qquickitem2/CMakeLists.txt b/tests/auto/quick/qquickitem2/CMakeLists.txt index f60d1b2d55..6ecd164787 100644 --- a/tests/auto/quick/qquickitem2/CMakeLists.txt +++ b/tests/auto/quick/qquickitem2/CMakeLists.txt @@ -22,6 +22,8 @@ qt_internal_add_test(tst_qquickitem2 Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + LIBRARIES + Qt::TestPrivate TESTDATA ${test_data} ) diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 94f71af394..b5e2f5e141 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -42,6 +42,7 @@ #include "../../shared/util.h" #include "../shared/visualtestutil.h" #include "../../shared/platforminputcontext.h" +#include <QtTest/private/qpropertytesthelper_p.h> using namespace QQuickVisualTestUtil; @@ -117,6 +118,8 @@ private slots: void childrenProperty(); void resourcesProperty(); + void bindableProperties_data(); + void bindableProperties(); void changeListener(); void transformCrash(); @@ -2843,6 +2846,30 @@ void tst_QQuickItem::resourcesProperty() delete object; } +void tst_QQuickItem::bindableProperties_data() +{ + QTest::addColumn<qreal>("initialValue"); + QTest::addColumn<qreal>("newValue"); + QTest::addColumn<QString>("property"); + + // can't simply use 3. or 3.0 for the numbers as qreal might + // be float instead of double... + QTest::addRow("x") << qreal(3) << qreal(14) << "x"; + QTest::addRow("y") << qreal(10) << qreal(20) << "y"; + QTest::addRow("width") << qreal(100) << qreal(200) << "width"; + QTest::addRow("height") << qreal(50) << qreal(40) << "height"; +} + +void tst_QQuickItem::bindableProperties() +{ + QQuickItem item; + QFETCH(qreal, initialValue); + QFETCH(qreal, newValue); + QFETCH(QString, property); + + QTestPrivate::testReadWritePropertyBasics(item, initialValue, newValue, property.toUtf8().constData()); +} + void tst_QQuickItem::propertyChanges() { QQuickView *window = new QQuickView(nullptr); @@ -2967,7 +2994,7 @@ void tst_QQuickItem::childrenRectBug() { QQuickView *window = new QQuickView(nullptr); - QString warning = testFileUrl("childrenRectBug.qml").toString() + ":7:5: QML Item: Binding loop detected for property \"height\""; + QString warning = testFileUrl("childrenRectBug.qml").toString() + ":11:9: QML Item: Binding loop detected for property \"height\""; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); @@ -2988,11 +3015,11 @@ void tst_QQuickItem::childrenRectBug2() { QQuickView *window = new QQuickView(nullptr); - QString warning1 = testFileUrl("childrenRectBug2.qml").toString() + ":7:5: QML Item: Binding loop detected for property \"width\""; + QString warning1 = testFileUrl("childrenRectBug2.qml").toString() + ":10:9: QML Item: Binding loop detected for property \"width\""; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); - QString warning2 = testFileUrl("childrenRectBug2.qml").toString() + ":7:5: QML Item: Binding loop detected for property \"height\""; + QString warning2 = testFileUrl("childrenRectBug2.qml").toString() + ":11:9: QML Item: Binding loop detected for property \"height\""; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index b5ab520055..056028b3f0 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -28,6 +28,7 @@ #include <QtTest/QtTest> #include <QtTest/QSignalSpy> +#include <private/qquickitem_p.h> #include <private/qquickmultipointtoucharea_p.h> #include <private/qquickflickable_p.h> #include <private/qquickmousearea_p.h> @@ -1051,10 +1052,13 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() QPoint touch1(10,10); QPoint touch2(100,10); - touch1rect->setX(10); - touch1rect->setY(10); - touch2rect->setX(20); - touch2rect->setY(10); + // do not break the QML bindings + auto t1priv = QQuickItemPrivate::get(touch1rect); + auto t2priv = QQuickItemPrivate::get(touch2rect); + t1priv->x.setValueBypassingBindings(10); + t1priv->y.setValueBypassingBindings(10); + t2priv->x.setValueBypassingBindings(20); + t2priv->y.setValueBypassingBindings(10); // Start with mouse, move it, touch a point, move it, touch another. // Mouse is ignored, both touch points are heeded. |