From a782a3e5d6c73aaa8de065452349a7b925c0faf9 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Fri, 25 Nov 2011 12:53:57 +1000 Subject: [PATCH] Anchoring system optimizations. 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 --- src/declarative/items/qquickanchors.cpp | 118 +++++++++++++++++++++++------ src/declarative/items/qquickanchors_p_p.h | 5 +- src/declarative/items/qquickitem.cpp | 49 ++++++++++-- src/declarative/items/qquickitem_p.h | 18 ++++- 4 files changed, 157 insertions(+), 33 deletions(-) diff --git a/src/declarative/items/qquickanchors.cpp b/src/declarative/items/qquickanchors.cpp index 678347c..8ebdc37 100644 --- a/src/declarative/items/qquickanchors.cpp +++ b/src/declarative/items/qquickanchors.cpp @@ -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::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::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(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(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 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(); diff --git a/src/declarative/items/qquickanchors_p_p.h b/src/declarative/items/qquickanchors_p_p.h index eae7981..50d55b0 100644 --- a/src/declarative/items/qquickanchors_p_p.h +++ b/src/declarative/items/qquickanchors_p_p.h @@ -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(); diff --git a/src/declarative/items/qquickitem.cpp b/src/declarative/items/qquickitem.cpp index e020a00..a4a5ab4 100644 --- a/src/declarative/items/qquickitem.cpp +++ b/src/declarative/items/qquickitem.cpp @@ -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(); diff --git a/src/declarative/items/qquickitem_p.h b/src/declarative/items/qquickitem_p.h index 38e5d15..e9ddae5 100644 --- a/src/declarative/items/qquickitem_p.h +++ b/src/declarative/items/qquickitem_p.h @@ -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 changeListeners; QDeclarativeStateGroup *_states(); -- 1.7.2.5