diff options
Diffstat (limited to 'src/gui/graphicsview/qgraphicsitem.cpp')
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 440 |
1 files changed, 277 insertions, 163 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 7b885df..fc81f39 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -522,6 +522,11 @@ #include <private/qtextdocumentlayout_p.h> #include <private/qtextengine_p.h> +#ifdef Q_WS_X11 +#include <private/qt_x11_p.h> +#include <private/qpixmap_x11_p.h> +#endif + #include <math.h> QT_BEGIN_NAMESPACE @@ -562,29 +567,6 @@ Q_GLOBAL_STATIC(QGraphicsItemCustomDataStore, qt_dataStore) /*! \internal - Removes the first instance of \a child from \a children. This is a - heuristic approach that assumes that it's common to remove items from the - start or end of the list. -*/ -static void qt_graphicsitem_removeChild(QGraphicsItem *child, QList<QGraphicsItem *> *children) -{ - const int n = children->size(); - for (int i = 0; i < (n + 1) / 2; ++i) { - if (children->at(i) == child) { - children->removeAt(i); - return; - } - int j = n - i - 1; - if (children->at(j) == child) { - children->removeAt(j); - return; - } - } -} - -/*! - \internal - Returns a QPainterPath of \a path when stroked with the \a pen. Ignoring dash pattern. */ @@ -783,6 +765,131 @@ QVariant QGraphicsItemPrivate::inputMethodQueryHelper(Qt::InputMethodQuery query /*! \internal + If \a deleting is true, then this item is being deleted, and \a parent is + null. Make sure not to trigger any pure virtual function calls (e.g., + prepareGeometryChange). +*/ +void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool deleting) +{ + Q_Q(QGraphicsItem); + if (newParent == q) { + qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this); + return; + } + if (newParent == parent) + return; + + QVariant variant; + qVariantSetValue<QGraphicsItem *>(variant, newParent); + newParent = qVariantValue<QGraphicsItem *>(q->itemChange(QGraphicsItem::ItemParentChange, variant)); + if (newParent == parent) + return; + + if (QGraphicsWidget *w = isWidget ? static_cast<QGraphicsWidget *>(q) : q->parentWidget()) { + // Update the child focus chain; when reparenting a widget that has a + // focus child, ensure that that focus child clears its focus child + // chain from our parents before it's reparented. + if (QGraphicsWidget *focusChild = w->focusWidget()) + focusChild->clearFocus(); + } + + // We anticipate geometry changes. If the item is deleted, it will be + // removed from the index at a later stage, and the whole scene will be + // updated. + if (!deleting) + q_ptr->prepareGeometryChange(); + + if (parent) { + // Remove from current parent + parent->d_ptr->removeChild(q); + qVariantSetValue<QGraphicsItem *>(variant, q); + parent->itemChange(QGraphicsItem::ItemChildRemovedChange, variant); + } + + // Update toplevelitem list. If this item is being deleted, its parent + // will be 0 but we don't want to register/unregister it in the TLI list. + if (scene && !deleting) { + if (parent && !newParent) { + scene->d_func()->registerTopLevelItem(q); + } else if (!parent && newParent) { + scene->d_func()->unregisterTopLevelItem(q); + } + } + + if ((parent = newParent)) { + bool implicitUpdate = false; + if (parent->d_func()->scene && parent->d_func()->scene != scene) { + // Move this item to its new parent's scene + parent->d_func()->scene->addItem(q); + implicitUpdate = true; + } else if (!parent->d_func()->scene && scene) { + // Remove this item from its former scene + scene->removeItem(q); + } + + parent->d_ptr->addChild(q); + qVariantSetValue<QGraphicsItem *>(variant, q); + parent->itemChange(QGraphicsItem::ItemChildAddedChange, variant); + if (!implicitUpdate) + updateHelper(); + + // Inherit ancestor flags from the new parent. + updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); + updateAncestorFlag(QGraphicsItem::ItemClipsChildrenToShape); + updateAncestorFlag(QGraphicsItem::ItemIgnoresTransformations); + + // Update item visible / enabled. + if (parent->isVisible() != visible) { + if (!parent->isVisible() || !explicitlyHidden) + setVisibleHelper(parent->isVisible(), /* explicit = */ false, /* update = */ !implicitUpdate); + } + if (parent->isEnabled() != enabled) { + if (!parent->isEnabled() || !explicitlyDisabled) + setEnabledHelper(parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate); + } + + } else { + // Inherit ancestor flags from the new parent. + updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); + updateAncestorFlag(QGraphicsItem::ItemClipsChildrenToShape); + updateAncestorFlag(QGraphicsItem::ItemIgnoresTransformations); + + // Update item visible / enabled. + if (!visible && !explicitlyHidden) + setVisibleHelper(true, /* explicit = */ false); + if (!enabled && !explicitlyDisabled) + setEnabledHelper(true, /* explicit = */ false); + + // If the item is being deleted, the whole scene will be updated. + if (!deleting) + updateHelper(); + } + + if (scene) { + // Invalidate any sort caching; arrival of a new item means we need to + // resort. + scene->d_func()->invalidateSortCache(); + } + + // Resolve opacity. + if (parent) + resolveEffectiveOpacity(parent->effectiveOpacity()); + else + resolveEffectiveOpacity(1.0); + + // Resolve depth. + resolveDepth(parent ? parent->d_ptr->depth : -1); + + // Invalidate transform cache. + invalidateSceneTransformCache(); + + // Deliver post-change notification + q->itemChange(QGraphicsItem::ItemParentHasChanged, qVariantFromValue<QGraphicsItem *>(parent)); +} + +/*! + \internal + Empty all cached pixmaps from the pixmap cache. */ void QGraphicsItemCache::purge() @@ -855,24 +962,17 @@ QGraphicsItem::QGraphicsItem(QGraphicsItemPrivate &dd, QGraphicsItem *parent, */ QGraphicsItem::~QGraphicsItem() { + if (d_ptr->scene && !d_ptr->parent) + d_ptr->scene->d_func()->unregisterTopLevelItem(this); + clearFocus(); - d_ptr->removeExtraItemCache(); - QVariant variant; - foreach (QGraphicsItem *child, d_ptr->children) { - if (QGraphicsItem *parent = child->parentItem()) { - qVariantSetValue<QGraphicsItem *>(variant, child); - parent->itemChange(ItemChildRemovedChange, variant); - } - delete child; - } - d_ptr->children.clear(); + d_ptr->removeExtraItemCache(); + QList<QGraphicsItem *> oldChildren = d_ptr->children; + qDeleteAll(oldChildren); + Q_ASSERT(d_ptr->children.isEmpty()); - if (QGraphicsItem *parent = parentItem()) { - qVariantSetValue<QGraphicsItem *>(variant, this); - parent->itemChange(ItemChildRemovedChange, variant); - qt_graphicsitem_removeChild(this, &parent->d_func()->children); - } + d_ptr->setParentItemHelper(0, /* deleting = */ true); if (d_ptr->scene) d_ptr->scene->d_func()->_q_removeItemLater(this); @@ -1017,103 +1117,7 @@ QGraphicsWidget *QGraphicsItem::window() const */ void QGraphicsItem::setParentItem(QGraphicsItem *parent) { - if (parent == this) { - qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this); - return; - } - if (parent == d_ptr->parent) - return; - QVariant variant; - qVariantSetValue<QGraphicsItem *>(variant, parent); - parent = qVariantValue<QGraphicsItem *>(itemChange(ItemParentChange, variant)); - if (parent == d_ptr->parent) - return; - - if (QGraphicsWidget *w = d_ptr->isWidget ? static_cast<QGraphicsWidget *>(this) : parentWidget()) { - // Update the child focus chain; when reparenting a widget that has a - // focus child, ensure that that focus child clears its focus child - // chain from our parents before it's reparented. - if (QGraphicsWidget *focusChild = w->focusWidget()) - focusChild->clearFocus(); - } - - // We anticipate geometry changes - prepareGeometryChange(); - - if (d_ptr->parent) { - // Remove from current parent - qt_graphicsitem_removeChild(this, &d_ptr->parent->d_func()->children); - qVariantSetValue<QGraphicsItem *>(variant, this); - d_ptr->parent->itemChange(ItemChildRemovedChange, variant); - } - - if ((d_ptr->parent = parent)) { - bool implicitUpdate = false; - if (parent->d_func()->scene && parent->d_func()->scene != d_ptr->scene) { - // Move this item to its new parent's scene - parent->d_func()->scene->addItem(this); - implicitUpdate = true; - } else if (!parent->d_func()->scene && d_ptr->scene) { - // Remove this item from its former scene - d_ptr->scene->removeItem(this); - } - - d_ptr->parent->d_func()->children << this; - qVariantSetValue<QGraphicsItem *>(variant, this); - d_ptr->parent->itemChange(ItemChildAddedChange, variant); - if (!implicitUpdate) - d_ptr->updateHelper(); - - // Inherit ancestor flags from the new parent. - d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); - d_ptr->updateAncestorFlag(ItemClipsChildrenToShape); - d_ptr->updateAncestorFlag(ItemIgnoresTransformations); - - // Update item visible / enabled. - if (d_ptr->parent->isVisible() != d_ptr->visible) { - if (!d_ptr->parent->isVisible() || !d_ptr->explicitlyHidden) - d_ptr->setVisibleHelper(d_ptr->parent->isVisible(), /* explicit = */ false, /* update = */ !implicitUpdate); - } - if (d_ptr->parent->isEnabled() != d_ptr->enabled) { - if (!d_ptr->parent->isEnabled() || !d_ptr->explicitlyDisabled) - d_ptr->setEnabledHelper(d_ptr->parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate); - } - - } else { - // Inherit ancestor flags from the new parent. - d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); - d_ptr->updateAncestorFlag(ItemClipsChildrenToShape); - d_ptr->updateAncestorFlag(ItemIgnoresTransformations); - - // Update item visible / enabled. - if (!d_ptr->visible && !d_ptr->explicitlyHidden) - d_ptr->setVisibleHelper(true, /* explicit = */ false); - if (!d_ptr->enabled && !d_ptr->explicitlyDisabled) - d_ptr->setEnabledHelper(true, /* explicit = */ false); - - d_ptr->updateHelper(); - } - - if (d_ptr->scene) { - // Invalidate any sort caching; arrival of a new item means we need to - // resort. - d_ptr->scene->d_func()->invalidateSortCache(); - } - - // Resolve opacity. - if (QGraphicsItem *p = d_ptr->parent) - d_ptr->resolveEffectiveOpacity(p->effectiveOpacity()); - else - d_ptr->resolveEffectiveOpacity(1.0); - - // Resolve depth. - d_ptr->resolveDepth(parent ? parent->d_ptr->depth : -1); - - // Invalidate transform cache. - d_ptr->invalidateSceneTransformCache(); - - // Deliver post-change notification - itemChange(QGraphicsItem::ItemParentHasChanged, qVariantFromValue<QGraphicsItem *>(parent)); + d_ptr->setParentItemHelper(parent, /* deleting = */ false); } /*! @@ -2930,11 +2934,11 @@ qreal QGraphicsItem::zValue() const /*! Sets the Z-value, or the elevation, of the item, to \a z. The elevation decides the stacking order of sibling (neighboring) items. An item of high - Z-value will be drawn on top of an item with a lower Z-value if they - share the same parent item. In addition, children of an item will always be drawn - on top of the parent, regardless of the child's Z-value. Sibling items - that share the same Z-value will be drawn in an undefined order, although - the order will stay the same for as long as the items live. + Z-value will be drawn on top of an item with a lower Z-value if they share + the same parent item. In addition, children of an item will always be + drawn on top of the parent, regardless of the child's Z-value. Sibling + items that share the same Z-value will be drawn in order of insertion; the + last inserted child is stacked above previous children. \img graphicsview-zorder.png @@ -2961,7 +2965,7 @@ void QGraphicsItem::setZValue(qreal z) qreal newZ = qreal(itemChange(ItemZValueChange, double(z)).toDouble()); if (newZ == d_ptr->z) return; - d_ptr->z = z; + d_ptr->z = newZ; d_ptr->fullUpdateHelper(); if (d_ptr->scene) { @@ -3589,15 +3593,27 @@ void QGraphicsItem::setBoundingRegionGranularity(qreal granularity) only case where the item's background should be marked as dirty even when the item isn't visible. */ -void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force) +void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force, bool updateCache) { // No scene, or if the scene is updating everything, means we have nothing // to do. The only exception is if the scene tracks the growing scene rect. - if (dirty) + QGraphicsItemCache *cache = maybeExtraItemCache(); + if (dirty && (!updateCache || !cache || cache->allExposed)) return; + if (!scene || (scene && scene->d_func()->updateAll && scene->d_func()->hasSceneRect)) return; - if (scene && (visible || force)) { + + if (updateCache && QGraphicsItem::CacheMode(cacheMode) != QGraphicsItem::NoCache) { + if (rect.isNull()) { + cache->allExposed = true; + cache->exposed.clear(); + } else { + cache->exposed.append(rect); + } + } + + if (!dirty && scene && (visible || force)) { if (rect.isNull()) dirty = 1; scene->itemUpdated(q_ptr, rect); @@ -3706,6 +3722,41 @@ void QGraphicsItemPrivate::invalidateSceneTransformCache() children.at(i)->d_ptr->invalidateSceneTransformCache(); } +/*! + \internal +*/ +void QGraphicsItemPrivate::addChild(QGraphicsItem *child) +{ + child->d_ptr->siblingIndex = children.size(); + children.append(child); +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::removeChild(QGraphicsItem *child) +{ + int idx = child->d_ptr->siblingIndex; + int size = children.size(); + for (int i = idx; i < size - 1; ++i) { + QGraphicsItem *p = children[i + 1]; + children[i] = p; + p->d_ptr->siblingIndex = i; + } + children.removeLast(); +} + +/*! + \internal +*/ +QGraphicsItemCache *QGraphicsItemPrivate::maybeExtraItemCache() const +{ + return (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); +} + +/*! + \internal +*/ QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const { QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); @@ -3717,6 +3768,9 @@ QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const return c; } +/*! + \internal +*/ void QGraphicsItemPrivate::removeExtraItemCache() { QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); @@ -3757,22 +3811,48 @@ bool QGraphicsItemPrivate::isProxyWidget() const */ void QGraphicsItem::update(const QRectF &rect) { - if (d_ptr->dirty) - return; - if (d_ptr->scene && isVisible()) { - if (CacheMode(d_ptr->cacheMode) != NoCache) { - QGraphicsItemCache *cache = d_ptr->extraItemCache(); - if (rect.isNull()) { - cache->allExposed = true; - cache->exposed.clear(); - } else { - cache->exposed.append(rect); - } - } - d_ptr->updateHelper(rect); - } + d_ptr->updateHelper(rect, /* force = */ false, /* updateCache = */ true); } +/*! + \internal + + Scrolls \a rect in \a pix by \a dx, \a dy. + + ### This can be done much more efficiently by using XCopyArea on X11 with + the same dst and src, and through moving pixels in the raster engine. It + can probably also be done much better on the other paint engines. +*/ +void _q_scrollPixmap(QPixmap *pix, const QRect &rect, int dx, int dy) +{ +#if 0 + QPainter painter(pix); + painter.setClipRect(rect); + painter.drawPixmap(rect.translated(dx, dy), *pix, rect); + painter.end(); +#elif defined Q_WS_X11 + GC gc = XCreateGC(X11->display, pix->handle(), 0, 0); + + XRectangle xrect; + xrect.x = rect.x(); + xrect.y = rect.y(); + xrect.width = rect.width(); + xrect.height = rect.height(); + XSetClipRectangles(X11->display, gc, 0, 0, &xrect, 1, YXBanded); + + XCopyArea(X11->display, pix->handle(), pix->handle(), gc, + rect.x(), rect.y(), rect.width(), rect.height(), + rect.x()+dx, rect.y()+dy); + XFreeGC(X11->display, gc); +#else + QPixmap newPix = *pix; + QPainter painter(&newPix); + painter.setClipRect(rect); + painter.drawPixmap(rect.translated(dx, dy), *pix, rect); + painter.end(); + *pix = newPix; +#endif +} /*! \since 4.4 @@ -3801,11 +3881,45 @@ void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect) if (!d->scene) return; if (d->cacheMode != NoCache) { - // ### This is very slow, and can be done much better. If the cache is - // local and matches the below criteria for rotation and scaling, we - // can easily scroll. And if the cache is in device coordinates, we - // can scroll both the viewport and the cache. - update(rect); + QGraphicsItemCache *c; + bool scrollCache = qFuzzyCompare(dx - int(dx), qreal(0.0)) && qFuzzyCompare(dy - int(dy), qreal(0.0)) + && (c = (QGraphicsItemCache *)qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraCacheData))) + && (d->cacheMode == ItemCoordinateCache && !c->fixedSize.isValid()); + if (scrollCache) { + QPixmap pix; + if (QPixmapCache::find(c->key, pix)) { + // Adjust with 2 pixel margin. Notice the loss of precision + // when converting to QRect. + int adjust = 2; + QRectF br = boundingRect().adjusted(-adjust, -adjust, adjust, adjust); + QRect irect = rect.toRect().translated(-br.x(), -br.y()); + + _q_scrollPixmap(&pix, irect, dx, dy); + + QPixmapCache::insert(c->key, pix); + + // Translate the existing expose. + foreach (QRectF exposedRect, c->exposed) + c->exposed += exposedRect.translated(dx, dy) & rect; + + // Calculate exposure. + QRegion exposed; + QRect r = rect.toRect(); + exposed += r; + exposed -= r.translated(dx, dy); + foreach (QRect rect, exposed.rects()) + update(rect); + d_ptr->updateHelper(); + } else { + update(rect); + } + } else { + // ### This is very slow, and can be done much better. If the cache is + // local and matches the below criteria for rotation and scaling, we + // can easily scroll. And if the cache is in device coordinates, we + // can scroll both the viewport and the cache. + update(rect); + } return; } |