Anchoring system optimizations.
authorMichael Brasser <michael.brasser@nokia.com>
Fri, 25 Nov 2011 02:53:57 +0000 (12:53 +1000)
committerQt by Nokia <qt-info@nokia.com>
Wed, 30 Nov 2011 02:21:12 +0000 (03:21 +0100)
Do a better job of only updating anchors when
needed. For example, if an item is anchored to
its parent, it shouldn't react when the parent
moves, only when it resizes.

Change-Id: I57b480631fc6e89ab214b3fd337478d2e6534044
Reviewed-by: Martin Jones <martin.jones@nokia.com>

src/declarative/items/qquickanchors.cpp
src/declarative/items/qquickanchors_p_p.h
src/declarative/items/qquickitem.cpp
src/declarative/items/qquickitem_p.h

index 678347c..8ebdc37 100644 (file)
@@ -146,6 +146,7 @@ QQuickAnchors::QQuickAnchors(QQuickItem *item, QObject *parent)
 QQuickAnchors::~QQuickAnchors()
 {
     Q_D(QQuickAnchors);
+    d->inDestructor = true;
     d->remDepend(d->fill);
     d->remDepend(d->centerIn);
     d->remDepend(d->left.item);
@@ -249,22 +250,67 @@ void QQuickAnchorsPrivate::clearItem(QQuickItem *item)
     }
 }
 
+int QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem)
+{
+    QQuickItemPrivate::GeometryChangeTypes dependency = QQuickItemPrivate::NoChange;
+
+    if (!controlItem || inDestructor)
+        return dependency;
+
+    if (fill == controlItem) {
+        if ((controlItem == item->parentItem()))
+            dependency |= QQuickItemPrivate::SizeChange;
+        else    //sibling
+            dependency |= QQuickItemPrivate::GeometryChange;
+        return dependency;  //exit early
+    }
+
+    if (centerIn == controlItem) {
+        if ((controlItem == item->parentItem()))
+            dependency |= QQuickItemPrivate::SizeChange;
+        else    //sibling
+            dependency |= QQuickItemPrivate::GeometryChange;
+        return dependency;  //exit early
+    }
+
+    if ((usedAnchors & QQuickAnchors::LeftAnchor && left.item == controlItem) ||
+        (usedAnchors & QQuickAnchors::RightAnchor && right.item == controlItem) ||
+        (usedAnchors & QQuickAnchors::HCenterAnchor && hCenter.item == controlItem)) {
+        if ((controlItem == item->parentItem()))
+            dependency |= QQuickItemPrivate::WidthChange;
+        else    //sibling
+            dependency |= QFlags<QQuickItemPrivate::GeometryChangeType>(QQuickItemPrivate::XChange | QQuickItemPrivate::WidthChange);
+    }
+
+    if ((usedAnchors & QQuickAnchors::TopAnchor && top.item == controlItem) ||
+        (usedAnchors & QQuickAnchors::BottomAnchor && bottom.item == controlItem) ||
+        (usedAnchors & QQuickAnchors::VCenterAnchor && vCenter.item == controlItem) ||
+        (usedAnchors & QQuickAnchors::BaselineAnchor && baseline.item == controlItem)) {
+        if ((controlItem == item->parentItem()))
+            dependency |= QQuickItemPrivate::HeightChange;
+        else    //sibling
+            dependency |= QFlags<QQuickItemPrivate::GeometryChangeType>(QQuickItemPrivate::YChange | QQuickItemPrivate::HeightChange);
+    }
+
+    return dependency;
+}
+
 void QQuickAnchorsPrivate::addDepend(QQuickItem *item)
 {
-    if (!item)
+    if (!item || !componentComplete)
         return;
 
     QQuickItemPrivate *p = QQuickItemPrivate::get(item);
-    p->addItemChangeListener(this, QQuickItemPrivate::Geometry);
+    p->updateOrAddGeometryChangeListener(this, QFlags<QQuickItemPrivate::GeometryChangeType>(calculateDependency(item)));
 }
 
 void QQuickAnchorsPrivate::remDepend(QQuickItem *item)
 {
-    if (!item)
+    if (!item || !componentComplete)
         return;
 
     QQuickItemPrivate *p = QQuickItemPrivate::get(item);
-    p->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
+    p->updateOrRemoveGeometryChangeListener(this, QFlags<QQuickItemPrivate::GeometryChangeType>(calculateDependency(item)));
 }
 
 bool QQuickAnchors::mirrored()
@@ -339,27 +385,41 @@ void QQuickAnchorsPrivate::updateMe()
         return;
     }
 
-    fillChanged();
-    centerInChanged();
-    updateHorizontalAnchors();
-    updateVerticalAnchors();
+    update();
 }
 
 void QQuickAnchorsPrivate::updateOnComplete()
 {
+    //optimization to only set initial dependencies once, at completion time
+    QSet<QQuickItem *> dependencies;
+    dependencies << fill << centerIn
+                 << left.item << right.item << hCenter.item
+                 << top.item << bottom.item << vCenter.item << baseline.item;
+
+    foreach (QQuickItem *dependency, dependencies)
+        addDepend(dependency);
+
+    update();
+}
+
+
+void QQuickAnchorsPrivate::update()
+{
     fillChanged();
     centerInChanged();
-    updateHorizontalAnchors();
-    updateVerticalAnchors();
+    if (usedAnchors & QQuickAnchorLine::Horizontal_Mask)
+        updateHorizontalAnchors();
+    if (usedAnchors & QQuickAnchorLine::Vertical_Mask)
+        updateVerticalAnchors();
 }
 
 void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newG, const QRectF &oldG)
 {
     fillChanged();
     centerInChanged();
-    if (newG.x() != oldG.x() || newG.width() != oldG.width())
+    if ((usedAnchors & QQuickAnchorLine::Horizontal_Mask) && (newG.x() != oldG.x() || newG.width() != oldG.width()))
         updateHorizontalAnchors();
-    if (newG.y() != oldG.y() || newG.height() != oldG.height())
+    if ((usedAnchors & QQuickAnchorLine::Vertical_Mask) && (newG.y() != oldG.y() || newG.height() != oldG.height()))
         updateVerticalAnchors();
 }
 
@@ -376,8 +436,9 @@ void QQuickAnchors::setFill(QQuickItem *f)
         return;
 
     if (!f) {
-        d->remDepend(d->fill);
+        QQuickItem *oldFill = d->fill;
         d->fill = f;
+        d->remDepend(oldFill);
         emit fillChanged();
         return;
     }
@@ -385,8 +446,9 @@ void QQuickAnchors::setFill(QQuickItem *f)
         qmlInfo(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling.");
         return;
     }
-    d->remDepend(d->fill);
+    QQuickItem *oldFill = d->fill;
     d->fill = f;
+    d->remDepend(oldFill);
     d->addDepend(d->fill);
     emit fillChanged();
     d->fillChanged();
@@ -410,8 +472,9 @@ void QQuickAnchors::setCenterIn(QQuickItem* c)
         return;
 
     if (!c) {
-        d->remDepend(d->centerIn);
+        QQuickItem *oldCI = d->centerIn;
         d->centerIn = c;
+        d->remDepend(oldCI);
         emit centerInChanged();
         return;
     }
@@ -419,9 +482,9 @@ void QQuickAnchors::setCenterIn(QQuickItem* c)
         qmlInfo(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling.");
         return;
     }
-
-    d->remDepend(d->centerIn);
+    QQuickItem *oldCI = d->centerIn;
     d->centerIn = c;
+    d->remDepend(oldCI);
     d->addDepend(d->centerIn);
     emit centerInChanged();
     d->centerInChanged();
@@ -642,8 +705,9 @@ void QQuickAnchors::setTop(const QQuickAnchorLine &edge)
         return;
     }
 
-    d->remDepend(d->top.item);
+    QQuickItem *oldTop = d->top.item;
     d->top = edge;
+    d->remDepend(oldTop);
     d->addDepend(d->top.item);
     emit topChanged();
     d->updateVerticalAnchors();
@@ -678,8 +742,9 @@ void QQuickAnchors::setBottom(const QQuickAnchorLine &edge)
         return;
     }
 
-    d->remDepend(d->bottom.item);
+    QQuickItem *oldBottom = d->bottom.item;
     d->bottom = edge;
+    d->remDepend(oldBottom);
     d->addDepend(d->bottom.item);
     emit bottomChanged();
     d->updateVerticalAnchors();
@@ -714,8 +779,9 @@ void QQuickAnchors::setVerticalCenter(const QQuickAnchorLine &edge)
         return;
     }
 
-    d->remDepend(d->vCenter.item);
+    QQuickItem *oldVCenter = d->vCenter.item;
     d->vCenter = edge;
+    d->remDepend(oldVCenter);
     d->addDepend(d->vCenter.item);
     emit verticalCenterChanged();
     d->updateVerticalAnchors();
@@ -750,8 +816,9 @@ void QQuickAnchors::setBaseline(const QQuickAnchorLine &edge)
         return;
     }
 
-    d->remDepend(d->baseline.item);
+    QQuickItem *oldBaseline = d->baseline.item;
     d->baseline = edge;
+    d->remDepend(oldBaseline);
     d->addDepend(d->baseline.item);
     emit baselineChanged();
     d->updateVerticalAnchors();
@@ -786,8 +853,9 @@ void QQuickAnchors::setLeft(const QQuickAnchorLine &edge)
         return;
     }
 
-    d->remDepend(d->left.item);
+    QQuickItem *oldLeft = d->left.item;
     d->left = edge;
+    d->remDepend(oldLeft);
     d->addDepend(d->left.item);
     emit leftChanged();
     d->updateHorizontalAnchors();
@@ -822,8 +890,9 @@ void QQuickAnchors::setRight(const QQuickAnchorLine &edge)
         return;
     }
 
-    d->remDepend(d->right.item);
+    QQuickItem *oldRight = d->right.item;
     d->right = edge;
+    d->remDepend(oldRight);
     d->addDepend(d->right.item);
     emit rightChanged();
     d->updateHorizontalAnchors();
@@ -858,8 +927,9 @@ void QQuickAnchors::setHorizontalCenter(const QQuickAnchorLine &edge)
         return;
     }
 
-    d->remDepend(d->hCenter.item);
+    QQuickItem *oldHCenter = d->hCenter.item;
     d->hCenter = edge;
+    d->remDepend(oldHCenter);
     d->addDepend(d->hCenter.item);
     emit horizontalCenterChanged();
     d->updateHorizontalAnchors();
index eae7981..50d55b0 100644 (file)
@@ -92,7 +92,7 @@ class QQuickAnchorsPrivate : public QObjectPrivate, public QQuickItemChangeListe
     Q_DECLARE_PUBLIC(QQuickAnchors)
 public:
     QQuickAnchorsPrivate(QQuickItem *i)
-      : componentComplete(true), updatingMe(false), updatingHorizontalAnchor(0),
+      : componentComplete(true), updatingMe(false), inDestructor(false), updatingHorizontalAnchor(0),
         updatingVerticalAnchor(0), updatingFill(0), updatingCenterIn(0), item(i), usedAnchors(0), fill(0),
         centerIn(0), leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0),
         margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0)
@@ -101,12 +101,14 @@ public:
 
     void clearItem(QQuickItem *);
 
+    int calculateDependency(QQuickItem *);
     void addDepend(QQuickItem *);
     void remDepend(QQuickItem *);
     bool isItemComplete() const;
 
     bool componentComplete:1;
     bool updatingMe:1;
+    bool inDestructor:1;
     uint updatingHorizontalAnchor:2;
     uint updatingVerticalAnchor:2;
     uint updatingFill:2;
@@ -119,6 +121,7 @@ public:
     void setItemPos(const QPointF &);
     void setItemSize(const QSizeF &);
 
+    void update();
     void updateOnComplete();
     void updateMe();
 
index e020a00..a4a5ab4 100644 (file)
@@ -1759,7 +1759,7 @@ QQuickItem::~QQuickItem()
     for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
         QQuickAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate();
         if (anchor && anchor->item && anchor->item->parent() != this) //child will be deleted anyway
-            anchor->updateOnComplete();
+            anchor->update();
     }
 
     for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
@@ -2784,19 +2784,32 @@ 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());
+
     for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
         const QQuickItemPrivate::ChangeListener &change = d->changeListeners.at(ii);
-        if (change.types & QQuickItemPrivate::Geometry)
-            change.listener->itemGeometryChanged(this, newGeometry, oldGeometry);
+        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);
+            }
+        }
     }
 
-    if (newGeometry.x() != oldGeometry.x())
+    if (xChange)
         emit xChanged();
-    if (newGeometry.y() != oldGeometry.y())
+    if (yChange)
         emit yChanged();
-    if (newGeometry.width() != oldGeometry.width())
+    if (widthChange)
         emit widthChanged();
-    if (newGeometry.height() != oldGeometry.height())
+    if (heightChange)
         emit heightChanged();
 }
 
@@ -2834,6 +2847,28 @@ void QQuickItemPrivate::removeItemChangeListener(QQuickItemChangeListener *liste
     changeListeners.removeOne(change);
 }
 
+void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types)
+{
+    ChangeListener change(listener, types);
+    int index = changeListeners.find(change);
+    if (index > -1)
+        changeListeners[index].gTypes = change.gTypes;  //we may have different GeometryChangeTypes
+    else
+        changeListeners.append(change);
+}
+
+void QQuickItemPrivate::updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types)
+{
+    ChangeListener change(listener, types);
+    if (types == NoChange) {
+        changeListeners.removeOne(change);
+    } else {
+        int index = changeListeners.find(change);
+        if (index > -1)
+            changeListeners[index].gTypes = change.gTypes;  //we may have different GeometryChangeTypes
+    }
+}
+
 void QQuickItem::keyPressEvent(QKeyEvent *event)
 {
     event->ignore();
index 38e5d15..e9ddae5 100644 (file)
@@ -216,10 +216,24 @@ 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, QQuickItemPrivate::ChangeTypes t) : listener(l), types(t) {}
+        ChangeListener(QQuickItemChangeListener *l, QQuickItemPrivate::ChangeTypes t) : listener(l), types(t), gTypes(GeometryChange) {}
+        ChangeListener(QQuickItemChangeListener *l, QQuickItemPrivate::GeometryChangeTypes gt) : listener(l), types(Geometry), gTypes(gt) {}
         QQuickItemChangeListener *listener;
         QQuickItemPrivate::ChangeTypes types;
+        QQuickItemPrivate::GeometryChangeTypes gTypes;  //NOTE: not used for ==
         bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; }
     };
 
@@ -227,6 +241,8 @@ public:
         changeListeners.append(ChangeListener(listener, types));
     }
     void removeItemChangeListener(QQuickItemChangeListener *, ChangeTypes types);
+    void updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types);
+    void updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types);
     QPODVector<ChangeListener,4> changeListeners;
 
     QDeclarativeStateGroup *_states();