summaryrefslogtreecommitdiffstats
path: root/src/gui/graphicsview/qgraphicsitem.cpp
diff options
context:
space:
mode:
authorAndreas Aardal Hanssen <andreas.aardal.hanssen@nokia.com>2009-09-24 14:25:14 (GMT)
committerAndreas Aardal Hanssen <andreas.aardal.hanssen@nokia.com>2009-09-25 08:02:23 (GMT)
commit45bf804ce2feec74bd7787a64d72184da9458254 (patch)
treec6e454a6c31a626151d3205502dfe8d50a6103f0 /src/gui/graphicsview/qgraphicsitem.cpp
parented730bf7807bf77714337096d036b101256e7ac6 (diff)
downloadQt-45bf804ce2feec74bd7787a64d72184da9458254.zip
Qt-45bf804ce2feec74bd7787a64d72184da9458254.tar.gz
Qt-45bf804ce2feec74bd7787a64d72184da9458254.tar.bz2
Add QGraphicsItem::stackBefore(), and fix Z ordering bugs.
When we changed the sibling stacking order to be defined (4.5) instead of undefined (4.2, 4.3, 4.4), the need to control this stacking order arose. Before we could just say the order was random, but stable, and the only way people could rely on order was to set Z. Now, when the default order is defined as "insertion order", people start relying on this order, and incidentally they want more control. In QML, the need to have insertion order semantics is very evident as the order you define the elements in QML more strongly implies a graphical stacking order than the imperative order they get when added in C++. This change adds QGraphicsItem::stackBefore(const QGraphicsItem *), which works similarily to QWidget::stackUnder(). It moves the item in front of the sibling item passed as an argument. While implementing this function, and writing tests for how this function behaves in combination with Z values, I found that the code we had for updating siblingIndex was broken in the case where you remove an item from the middle of the children list. In this case newly added items would be assigned the same sibling index order as one that's already in the list. So in order to get the tests to pass I had to fix this bug as well.. The approach is to sort the children list by insertion order, so that we can fix up the sibling indexes. Performancewise this has little implications. If there are gaps in the sibling index list, which only occurs if you remove an item from the middle of the children list, will the sibling index list be adjusted / corrected before used (for example, by stackBehind()). Multiple calls to stackBehind will be fast, and the list is flagged for resorting (including Z order). Reviewed-by: jasplin
Diffstat (limited to 'src/gui/graphicsview/qgraphicsitem.cpp')
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp97
1 files changed, 95 insertions, 2 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
index 3249bb1..c3934c7 100644
--- a/src/gui/graphicsview/qgraphicsitem.cpp
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -1479,6 +1479,7 @@ QList<QGraphicsItem *> QGraphicsItem::children() const
*/
QList<QGraphicsItem *> QGraphicsItem::childItems() const
{
+ const_cast<QGraphicsItem *>(this)->d_ptr->ensureSortedChildren();
return d_ptr->children;
}
@@ -4048,6 +4049,82 @@ void QGraphicsItem::setZValue(qreal z)
}
/*!
+ \internal
+
+ Ensures that the list of children is sorted by insertion order, and that
+ the siblingIndexes are packed (no gaps), and start at 0.
+
+ ### This function is almost identical to
+ QGraphicsScenePrivate::ensureSequentialTopLevelSiblingIndexes().
+*/
+void QGraphicsItemPrivate::ensureSequentialSiblingIndex()
+{
+ if (!sequentialOrdering) {
+ qSort(children.begin(), children.end(), insertionOrder);
+ sequentialOrdering = 1;
+ needSortChildren = 1;
+ }
+ if (holesInSiblingIndex) {
+ holesInSiblingIndex = 0;
+ for (int i = 0; i < children.size(); ++i)
+ children[i]->d_ptr->siblingIndex = i;
+ }
+}
+
+/*!
+ \since 4.6
+
+ Stacks this item before \a sibling, which must be a sibling item (i.e., the
+ two items must share the same parent item, or must both be toplevel items).
+ The \a sibling must have the same Z value as this item, otherwise calling
+ this function will have no effect.
+
+ By default, all items are stacked by insertion order (i.e., the first item
+ you add is drawn before the next item you add). If two items' Z values are
+ different, then the item with the highest Z value is drawn on top. When the
+ Z values are the same, the insertion order will decide the stacking order.
+
+ \sa setZValue(), ItemStacksBehindParent
+*/
+void QGraphicsItem::stackBefore(const QGraphicsItem *sibling)
+{
+ if (sibling == this)
+ return;
+ if (!sibling || d_ptr->parent != sibling->parentItem()) {
+ qWarning("QGraphicsItem::stackUnder: cannot stack under %p, which must be a sibling", sibling);
+ return;
+ }
+ QList<QGraphicsItem *> *siblings = d_ptr->parent
+ ? &d_ptr->parent->d_ptr->children
+ : (d_ptr->scene ? &d_ptr->scene->d_func()->topLevelItems : 0);
+ if (!siblings) {
+ qWarning("QGraphicsItem::stackUnder: cannot stack under %p, which must be a sibling", sibling);
+ return;
+ }
+
+ // First, make sure that the sibling indexes have no holes. This also
+ // marks the children list for sorting.
+ if (d_ptr->parent)
+ d_ptr->parent->d_ptr->ensureSequentialSiblingIndex();
+ else
+ d_ptr->scene->d_func()->ensureSequentialTopLevelSiblingIndexes();
+
+ // Only move items with the same Z value, and that need moving.
+ int siblingIndex = sibling->d_ptr->siblingIndex;
+ int myIndex = d_ptr->siblingIndex;
+ if (myIndex >= siblingIndex && d_ptr->z == sibling->d_ptr->z) {
+ siblings->move(myIndex, siblingIndex);
+ // Fixup the insertion ordering.
+ for (int i = 0; i < siblings->size(); ++i) {
+ int &index = siblings->at(i)->d_ptr->siblingIndex;
+ if (i != siblingIndex && index >= siblingIndex && index <= myIndex)
+ ++index;
+ }
+ d_ptr->siblingIndex = siblingIndex;
+ }
+}
+
+/*!
Returns the bounding rect of this item's descendants (i.e., its
children, their children, etc.) in local coordinates. The
rectangle will contain all descendants after they have been mapped
@@ -4753,20 +4830,36 @@ void QGraphicsItemPrivate::resolveDepth()
/*!
\internal
+
+ ### This function is almost identical to
+ QGraphicsScenePrivate::registerTopLevelItem().
*/
void QGraphicsItemPrivate::addChild(QGraphicsItem *child)
{
- needSortChildren = 1;
+ // Remove all holes from the sibling index list. Now the max index
+ // number is equal to the size of the children list.
+ ensureSequentialSiblingIndex();
+ needSortChildren = 1; // ### maybe 0
child->d_ptr->siblingIndex = children.size();
children.append(child);
}
/*!
\internal
+
+ ### This function is almost identical to
+ QGraphicsScenePrivate::unregisterTopLevelItem().
*/
void QGraphicsItemPrivate::removeChild(QGraphicsItem *child)
{
- children.removeOne(child);
+ // When removing elements in the middle of the children list,
+ // there will be a "gap" in the list of sibling indexes (0,1,3,4).
+ if (!holesInSiblingIndex)
+ holesInSiblingIndex = child->d_ptr->siblingIndex != children.size() - 1;
+ if (sequentialOrdering && !holesInSiblingIndex)
+ children.removeAt(child->d_ptr->siblingIndex);
+ else
+ children.removeOne(child);
// NB! Do not use children.removeAt(child->d_ptr->siblingIndex) because
// the child is not guaranteed to be at the index after the list is sorted.
// (see ensureSortedChildren()).