summaryrefslogtreecommitdiffstats
path: root/src/gui/graphicsview
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/graphicsview')
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp597
-rw-r--r--src/gui/graphicsview/qgraphicsitem_p.h61
-rw-r--r--src/gui/graphicsview/qgraphicsproxywidget.cpp8
-rw-r--r--src/gui/graphicsview/qgraphicsscene.cpp432
-rw-r--r--src/gui/graphicsview/qgraphicsscene_p.h7
-rw-r--r--src/gui/graphicsview/qgraphicsview.cpp314
-rw-r--r--src/gui/graphicsview/qgraphicsview_p.h7
-rw-r--r--src/gui/graphicsview/qgraphicswidget.cpp63
-rw-r--r--src/gui/graphicsview/qgraphicswidget_p.cpp9
-rw-r--r--src/gui/graphicsview/qgraphicswidget_p.h2
10 files changed, 1013 insertions, 487 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
index fc81f39..3aa74f3 100644
--- a/src/gui/graphicsview/qgraphicsitem.cpp
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -533,25 +533,22 @@ QT_BEGIN_NAMESPACE
// QRectF::intersects() returns false always if either the source or target
// rectangle's width or height are 0. This works around that problem.
-static QRectF _q_adjustedRect(const QRectF &rect)
-{
- static const qreal p = (qreal)0.00001;
- QRectF r = rect;
- if (!r.width())
- r.adjust(-p, 0, p, 0);
- if (!r.height())
- r.adjust(0, -p, 0, p);
- return r;
+static inline void _q_adjustRect(QRectF *rect)
+{
+ Q_ASSERT(rect);
+ if (!rect->width())
+ rect->adjust(-0.00001, 0, 0.00001, 0);
+ if (!rect->height())
+ rect->adjust(0, -0.00001, 0, 0.00001);
}
-static QRect _q_adjustedRect(const QRect &rect)
+static inline void _q_adjustRect(QRect *rect)
{
- QRect r = rect;
- if (!r.width())
- r.adjust(0, 0, 1, 0);
- if (!r.height())
- r.adjust(0, 0, 0, 1);
- return r;
+ Q_ASSERT(rect);
+ if (!rect->width())
+ rect->adjust(0, 0, 1, 0);
+ if (!rect->height())
+ rect->adjust(0, 0, 0, 1);
}
/*
@@ -613,6 +610,7 @@ void QGraphicsItemPrivate::updateAncestorFlag(QGraphicsItem::GraphicsItemFlag ch
case QGraphicsItem::ItemClipsChildrenToShape:
flag = AncestorClipsChildren;
enabled = flags & QGraphicsItem::ItemClipsChildrenToShape;
+ invalidateCachedClipPathRecursively(/*childrenOnly=*/true);
break;
case QGraphicsItem::ItemIgnoresTransformations:
flag = AncestorIgnoresTransformations;
@@ -779,9 +777,9 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool de
if (newParent == parent)
return;
- QVariant variant;
- qVariantSetValue<QGraphicsItem *>(variant, newParent);
- newParent = qVariantValue<QGraphicsItem *>(q->itemChange(QGraphicsItem::ItemParentChange, variant));
+ const QVariant newParentVariant(q->itemChange(QGraphicsItem::ItemParentChange,
+ qVariantFromValue<QGraphicsItem *>(newParent)));
+ newParent = qVariantValue<QGraphicsItem *>(newParentVariant);
if (newParent == parent)
return;
@@ -799,11 +797,11 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool de
if (!deleting)
q_ptr->prepareGeometryChange();
+ const QVariant thisPointerVariant(qVariantFromValue<QGraphicsItem *>(q));
if (parent) {
// Remove from current parent
parent->d_ptr->removeChild(q);
- qVariantSetValue<QGraphicsItem *>(variant, q);
- parent->itemChange(QGraphicsItem::ItemChildRemovedChange, variant);
+ parent->itemChange(QGraphicsItem::ItemChildRemovedChange, thisPointerVariant);
}
// Update toplevelitem list. If this item is being deleted, its parent
@@ -828,10 +826,9 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool de
}
parent->d_ptr->addChild(q);
- qVariantSetValue<QGraphicsItem *>(variant, q);
- parent->itemChange(QGraphicsItem::ItemChildAddedChange, variant);
+ parent->itemChange(QGraphicsItem::ItemChildAddedChange, thisPointerVariant);
if (!implicitUpdate)
- updateHelper();
+ updateHelper(QRectF(), false, true);
// Inherit ancestor flags from the new parent.
updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1));
@@ -854,15 +851,16 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool de
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 (!deleting) {
+ // 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 the item is being deleted, the whole scene will be updated.
+ updateHelper(QRectF(), false, true);
+ }
}
if (scene) {
@@ -872,10 +870,7 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool de
}
// Resolve opacity.
- if (parent)
- resolveEffectiveOpacity(parent->effectiveOpacity());
- else
- resolveEffectiveOpacity(1.0);
+ updateEffectiveOpacity();
// Resolve depth.
resolveDepth(parent ? parent->d_ptr->depth : -1);
@@ -884,7 +879,7 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool de
invalidateSceneTransformCache();
// Deliver post-change notification
- q->itemChange(QGraphicsItem::ItemParentHasChanged, qVariantFromValue<QGraphicsItem *>(parent));
+ q->itemChange(QGraphicsItem::ItemParentHasChanged, newParentVariant);
}
/*!
@@ -1239,7 +1234,7 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags)
int geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations);
bool fullUpdate = (flags & geomChangeFlagsMask) != (d_ptr->flags & geomChangeFlagsMask);
if (fullUpdate)
- d_ptr->fullUpdateHelper();
+ d_ptr->fullUpdateHelper(false, true);
// Keep the old flags to compare the diff.
GraphicsItemFlags oldFlags = this->flags();
@@ -1249,12 +1244,8 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags)
// Reresolve effective opacity if the opacity flags change.
static const quint32 opacityFlagsMask = ItemIgnoresParentOpacity | ItemDoesntPropagateOpacityToChildren;
- if ((flags & opacityFlagsMask) != (oldFlags & opacityFlagsMask)) {
- if (QGraphicsItem *p = d_ptr->parent)
- d_ptr->resolveEffectiveOpacity(p->effectiveOpacity());
- else
- d_ptr->resolveEffectiveOpacity(1.0);
- }
+ if ((flags & opacityFlagsMask) != (oldFlags & opacityFlagsMask))
+ d_ptr->updateEffectiveOpacity();
if (!(d_ptr->flags & ItemIsFocusable) && hasFocus()) {
// Clear focus on the item if it has focus when the focusable flag
@@ -1274,6 +1265,9 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags)
d_ptr->updateAncestorFlag(ItemClipsChildrenToShape);
}
+ if ((flags & ItemClipsToShape) != (oldFlags & ItemClipsToShape))
+ d_ptr->invalidateCachedClipPath();
+
if ((flags & ItemIgnoresTransformations) != (oldFlags & ItemIgnoresTransformations)) {
// Item children clipping changes. Propagate the ancestor flag to
// all children.
@@ -1281,7 +1275,7 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags)
}
// ### Why updateHelper?
- d_ptr->updateHelper();
+ d_ptr->updateHelper(QRectF(), false, true);
// Notify change.
itemChange(ItemFlagsHaveChanged, quint32(flags));
@@ -1379,9 +1373,9 @@ QString QGraphicsItem::toolTip() const
*/
void QGraphicsItem::setToolTip(const QString &toolTip)
{
- QString newCursor = itemChange(ItemToolTipChange, toolTip).toString();
- d_ptr->setExtra(QGraphicsItemPrivate::ExtraToolTip, toolTip);
- itemChange(ItemToolTipHasChanged, toolTip);
+ const QVariant toolTipVariant(itemChange(ItemToolTipChange, toolTip));
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraToolTip, toolTipVariant.toString());
+ itemChange(ItemToolTipHasChanged, toolTipVariant);
}
#endif // QT_NO_TOOLTIP
@@ -1423,12 +1417,13 @@ QCursor QGraphicsItem::cursor() const
*/
void QGraphicsItem::setCursor(const QCursor &cursor)
{
- QCursor newCursor = qVariantValue<QCursor>(itemChange(ItemCursorChange,
- qVariantFromValue<QCursor>(cursor)));
- d_ptr->setExtra(QGraphicsItemPrivate::ExtraCursor, newCursor);
+ const QVariant cursorVariant(itemChange(ItemCursorChange, qVariantFromValue<QCursor>(cursor)));
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraCursor, qVariantValue<QCursor>(cursorVariant));
d_ptr->hasCursor = 1;
if (d_ptr->scene) {
+ d_ptr->scene->d_func()->allItemsUseDefaultCursor = false;
foreach (QGraphicsView *view, d_ptr->scene->views()) {
+ view->viewport()->setMouseTracking(true);
// Note: Some of this logic is duplicated in QGraphicsView's mouse events.
if (view->underMouse()) {
foreach (QGraphicsItem *itemUnderCursor, view->items(view->mapFromGlobal(QCursor::pos()))) {
@@ -1442,7 +1437,7 @@ void QGraphicsItem::setCursor(const QCursor &cursor)
}
}
}
- itemChange(ItemCursorHasChanged, qVariantFromValue<QCursor>(newCursor));
+ itemChange(ItemCursorHasChanged, cursorVariant);
}
/*!
@@ -1536,7 +1531,9 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo
return;
// Modify the property.
- newVisible = q_ptr->itemChange(QGraphicsItem::ItemVisibleChange, quint32(newVisible)).toBool();
+ const QVariant newVisibleVariant(q_ptr->itemChange(QGraphicsItem::ItemVisibleChange,
+ quint32(newVisible)));
+ newVisible = newVisibleVariant.toBool();
if (visible == quint32(newVisible))
return;
visible = newVisible;
@@ -1583,9 +1580,10 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo
}
// Update children with explicitly = false.
+ const bool updateChildren = update && !(flags & QGraphicsItem::ItemClipsChildrenToShape);
foreach (QGraphicsItem *child, children) {
if (!newVisible || !child->d_ptr->explicitlyHidden)
- child->d_ptr->setVisibleHelper(newVisible, false);
+ child->d_ptr->setVisibleHelper(newVisible, false, updateChildren);
}
// Enable subfocus
@@ -1597,7 +1595,7 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo
}
// Deliver post-change notification.
- q_ptr->itemChange(QGraphicsItem::ItemVisibleHasChanged, quint32(visible));
+ q_ptr->itemChange(QGraphicsItem::ItemVisibleHasChanged, newVisibleVariant);
}
/*!
@@ -1703,7 +1701,9 @@ void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bo
}
// Modify the property.
- enabled = q_ptr->itemChange(QGraphicsItem::ItemEnabledChange, quint32(newEnabled)).toBool();
+ const QVariant newEnabledVariant(q_ptr->itemChange(QGraphicsItem::ItemEnabledChange,
+ quint32(newEnabled)));
+ enabled = newEnabledVariant.toBool();
// Schedule redraw.
if (update)
@@ -1715,7 +1715,7 @@ void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bo
}
// Deliver post-change notification.
- q_ptr->itemChange(QGraphicsItem::ItemEnabledHasChanged, quint32(enabled));
+ q_ptr->itemChange(QGraphicsItem::ItemEnabledHasChanged, newEnabledVariant);
}
/*!
@@ -1802,7 +1802,8 @@ void QGraphicsItem::setSelected(bool selected)
selected = false;
if (d_ptr->selected == selected)
return;
- bool newSelected = itemChange(ItemSelectedChange, quint32(selected)).toBool();
+ const QVariant newSelectedVariant(itemChange(ItemSelectedChange, quint32(selected)));
+ bool newSelected = newSelectedVariant.toBool();
if (d_ptr->selected == newSelected)
return;
d_ptr->selected = newSelected;
@@ -1822,7 +1823,7 @@ void QGraphicsItem::setSelected(bool selected)
}
// Deliver post-change notification.
- itemChange(QGraphicsItem::ItemSelectedHasChanged, quint32(d_ptr->selected));
+ itemChange(QGraphicsItem::ItemSelectedHasChanged, newSelectedVariant);
}
/*!
@@ -1866,6 +1867,9 @@ qreal QGraphicsItem::opacity() const
*/
qreal QGraphicsItem::effectiveOpacity() const
{
+ if (!d_ptr->hasEffectiveOpacity)
+ return qreal(1.0);
+
QVariant effectiveOpacity = d_ptr->extra(QGraphicsItemPrivate::ExtraEffectiveOpacity);
return effectiveOpacity.isNull() ? qreal(1.0) : qreal(effectiveOpacity.toDouble());
}
@@ -1895,17 +1899,18 @@ qreal QGraphicsItem::effectiveOpacity() const
void QGraphicsItem::setOpacity(qreal opacity)
{
// Notify change.
- qreal newOpacity = itemChange(ItemOpacityChange, double(opacity)).toDouble();
+ const QVariant newOpacityVariant(itemChange(ItemOpacityChange, double(opacity)));
+ qreal newOpacity = newOpacityVariant.toDouble();
// Normalize.
newOpacity = qBound<qreal>(0.0, newOpacity, 1.0);
// No change? Done.
- if (qFuzzyCompare(newOpacity, this->opacity()))
+ if (qFuzzyIsNull(newOpacity - this->opacity()))
return;
// Assign local opacity.
- if (qFuzzyCompare(newOpacity, qreal(1.0))) {
+ if (qFuzzyIsNull(newOpacity - 1)) {
// Opaque, unset opacity.
d_ptr->hasOpacity = 0;
d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraOpacity);
@@ -2050,7 +2055,13 @@ bool QGraphicsItem::acceptsHoverEvents() const
*/
void QGraphicsItem::setAcceptHoverEvents(bool enabled)
{
+ if (d_ptr->acceptsHover == quint32(enabled))
+ return;
d_ptr->acceptsHover = quint32(enabled);
+ if (d_ptr->acceptsHover && d_ptr->scene && d_ptr->scene->d_func()->allItemsIgnoreHoverEvents) {
+ d_ptr->scene->d_func()->allItemsIgnoreHoverEvents = false;
+ d_ptr->scene->d_func()->enableMouseTrackingOnViews();
+ }
}
/*!
@@ -2060,7 +2071,7 @@ void QGraphicsItem::setAcceptHoverEvents(bool enabled)
*/
void QGraphicsItem::setAcceptsHoverEvents(bool enabled)
{
- d_ptr->acceptsHover = quint32(enabled);
+ setAcceptHoverEvents(enabled);
}
/*!
@@ -2353,19 +2364,22 @@ QPointF QGraphicsItem::scenePos() const
the item is also updated; otherwise it is not updated before and after the
change.
*/
-void QGraphicsItemPrivate::setPosHelper(const QPointF &pos, bool update)
+void QGraphicsItemPrivate::setPosHelper(const QPointF &pos)
{
Q_Q(QGraphicsItem);
if (this->pos == pos)
return;
// Notify the item that the position is changing.
- QPointF newPos = q->itemChange(QGraphicsItem::ItemPositionChange, pos).toPointF();
+ const QVariant newPosVariant(q->itemChange(QGraphicsItem::ItemPositionChange, pos));
+ QPointF newPos = newPosVariant.toPointF();
if (newPos == this->pos)
return;
// Update and repositition.
- if (scene && update) {
+ inSetPosHelper = 1;
+ updateCachedClipPathFromSetPosHelper(newPos);
+ if (scene) {
fullUpdateHelper(true);
q->prepareGeometryChange();
}
@@ -2373,7 +2387,8 @@ void QGraphicsItemPrivate::setPosHelper(const QPointF &pos, bool update)
invalidateSceneTransformCache();
// Send post-notification.
- q->itemChange(QGraphicsItem::ItemPositionHasChanged, newPos);
+ q->itemChange(QGraphicsItem::ItemPositionHasChanged, newPosVariant);
+ inSetPosHelper = 0;
}
/*!
@@ -2388,7 +2403,7 @@ void QGraphicsItemPrivate::setPosHelper(const QPointF &pos, bool update)
*/
void QGraphicsItem::setPos(const QPointF &pos)
{
- d_ptr->setPosHelper(pos, /* update = */ true);
+ d_ptr->setPosHelper(pos);
}
/*!
@@ -2515,10 +2530,10 @@ QTransform QGraphicsItem::sceneTransform() const
QTransform m;
if (d_ptr->hasTransform) {
m = transform();
- m *= QTransform::fromTranslate(d_ptr->pos.x(), d_ptr->pos.y());
- } else {
- // ### ? QTransform::fromTranslate(d_ptr->pos.x(), d_ptr->pos.y())
- m.translate(d_ptr->pos.x(), d_ptr->pos.y());
+ if (!d_ptr->pos.isNull())
+ m *= QTransform::fromTranslate(d_ptr->pos.x(), d_ptr->pos.y());
+ } else if (!d_ptr->pos.isNull()) {
+ m = QTransform::fromTranslate(d_ptr->pos.x(), d_ptr->pos.y());
}
// Combine with parent and add to cache.
@@ -2643,6 +2658,8 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co
if (ok)
*ok = true;
const QPointF &itemPos = d_ptr->pos;
+ if (itemPos.isNull())
+ return d_ptr->hasTransform ? transform() : QTransform();
if (d_ptr->hasTransform)
return transform() * QTransform::fromTranslate(itemPos.x(), itemPos.y());
return QTransform::fromTranslate(itemPos.x(), itemPos.y());
@@ -2653,7 +2670,8 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co
const QPointF &otherPos = other->d_ptr->pos;
if (other->d_ptr->hasTransform) {
QTransform otherToParent = other->transform();
- otherToParent *= QTransform::fromTranslate(otherPos.x(), otherPos.y());
+ if (!otherPos.isNull())
+ otherToParent *= QTransform::fromTranslate(otherPos.x(), otherPos.y());
return otherToParent.inverted(ok);
} else {
if (ok)
@@ -2678,11 +2696,11 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co
QTransform itemToParent = QTransform::fromTranslate(itemPos.x(), itemPos.y());
if (hasTr)
- itemToParent = transform() * itemToParent;
+ itemToParent = itemPos.isNull() ? transform() : transform() * itemToParent;
QTransform otherToParent = QTransform::fromTranslate(otherPos.x(), otherPos.y());
if (otherHasTr)
- otherToParent = other->transform() * otherToParent;
+ otherToParent = otherPos.isNull() ? other->transform() : other->transform() * otherToParent;
return itemToParent * otherToParent.inverted(ok);
}
@@ -2722,7 +2740,8 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co
const QGraphicsItemPrivate *pd = p->d_ptr;
if (pd->hasTransform)
x *= p->transform();
- x *= QTransform::fromTranslate(pd->pos.x(), pd->pos.y());
+ if (!pd->pos.isNull())
+ x *= QTransform::fromTranslate(pd->pos.x(), pd->pos.y());
} while ((p = p->d_ptr->parent) && p != root);
if (parentOfOther)
return x.inverted(ok);
@@ -2754,21 +2773,23 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine)
return;
// Notify the item that the matrix is changing.
- QVariant variant;
- qVariantSetValue<QMatrix>(variant, newTransform.toAffine());
- newTransform = QTransform(qVariantValue<QMatrix>(itemChange(ItemMatrixChange, variant)));
+ QVariant newTransformVariant(itemChange(ItemMatrixChange,
+ qVariantFromValue<QMatrix>(newTransform.toAffine())));
+ newTransform = QTransform(qVariantValue<QMatrix>(newTransformVariant));
if (oldTransform == newTransform)
return;
// Update and set the new transformation.
- d_ptr->fullUpdateHelper(true);
+ d_ptr->fullUpdateHelper(true, true);
prepareGeometryChange();
d_ptr->hasTransform = !newTransform.isIdentity();
d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform);
d_ptr->invalidateSceneTransformCache();
// Send post-notification.
- itemChange(ItemTransformHasChanged, newTransform);
+ // NB! We have to change the value from QMatrix to QTransform.
+ qVariantSetValue<QTransform>(newTransformVariant, newTransform);
+ itemChange(ItemTransformHasChanged, newTransformVariant);
}
/*!
@@ -2800,21 +2821,21 @@ void QGraphicsItem::setTransform(const QTransform &matrix, bool combine)
return;
// Notify the item that the transformation matrix is changing.
- QVariant variant;
- qVariantSetValue<QTransform>(variant, newTransform);
- newTransform = qVariantValue<QTransform>(itemChange(ItemTransformChange, variant));
+ const QVariant newTransformVariant(itemChange(ItemTransformChange,
+ qVariantFromValue<QTransform>(newTransform)));
+ newTransform = qVariantValue<QTransform>(newTransformVariant);
if (oldTransform == newTransform)
return;
// Update and set the new transformation.
- d_ptr->fullUpdateHelper(true);
+ d_ptr->fullUpdateHelper(true, true);
prepareGeometryChange();
d_ptr->hasTransform = !newTransform.isIdentity();
d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform);
d_ptr->invalidateSceneTransformCache();
// Send post-notification.
- itemChange(ItemTransformHasChanged, newTransform);
+ itemChange(ItemTransformHasChanged, newTransformVariant);
}
/*!
@@ -2962,7 +2983,8 @@ qreal QGraphicsItem::zValue() const
*/
void QGraphicsItem::setZValue(qreal z)
{
- qreal newZ = qreal(itemChange(ItemZValueChange, double(z)).toDouble());
+ const QVariant newZVariant(itemChange(ItemZValueChange, double(z)));
+ qreal newZ = qreal(newZVariant.toDouble());
if (newZ == d_ptr->z)
return;
d_ptr->z = newZ;
@@ -2974,7 +2996,7 @@ void QGraphicsItem::setZValue(qreal z)
d_ptr->scene->d_func()->invalidateSortCache();
}
- itemChange(ItemZValueHasChanged, double(newZ));
+ itemChange(ItemZValueHasChanged, newZVariant);
}
/*!
@@ -2999,7 +3021,9 @@ QRectF QGraphicsItem::childrenBoundingRect() const
QRectF childRect;
foreach (QGraphicsItem *child, children()) {
QPointF childPos = child->pos();
- QTransform matrix = child->transform() * QTransform::fromTranslate(childPos.x(), childPos.y());
+ QTransform matrix = child->transform();
+ if (!childPos.isNull())
+ matrix *= QTransform::fromTranslate(childPos.x(), childPos.y());
childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect());
}
return childRect;
@@ -3131,57 +3155,79 @@ bool QGraphicsItem::isClipped() const
QPainterPath QGraphicsItem::clipPath() const
{
Q_D(const QGraphicsItem);
- QPainterPath clip;
- if (!isClipped())
- return clip;
+ if (!d->dirtyClipPath)
+ return d->emptyClipPath ? QPainterPath() : d->cachedClipPath;
+
+ if (!isClipped()) {
+ d_ptr->setCachedClipPath(QPainterPath());
+ return d->cachedClipPath;
+ }
+ const QRectF thisBoundingRect(boundingRect());
+ if (thisBoundingRect.isEmpty()) {
+ if (d_ptr->flags & ItemClipsChildrenToShape)
+ d_ptr->setEmptyCachedClipPathRecursively();
+ else
+ d_ptr->setEmptyCachedClipPath();
+ return QPainterPath();
+ }
+
+ QPainterPath clip;
// Start with the item's bounding rect.
- clip.addRect(boundingRect());
+ clip.addRect(thisBoundingRect);
- bool clipAway = false;
if (d->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) {
- // Make list of parents up to the farthest ancestor that clips its
- // children to its shape.
- QVarLengthArray<const QGraphicsItem *, 32> clippingAncestors;
- const QGraphicsItem *parent = parentItem();
- const QGraphicsItem *clipOwner = 0;
- do {
+ const QGraphicsItem *parent = this;
+ const QGraphicsItem *lastParent = this;
+
+ // Intersect any in-between clips starting at the top and moving downwards.
+ bool foundValidClipPath = false;
+ while ((parent = parent->d_ptr->parent)) {
if (parent->d_ptr->flags & ItemClipsChildrenToShape) {
- clippingAncestors.append(parent);
- clipOwner = parent;
- }
- } while ((parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) && (parent = parent->parentItem()));
+ // Map clip to the current parent and intersect with its shape/clipPath
+ clip = lastParent->itemTransform(parent).map(clip);
+ if ((foundValidClipPath = !parent->d_ptr->dirtyClipPath && parent->isClipped())) {
+ if (parent->d_ptr->emptyClipPath) {
+ if (d_ptr->flags & ItemClipsChildrenToShape)
+ d_ptr->setEmptyCachedClipPathRecursively();
+ else
+ d_ptr->setEmptyCachedClipPath();
+ return QPainterPath();
+ }
+ clip = clip.intersected(parent->d_ptr->cachedClipPath);
+ if (!(parent->d_ptr->flags & ItemClipsToShape))
+ clip = clip.intersected(parent->shape());
+ } else {
+ clip = clip.intersected(parent->shape());
+ }
- // Start with the topmost clip.
- QPainterPath parentClip = clipOwner->shape();
+ if (clip.isEmpty()) {
+ if (d_ptr->flags & ItemClipsChildrenToShape)
+ d_ptr->setEmptyCachedClipPathRecursively();
+ else
+ d_ptr->setEmptyCachedClipPath();
+ return clip;
+ }
+ lastParent = parent;
+ }
- // Intersect any in-between clips starting at the bottom and moving
- // upwards.
- for (int i = clippingAncestors.size() - 2; i >= 0; --i) {
- const QGraphicsItem *item = clippingAncestors[i];
- // ### what if itemtransform fails
- if (clipOwner)
- parentClip = clipOwner->itemTransform(item).map(parentClip);
- parentClip = parentClip.intersected(item->shape());
- if (parentClip.isEmpty()) {
- clip = parentClip;
- clipAway = true;
+ if (!(parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
+ || foundValidClipPath) {
break;
}
- clipOwner = item;
}
- if (!clipAway) {
+ if (lastParent != this) {
+ // Map clip back to the item's transform.
// ### what if itemtransform fails
- clip = clip.intersected(clipOwner->itemTransform(this).map(parentClip));
- if (clip.isEmpty())
- clipAway = true;
+ clip = lastParent->itemTransform(this).map(clip);
}
}
- if (!clipAway && d->flags & ItemClipsToShape)
+ if (d->flags & ItemClipsToShape)
clip = clip.intersected(shape());
+ d_ptr->setCachedClipPath(clip);
return clip;
}
@@ -3270,8 +3316,10 @@ bool QGraphicsItem::collidesWithPath(const QPainterPath &path, Qt::ItemSelection
return false;
}
- QRectF rectA = _q_adjustedRect(boundingRect());
- QRectF rectB = _q_adjustedRect(path.controlPointRect());
+ QRectF rectA(boundingRect());
+ _q_adjustRect(&rectA);
+ QRectF rectB(path.controlPointRect());
+ _q_adjustRect(&rectB);
if (!rectA.intersects(rectB)) {
// This we can determine efficiently. If the two rects neither
// intersect nor contain eachother, then the two items do not collide.
@@ -3280,12 +3328,11 @@ bool QGraphicsItem::collidesWithPath(const QPainterPath &path, Qt::ItemSelection
// For further testing, we need this item's shape or bounding rect.
QPainterPath thisShape;
- if (mode == Qt::IntersectsItemShape || mode == Qt::ContainsItemShape) {
+ if (mode == Qt::IntersectsItemShape || mode == Qt::ContainsItemShape)
thisShape = (isClipped() && !d_ptr->localCollisionHack) ? clipPath() : shape();
- } else {
- thisShape.addPolygon(_q_adjustedRect(boundingRect()));
- thisShape.closeSubpath();
- }
+ else
+ thisShape.addRect(rectA);
+
if (thisShape == QPainterPath()) {
// Empty shape? No collision.
return false;
@@ -3455,7 +3502,8 @@ QRegion QGraphicsItem::boundingRegion(const QTransform &itemToDeviceTransform) c
// into the bitmap, converts the result to a QRegion and scales the region
// back to device space with inverse granularity.
qreal granularity = boundingRegionGranularity();
- QRect deviceRect = _q_adjustedRect(itemToDeviceTransform.mapRect(boundingRect()).toRect());
+ QRect deviceRect = itemToDeviceTransform.mapRect(boundingRect()).toRect();
+ _q_adjustRect(&deviceRect);
if (granularity == 0.0)
return QRegion(deviceRect);
@@ -3585,6 +3633,24 @@ void QGraphicsItem::setBoundingRegionGranularity(qreal granularity)
/*!
\internal
+ Returns true if we can discard an update request; otherwise false.
+*/
+bool QGraphicsItemPrivate::discardUpdateRequest(bool ignoreClipping,
+ bool ignoreVisibleBit,
+ bool ignoreDirtyBit) const
+{
+ // 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.
+ return (!visible && !ignoreVisibleBit)
+ || (dirty && !ignoreDirtyBit)
+ || !scene
+ || (scene->d_func()->updateAll && scene->d_func()->hasSceneRect)
+ || (!ignoreClipping && (childrenClippedToShape() && isClippedAway()))
+ || (childrenCombineOpacity() && isFullyTransparent());
+}
+
+/*!
+ \internal
Asks the scene to mark this item's scene rect as dirty, requesting a
redraw. This does not invalidate any cache.
@@ -3593,31 +3659,16 @@ 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, bool updateCache)
+void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force, bool maybeDirtyClipPath)
{
// 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.
- QGraphicsItemCache *cache = maybeExtraItemCache();
- if (dirty && (!updateCache || !cache || cache->allExposed))
+ if (discardUpdateRequest(/*ignoreClipping=*/maybeDirtyClipPath, /*ignoreVisibleBit=*/force))
return;
- if (!scene || (scene && scene->d_func()->updateAll && scene->d_func()->hasSceneRect))
- return;
-
- 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);
- }
+ if (rect.isNull())
+ dirty = 1;
+ scene->itemUpdated(q_ptr, rect);
}
/*!
@@ -3625,21 +3676,25 @@ void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force, bool upd
Propagates updates to \a item and all its children.
*/
-void QGraphicsItemPrivate::fullUpdateHelper(bool childrenOnly)
+void QGraphicsItemPrivate::fullUpdateHelper(bool childrenOnly, bool maybeDirtyClipPath)
{
- // 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 (!scene || (scene && scene->d_func()->updateAll && scene->d_func()->hasSceneRect))
+ if (discardUpdateRequest(/*ignoreClipping=*/maybeDirtyClipPath,
+ /*ignoreVisibleBit=*/false,
+ /*ignoreDirtyBit=*/true)) {
return;
- if (!childrenOnly && !dirty)
- updateHelper();
- if (children.isEmpty() || dirtyChildren)
- return;
- if (flags & QGraphicsItem::ItemClipsChildrenToShape) {
- // ### mark all children dirty?
+ }
+
+ if (!childrenOnly && !dirty) {
+ // Effectively the same as updateHelper(QRectF(), false, maybeDirtyClipPath).
+ dirty = 1;
+ scene->itemUpdated(q_ptr, QRectF());
+ }
+
+ if (dirtyChildren || childrenClippedToShape()) {
// Unnecessary to update children as well.
return;
}
+
if (ancestorFlags & AncestorClipsChildren) {
Q_Q(QGraphicsItem);
// Check if we can avoid updating all children.
@@ -3662,10 +3717,36 @@ void QGraphicsItemPrivate::fullUpdateHelper(bool childrenOnly)
}
}
foreach (QGraphicsItem *child, children)
- child->d_ptr->fullUpdateHelper();
+ child->d_ptr->fullUpdateHelper(false, maybeDirtyClipPath);
dirtyChildren = 1;
}
+static inline bool allChildrenCombineOpacityHelper(QGraphicsItem *parent)
+{
+ Q_ASSERT(parent);
+ if (parent->flags() & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)
+ return false;
+
+ const QList<QGraphicsItem *> children(parent->childItems());
+ for (int i = 0; i < children.size(); ++i) {
+ if (children.at(i)->flags() & QGraphicsItem::ItemIgnoresParentOpacity)
+ return false;
+ }
+ return true;
+}
+
+void QGraphicsItemPrivate::updateEffectiveOpacity()
+{
+ Q_Q(QGraphicsItem);
+ if (parent) {
+ resolveEffectiveOpacity(parent->effectiveOpacity());
+ parent->d_ptr->allChildrenCombineOpacity = allChildrenCombineOpacityHelper(parent);
+ } else {
+ resolveEffectiveOpacity(1.0);
+ }
+ allChildrenCombineOpacity = allChildrenCombineOpacityHelper(q);
+}
+
/*!
\internal
@@ -3690,7 +3771,14 @@ void QGraphicsItemPrivate::resolveEffectiveOpacity(qreal parentEffectiveOpacity)
}
// Set this item's resolved opacity.
- setExtra(ExtraEffectiveOpacity, myEffectiveOpacity);
+ if (qFuzzyIsNull(myEffectiveOpacity - 1)) {
+ // Opaque, unset effective opacity.
+ hasEffectiveOpacity = 0;
+ unsetExtra(ExtraEffectiveOpacity);
+ } else {
+ hasEffectiveOpacity = 1;
+ setExtra(ExtraEffectiveOpacity, myEffectiveOpacity);
+ }
// Resolve children always.
for (int i = 0; i < children.size(); ++i)
@@ -3781,6 +3869,109 @@ void QGraphicsItemPrivate::removeExtraItemCache()
unsetExtra(ExtraCacheData);
}
+void QGraphicsItemPrivate::setEmptyCachedClipPathRecursively(const QRectF &emptyIfOutsideThisRect)
+{
+ setEmptyCachedClipPath();
+
+ const bool checkRect = !emptyIfOutsideThisRect.isNull()
+ && !(flags & QGraphicsItem::ItemClipsChildrenToShape);
+ for (int i = 0; i < children.size(); ++i) {
+ if (!checkRect) {
+ children.at(i)->d_ptr->setEmptyCachedClipPathRecursively();
+ continue;
+ }
+
+ QGraphicsItem *child = children.at(i);
+ const QRectF rect = child->mapRectFromParent(emptyIfOutsideThisRect);
+ if (rect.intersects(child->boundingRect()))
+ child->d_ptr->invalidateCachedClipPathRecursively(false, rect);
+ else
+ child->d_ptr->setEmptyCachedClipPathRecursively(rect);
+ }
+}
+
+void QGraphicsItemPrivate::invalidateCachedClipPathRecursively(bool childrenOnly, const QRectF &emptyIfOutsideThisRect)
+{
+ if (!childrenOnly)
+ invalidateCachedClipPath();
+
+ const bool checkRect = !emptyIfOutsideThisRect.isNull();
+ for (int i = 0; i < children.size(); ++i) {
+ if (!checkRect) {
+ children.at(i)->d_ptr->invalidateCachedClipPathRecursively(false);
+ continue;
+ }
+
+ QGraphicsItem *child = children.at(i);
+ const QRectF rect = child->mapRectFromParent(emptyIfOutsideThisRect);
+ if (rect.intersects(child->boundingRect()))
+ child->d_ptr->invalidateCachedClipPathRecursively(false, rect);
+ else
+ child->d_ptr->setEmptyCachedClipPathRecursively(rect);
+ }
+}
+
+void QGraphicsItemPrivate::updateCachedClipPathFromSetPosHelper(const QPointF &newPos)
+{
+ Q_ASSERT(inSetPosHelper);
+
+ if (!(ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren))
+ return; // Not clipped by any ancestor.
+
+ // Find closest clip ancestor and transform.
+ Q_Q(QGraphicsItem);
+ QTransform thisToParentTransform = hasTransform
+ ? q->transform() * QTransform::fromTranslate(newPos.x(), newPos.y())
+ : QTransform::fromTranslate(newPos.x(), newPos.y());
+ QGraphicsItem *clipParent = parent;
+ while (clipParent && !(clipParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape)) {
+ if (clipParent->d_ptr->hasTransform)
+ thisToParentTransform *= clipParent->transform();
+ if (!clipParent->d_ptr->pos.isNull()) {
+ thisToParentTransform *= QTransform::fromTranslate(clipParent->d_ptr->pos.x(),
+ clipParent->d_ptr->pos.y());
+ }
+ clipParent = clipParent->d_ptr->parent;
+ }
+
+ // thisToParentTransform is now the same as q->itemTransform(clipParent), except
+ // that the new position (which is not yet set on the item) is taken into account.
+ Q_ASSERT(clipParent);
+ Q_ASSERT(clipParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
+
+ // From here everything is calculated in clip parent's coordinates.
+ const QRectF parentBoundingRect(clipParent->boundingRect());
+ const QRectF thisBoundingRect(thisToParentTransform.mapRect(q->boundingRect()));
+
+ if (!parentBoundingRect.intersects(thisBoundingRect)) {
+ // Item is moved outside the clip parent's bounding rect,
+ // i.e. it is fully clipped and the clip path is empty.
+ if (flags & QGraphicsItem::ItemClipsChildrenToShape)
+ setEmptyCachedClipPathRecursively();
+ else
+ setEmptyCachedClipPathRecursively(thisToParentTransform.inverted().mapRect(parentBoundingRect));
+ return;
+ }
+
+ const QPainterPath parentClip(clipParent->isClipped() ? clipParent->clipPath() : clipParent->shape());
+ if (parentClip.contains(thisBoundingRect))
+ return; // Item is inside the clip parent's shape. No update required.
+
+ const QRectF parentClipRect(parentClip.controlPointRect());
+ if (!parentClipRect.intersects(thisBoundingRect)) {
+ // Item is moved outside the clip parent's shape,
+ // i.e. it is fully clipped and the clip path is empty.
+ if (flags & QGraphicsItem::ItemClipsChildrenToShape)
+ setEmptyCachedClipPathRecursively();
+ else
+ setEmptyCachedClipPathRecursively(thisToParentTransform.inverted().mapRect(parentClipRect));
+ } else {
+ // Item is partially inside the clip parent's shape,
+ // i.e. the cached clip path must be invalidated.
+ invalidateCachedClipPathRecursively(false, thisToParentTransform.inverted().mapRect(parentClipRect));
+ }
+}
+
/*!
\internal
@@ -3811,7 +4002,25 @@ bool QGraphicsItemPrivate::isProxyWidget() const
*/
void QGraphicsItem::update(const QRectF &rect)
{
- d_ptr->updateHelper(rect, /* force = */ false, /* updateCache = */ true);
+ if ((rect.isEmpty() && !rect.isNull()) || d_ptr->discardUpdateRequest())
+ return;
+
+ if (CacheMode(d_ptr->cacheMode) != NoCache) {
+ QGraphicsItemCache *cache = d_ptr->extraItemCache();
+ if (!cache->allExposed) {
+ if (rect.isNull()) {
+ cache->allExposed = true;
+ cache->exposed.clear();
+ } else {
+ cache->exposed.append(rect);
+ }
+ }
+ }
+
+ // Effectively the same as updateHelper(rect);
+ if (rect.isNull())
+ d_ptr->dirty = 1;
+ d_ptr->scene->itemUpdated(this, rect);
}
/*!
@@ -3882,7 +4091,7 @@ void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect)
return;
if (d->cacheMode != NoCache) {
QGraphicsItemCache *c;
- bool scrollCache = qFuzzyCompare(dx - int(dx), qreal(0.0)) && qFuzzyCompare(dy - int(dy), qreal(0.0))
+ bool scrollCache = qFuzzyIsNull(dx - int(dx)) && qFuzzyIsNull(dy - int(dy))
&& (c = (QGraphicsItemCache *)qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraCacheData)))
&& (d->cacheMode == ItemCoordinateCache && !c->fixedSize.isValid());
if (scrollCache) {
@@ -4076,7 +4285,9 @@ QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPointF &point
*/
QPointF QGraphicsItem::mapToParent(const QPointF &point) const
{
- return d_ptr->pos + (d_ptr->hasTransform ? transform().map(point) : point);
+ if (!d_ptr->hasTransform)
+ return point + d_ptr->pos;
+ return transform().map(point) + d_ptr->pos;
}
/*!
@@ -4142,8 +4353,8 @@ QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QRectF &rect
QPolygonF QGraphicsItem::mapToParent(const QRectF &rect) const
{
if (!d_ptr->hasTransform)
- return QPolygonF(rect.translated(d_ptr->pos));
- return transform().map(rect.translated(d_ptr->pos));
+ return rect.translated(d_ptr->pos);
+ return transform().map(rect).translated(d_ptr->pos);
}
/*!
@@ -4210,8 +4421,8 @@ QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, const QRectF &rec
*/
QRectF QGraphicsItem::mapRectToParent(const QRectF &rect) const
{
- QRectF r = rect.translated(d_ptr->pos.x(), d_ptr->pos.y());
- return !d_ptr->hasTransform ? r : transform().mapRect(r);
+ QRectF r = !d_ptr->hasTransform ? rect : transform().mapRect(rect);
+ return r.translated(d_ptr->pos);
}
/*!
@@ -4342,9 +4553,9 @@ QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPolygonF &p
*/
QPolygonF QGraphicsItem::mapToParent(const QPolygonF &polygon) const
{
- QPolygonF p = polygon;
- p.translate(d_ptr->pos);
- return d_ptr->hasTransform ? transform().map(p) : p;
+ if (!d_ptr->hasTransform)
+ return polygon.translated(d_ptr->pos);
+ return transform().map(polygon).translated(d_ptr->pos);
}
/*!
@@ -4386,7 +4597,9 @@ QPainterPath QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPainterP
*/
QPainterPath QGraphicsItem::mapToParent(const QPainterPath &path) const
{
- return d_ptr->parent ? itemTransform(d_ptr->parent).map(path) : mapToScene(path);
+ if (!d_ptr->hasTransform)
+ return path.translated(d_ptr->pos);
+ return transform().map(path).translated(d_ptr->pos);
}
/*!
@@ -4601,9 +4814,9 @@ QPainterPath QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPainte
*/
QPainterPath QGraphicsItem::mapFromParent(const QPainterPath &path) const
{
- if (d_ptr->parent)
- return d_ptr->parent->itemTransform(this).map(path);
- return mapFromScene(path);
+ QPainterPath p(path);
+ p.translate(-d_ptr->pos);
+ return d_ptr->hasTransform ? transform().inverted().map(p) : p;
}
/*!
@@ -5616,11 +5829,18 @@ void QGraphicsItem::removeFromIndex()
void QGraphicsItem::prepareGeometryChange()
{
if (d_ptr->scene) {
- d_ptr->updateHelper();
-
+ d_ptr->updateHelper(QRectF(), false, /*maybeDirtyClipPath=*/!d_ptr->inSetPosHelper);
QGraphicsScenePrivate *scenePrivate = d_ptr->scene->d_func();
scenePrivate->removeFromIndex(this);
}
+
+ if (d_ptr->inSetPosHelper)
+ return;
+
+ if (d_ptr->flags & ItemClipsChildrenToShape)
+ d_ptr->invalidateCachedClipPathRecursively();
+ else
+ d_ptr->invalidateCachedClipPath();
}
/*!
@@ -5635,7 +5855,7 @@ static void qt_graphicsItem_highlightSelected(
QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option)
{
const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1));
- if (qFuzzyCompare(qMax(murect.width(), murect.height()) + 1, 1))
+ if (qFuzzyIsNull(qMax(murect.width(), murect.height())))
return;
const QRectF mbrect = painter->transform().mapRect(item->boundingRect());
@@ -8204,6 +8424,8 @@ void QGraphicsTextItem::setTabChangesFocus(bool b)
Returns true if the \gui Tab key will cause the widget to change focus;
otherwise, false is returned.
+ By default, this behavior is disabled, and this function will return false.
+
\sa setTabChangesFocus()
*/
bool QGraphicsTextItem::tabChangesFocus() const
@@ -8395,6 +8617,7 @@ void QGraphicsSimpleTextItem::setText(const QString &text)
return;
d->text = text;
d->updateBoundingRect();
+ update();
}
/*!
@@ -8656,13 +8879,17 @@ void QGraphicsItemGroup::addToGroup(QGraphicsItem *item)
QTransform oldSceneMatrix = item->sceneTransform();
item->setPos(mapFromItem(item, 0, 0));
item->setParentItem(this);
- item->setTransform(oldSceneMatrix
- * sceneTransform().inverted()
- * QTransform::fromTranslate(-item->x(), -item->y()));
+ QTransform newItemTransform(oldSceneMatrix);
+ newItemTransform *= sceneTransform().inverted();
+ if (!item->pos().isNull())
+ newItemTransform *= QTransform::fromTranslate(-item->x(), -item->y());
+ item->setTransform(newItemTransform);
item->d_func()->setIsMemberOfGroup(true);
prepareGeometryChange();
- d->itemsBoundingRect |= (item->transform() * QTransform::fromTranslate(item->x(), item->y()))
- .mapRect(item->boundingRect() | item->childrenBoundingRect());
+ QTransform itemTransform(item->transform());
+ if (!item->pos().isNull())
+ itemTransform *= QTransform::fromTranslate(item->x(), item->y());
+ d->itemsBoundingRect |= itemTransform.mapRect(item->boundingRect() | item->childrenBoundingRect());
update();
}
diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h
index af0b077..078c543 100644
--- a/src/gui/graphicsview/qgraphicsitem_p.h
+++ b/src/gui/graphicsview/qgraphicsitem_p.h
@@ -134,10 +134,15 @@ public:
hasBoundingRegionGranularity(0),
flags(0),
hasOpacity(0),
+ hasEffectiveOpacity(0),
isWidget(0),
dirty(0),
dirtyChildren(0),
localCollisionHack(0),
+ dirtyClipPath(1),
+ emptyClipPath(0),
+ inSetPosHelper(0),
+ allChildrenCombineOpacity(1),
globalStackingOrder(-1),
sceneTransformIndex(-1),
q_ptr(0)
@@ -158,11 +163,15 @@ public:
virtual QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const;
static bool movableAncestorIsSelected(const QGraphicsItem *item);
- void setPosHelper(const QPointF &pos, bool update);
+ void setPosHelper(const QPointF &pos);
void setVisibleHelper(bool newVisible, bool explicitly, bool update = true);
void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true);
- void updateHelper(const QRectF &rect = QRectF(), bool force = false, bool updateCache = false);
- void fullUpdateHelper(bool childrenOnly = false);
+ bool discardUpdateRequest(bool ignoreClipping = false,
+ bool ignoreVisibleBit = false,
+ bool ignoreDirtyBit = false) const;
+ void updateHelper(const QRectF &rect = QRectF(), bool force = false, bool maybeDirtyClipPath = false);
+ void fullUpdateHelper(bool childrenOnly = false, bool maybeDirtyClipPath = false);
+ void updateEffectiveOpacity();
void resolveEffectiveOpacity(qreal effectiveParentOpacity);
void resolveDepth(int parentDepth);
void invalidateSceneTransformCache();
@@ -238,6 +247,47 @@ public:
QGraphicsItemCache *extraItemCache() const;
void removeExtraItemCache();
+ inline void setCachedClipPath(const QPainterPath &path)
+ {
+ cachedClipPath = path;
+ dirtyClipPath = 0;
+ emptyClipPath = 0;
+ }
+
+ inline void setEmptyCachedClipPath()
+ {
+ emptyClipPath = 1;
+ dirtyClipPath = 0;
+ }
+
+ void setEmptyCachedClipPathRecursively(const QRectF &emptyIfOutsideThisRect = QRectF());
+
+ inline void invalidateCachedClipPath()
+ { /*static int count = 0 ;qWarning("%i", ++count);*/ dirtyClipPath = 1; emptyClipPath = 0; }
+
+ void invalidateCachedClipPathRecursively(bool childrenOnly = false, const QRectF &emptyIfOutsideThisRect = QRectF());
+ void updateCachedClipPathFromSetPosHelper(const QPointF &newPos);
+
+ inline bool isFullyTransparent() const
+ { return hasEffectiveOpacity && qFuzzyIsNull(q_func()->effectiveOpacity()); }
+
+ inline bool childrenCombineOpacity() const
+ { return allChildrenCombineOpacity || children.isEmpty(); }
+
+ inline bool isClippedAway() const
+ { return !dirtyClipPath && q_func()->isClipped() && (emptyClipPath || cachedClipPath.isEmpty()); }
+
+ inline bool childrenClippedToShape() const
+ { return (flags & QGraphicsItem::ItemClipsChildrenToShape) || children.isEmpty(); }
+
+ inline bool isInvisible() const
+ {
+ return !visible
+ || (childrenClippedToShape() && isClippedAway())
+ || (childrenCombineOpacity() && isFullyTransparent());
+ }
+
+ QPainterPath cachedClipPath;
QPointF pos;
qreal z;
QGraphicsScene *scene;
@@ -268,10 +318,15 @@ public:
// New 32 bytes
quint32 hasOpacity : 1;
+ quint32 hasEffectiveOpacity : 1;
quint32 isWidget : 1;
quint32 dirty : 1;
quint32 dirtyChildren : 1;
quint32 localCollisionHack : 1;
+ quint32 dirtyClipPath : 1;
+ quint32 emptyClipPath : 1;
+ quint32 inSetPosHelper : 1;
+ quint32 allChildrenCombineOpacity : 1;
// Optional stacking order
int globalStackingOrder;
diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp
index 1d2721b..e660879 100644
--- a/src/gui/graphicsview/qgraphicsproxywidget.cpp
+++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp
@@ -177,6 +177,10 @@ QT_BEGIN_NAMESPACE
while the widget is embedded. In this state, the widget may differ slightly
in behavior from when it is not embedded.
+ \warning This class is provided for convenience when bridging
+ QWidgets and QGraphicsItems, it should not be used for
+ high-performance scenarios.
+
\sa QGraphicsScene::addWidget(), QGraphicsWidget
*/
@@ -1033,7 +1037,7 @@ void QGraphicsProxyWidget::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
if (receiver != d->dragDropWidget) {
// Try to enter before we leave
QDragEnterEvent dragEnter(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
- dragEnter.setDropAction(event->proposedAction());
+ dragEnter.setDropAction(event->proposedAction());
QApplication::sendEvent(receiver, &dragEnter);
event->setAccepted(dragEnter.isAccepted());
event->setDropAction(dragEnter.dropAction());
@@ -1431,7 +1435,7 @@ int QGraphicsProxyWidget::type() const
Creates a proxy widget for the given \a child of the widget
contained in this proxy.
-
+
This function makes it possible to aquire proxies for
non top-level widgets. For instance, you can embed a dialog,
and then transform only one of its widgets.
diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp
index 553967c..d9e0eec 100644
--- a/src/gui/graphicsview/qgraphicsscene.cpp
+++ b/src/gui/graphicsview/qgraphicsscene.cpp
@@ -292,15 +292,21 @@ static inline bool QRectF_intersects(const QRectF &s, const QRectF &r)
// QRectF::intersects() returns false always if either the source or target
// rectangle's width or height are 0. This works around that problem.
-static QRectF _q_adjustedRect(const QRectF &rect)
+static inline void _q_adjustRect(QRectF *rect)
{
- static const qreal p = (qreal)0.00001;
- QRectF r = rect;
- if (!r.width())
- r.adjust(-p, 0, p, 0);
- if (!r.height())
- r.adjust(0, -p, 0, p);
- return r;
+ Q_ASSERT(rect);
+ if (!rect->width())
+ rect->adjust(-0.00001, 0, 0.00001, 0);
+ if (!rect->height())
+ rect->adjust(0, -0.00001, 0, 0.00001);
+}
+
+static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item)
+{
+ Q_ASSERT(item);
+ QRectF boundingRect(item->boundingRect());
+ _q_adjustRect(&boundingRect);
+ return boundingRect;
}
static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraphicsSceneMouseEvent *mouseEvent)
@@ -345,6 +351,8 @@ QGraphicsScenePrivate::QGraphicsScenePrivate()
dragDropItem(0),
enterWidget(0),
lastDropAction(Qt::IgnoreAction),
+ allItemsIgnoreHoverEvents(true),
+ allItemsUseDefaultCursor(true),
painterStateProtection(true),
sortCacheEnabled(false),
updatingSortCache(false),
@@ -656,10 +664,11 @@ void QGraphicsScenePrivate::_q_updateLater()
*/
void QGraphicsScenePrivate::_q_polishItems()
{
+ const QVariant booleanTrueVariant(true);
foreach (QGraphicsItem *item, unpolishedItems) {
if (!item->d_ptr->explicitlyHidden) {
- item->itemChange(QGraphicsItem::ItemVisibleChange, true);
- item->itemChange(QGraphicsItem::ItemVisibleHasChanged, true);
+ item->itemChange(QGraphicsItem::ItemVisibleChange, booleanTrueVariant);
+ item->itemChange(QGraphicsItem::ItemVisibleHasChanged, booleanTrueVariant);
}
if (item->isWidget()) {
QEvent event(QEvent::Polish);
@@ -757,6 +766,15 @@ void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item)
unpolishedItems.removeAll(item);
dirtyItems.removeAll(item);
+ //We remove all references of item from the sceneEventFilter arrays
+ QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = sceneEventFilters.begin();
+ while (iterator != sceneEventFilters.end()) {
+ if (iterator.value() == item || iterator.key() == item)
+ iterator = sceneEventFilters.erase(iterator);
+ else
+ ++iterator;
+ }
+
// Remove from scene transform cache
int transformIndex = item->d_func()->sceneTransformIndex;
if (transformIndex != -1) {
@@ -884,11 +902,18 @@ void QGraphicsScenePrivate::grabMouse(QGraphicsItem *item, bool implicit)
{
// Append to list of mouse grabber items, and send a mouse grab event.
if (mouseGrabberItems.contains(item)) {
- if (mouseGrabberItems.last() == item)
- qWarning("QGraphicsItem::grabMouse: already a mouse grabber");
- else
+ if (mouseGrabberItems.last() == item) {
+ Q_ASSERT(!implicit);
+ if (!lastMouseGrabberItemHasImplicitMouseGrab) {
+ qWarning("QGraphicsItem::grabMouse: already a mouse grabber");
+ } else {
+ // Upgrade to an explicit mouse grab
+ lastMouseGrabberItemHasImplicitMouseGrab = false;
+ }
+ } else {
qWarning("QGraphicsItem::grabMouse: already blocked by mouse grabber: %p",
mouseGrabberItems.last());
+ }
return;
}
@@ -1040,6 +1065,12 @@ void QGraphicsScenePrivate::clearKeyboardGrabber()
ungrabKeyboard(keyboardGrabberItems.first());
}
+void QGraphicsScenePrivate::enableMouseTrackingOnViews()
+{
+ foreach (QGraphicsView *view, views)
+ view->viewport()->setMouseTracking(true);
+}
+
/*!
Returns all items for the screen position in \a event.
*/
@@ -1371,6 +1402,48 @@ QGraphicsWidget *QGraphicsScenePrivate::windowForItem(const QGraphicsItem *item)
return 0;
}
+
+QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPointF &pos) const
+{
+ QList<QGraphicsItem *> items;
+
+ // The index returns a rough estimate of what items are inside the rect.
+ // Refine it by iterating through all returned items.
+ QRectF adjustedRect = QRectF(pos, QSize(1,1));
+ foreach (QGraphicsItem *item, estimateItemsInRect(adjustedRect)) {
+ // Find the item's scene transform in a clever way.
+ QTransform x = item->sceneTransform();
+ bool keep = false;
+
+ // ### _q_adjustedRect is only needed because QRectF::intersects,
+ // QRectF::contains and QTransform::map() and friends don't work with
+ // flat rectangles.
+ const QRectF br(adjustedItemBoundingRect(item));
+ // Rect intersects/contains item's shape
+ if (QRectF_intersects(adjustedRect, x.mapRect(br))) {
+ bool ok;
+ QTransform xinv = x.inverted(&ok);
+ if (ok) {
+ if (item->contains(xinv.map(pos))) {
+ items << item;
+ keep = true;
+ }
+ }
+ }
+
+ if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) {
+ // Recurse into children that clip children.
+ bool ok;
+ QTransform xinv = x.inverted(&ok);
+ if (ok)
+ childItems_helper(&items, item, xinv.map(pos));
+ }
+ }
+
+ sortItems(&items, Qt::AscendingOrder, sortCacheEnabled);
+ return items;
+}
+
QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QRectF &rect,
Qt::ItemSelectionMode mode,
Qt::SortOrder order) const
@@ -1381,7 +1454,8 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QRectF &rect,
// The index returns a rough estimate of what items are inside the rect.
// Refine it by iterating through all returned items.
- QRectF adjustedRect = _q_adjustedRect(rect);
+ QRectF adjustedRect(rect);
+ _q_adjustRect(&adjustedRect);
foreach (QGraphicsItem *item, estimateItemsInRect(adjustedRect)) {
// Find the item's scene transform in a clever way.
QTransform x = item->sceneTransform();
@@ -1390,7 +1464,7 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QRectF &rect,
// ### _q_adjustedRect is only needed because QRectF::intersects,
// QRectF::contains and QTransform::map() and friends don't work with
// flat rectangles.
- QRectF br = _q_adjustedRect(item->boundingRect());
+ const QRectF br(adjustedItemBoundingRect(item));
if (mode >= Qt::ContainsItemBoundingRect) {
// Rect intersects/contains item's bounding rect
QRectF mbr = x.mapRect(br);
@@ -1405,7 +1479,7 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QRectF &rect,
bool ok;
QTransform xinv = x.inverted(&ok);
if (ok) {
- if (path == QPainterPath())
+ if (path.isEmpty())
path.addRect(rect);
if (itemCollidesWithPath(item, xinv.map(path), mode)) {
items << item;
@@ -1442,7 +1516,8 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPolygonF &poly
{
QList<QGraphicsItem *> items;
- QRectF polyRect = _q_adjustedRect(polygon.boundingRect());
+ QRectF polyRect(polygon.boundingRect());
+ _q_adjustRect(&polyRect);
QPainterPath path;
// The index returns a rough estimate of what items are inside the rect.
@@ -1455,7 +1530,7 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPolygonF &poly
// ### _q_adjustedRect is only needed because QRectF::intersects,
// QRectF::contains and QTransform::map() and friends don't work with
// flat rectangles.
- QRectF br = _q_adjustedRect(item->boundingRect());
+ const QRectF br(adjustedItemBoundingRect(item));
if (mode >= Qt::ContainsItemBoundingRect) {
// Polygon contains/intersects item's bounding rect
if (path == QPainterPath())
@@ -1500,21 +1575,47 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPainterPath &p
Qt::SortOrder order) const
{
QList<QGraphicsItem *> items;
+ QRectF pathRect(path.controlPointRect());
+ _q_adjustRect(&pathRect);
+
// The index returns a rough estimate of what items are inside the rect.
// Refine it by iterating through all returned items.
- foreach (QGraphicsItem *item, estimateItemsInRect(_q_adjustedRect(path.controlPointRect()))) {
+ foreach (QGraphicsItem *item, estimateItemsInRect(pathRect)) {
// Find the item's scene transform in a clever way.
QTransform x = item->sceneTransform();
- bool ok;
- QTransform xinv = x.inverted(&ok);
- if (ok) {
- QPainterPath mappedPath = xinv.map(path);
- if (itemCollidesWithPath(item, mappedPath, mode)) {
+ bool keep = false;
+
+ // ### _q_adjustedRect is only needed because QRectF::intersects,
+ // QRectF::contains and QTransform::map() and friends don't work with
+ // flat rectangles.
+ const QRectF br(adjustedItemBoundingRect(item));
+ if (mode >= Qt::ContainsItemBoundingRect) {
+ // Path contains/intersects item's bounding rect
+ if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(x.mapRect(br)))
+ || (mode == Qt::ContainsItemBoundingRect && path.contains(x.mapRect(br)))) {
items << item;
- if (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)
- childItems_helper(&items, item, mappedPath, mode);
+ keep = true;
+ }
+ } else {
+ // Path contains/intersects item's shape
+ if (QRectF_intersects(pathRect, x.mapRect(br))) {
+ bool ok;
+ QTransform xinv = x.inverted(&ok);
+ if (ok) {
+ if (itemCollidesWithPath(item, xinv.map(path), mode)) {
+ items << item;
+ keep = true;
+ }
+ }
}
}
+
+ if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) {
+ bool ok;
+ QTransform xinv = x.inverted(&ok);
+ if (ok)
+ childItems_helper(&items, item, xinv.map(path), mode);
+ }
}
if (order != Qt::SortOrder(-1))
@@ -1524,13 +1625,13 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPainterPath &p
void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items,
const QGraphicsItem *parent,
- const QRectF &rect,
- Qt::ItemSelectionMode mode) const
+ const QPointF &pos) const
{
- QPainterPath path;
bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape);
- QRectF r = !parentClip ? _q_adjustedRect(rect) : _q_adjustedRect(rect).intersected(_q_adjustedRect(parent->boundingRect()));
- if (r.isEmpty())
+ if (parentClip && parent->d_ptr->isClippedAway())
+ return;
+ // ### is this needed?
+ if (parentClip && !parent->boundingRect().contains(pos))
return;
QList<QGraphicsItem *> &children = parent->d_ptr->children;
@@ -1540,31 +1641,73 @@ void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items,
continue;
// Skip invisible items and all their children.
- if (!item->d_ptr->visible || qFuzzyCompare(item->effectiveOpacity(), qreal(0.0)))
+ if (item->d_ptr->isInvisible())
continue;
- // ### _q_adjustedRect is only needed because QRectF::intersects,
- // QRectF::contains and QTransform::map() and friends don't work with
- // flat rectangles.
- QRectF br = _q_adjustedRect(item->boundingRect());
- QRectF mbr = item->mapRectToParent(br);
bool keep = false;
- if (mode >= Qt::ContainsItemBoundingRect) {
- // Rect intersects/contains item's bounding rect
- if ((mode == Qt::IntersectsItemBoundingRect && QRectF_intersects(rect, mbr))
- || (mode == Qt::ContainsItemBoundingRect && rect != mbr && rect.contains(br))) {
+ if (!item->d_ptr->isClippedAway()) {
+ if (item->contains(item->mapFromParent(pos))) {
items->append(item);
keep = true;
}
- } else {
- // Rect intersects/contains item's shape
- if (QRectF_intersects(rect, mbr)) {
- if (path == QPainterPath())
- path.addRect(rect);
- if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) {
+ }
+
+ if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty())
+ // Recurse into children.
+ childItems_helper(items, item, item->mapFromParent(pos));
+ }
+}
+
+
+void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items,
+ const QGraphicsItem *parent,
+ const QRectF &rect,
+ Qt::ItemSelectionMode mode) const
+{
+ bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape);
+ if (parentClip && parent->d_ptr->isClippedAway())
+ return;
+ QRectF adjustedRect(rect);
+ _q_adjustRect(&adjustedRect);
+ QRectF r = !parentClip ? adjustedRect : adjustedRect.intersected(adjustedItemBoundingRect(parent));
+ if (r.isEmpty())
+ return;
+
+ QPainterPath path;
+ QList<QGraphicsItem *> &children = parent->d_ptr->children;
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *item = children.at(i);
+ if (item->d_ptr->hasTransform && !item->transform().isInvertible())
+ continue;
+
+ // Skip invisible items and all their children.
+ if (item->d_ptr->isInvisible())
+ continue;
+
+ bool keep = false;
+ if (!item->d_ptr->isClippedAway()) {
+ // ### _q_adjustedRect is only needed because QRectF::intersects,
+ // QRectF::contains and QTransform::map() and friends don't work with
+ // flat rectangles.
+ const QRectF br(adjustedItemBoundingRect(item));
+ QRectF mbr = item->mapRectToParent(br);
+ if (mode >= Qt::ContainsItemBoundingRect) {
+ // Rect intersects/contains item's bounding rect
+ if ((mode == Qt::IntersectsItemBoundingRect && QRectF_intersects(rect, mbr))
+ || (mode == Qt::ContainsItemBoundingRect && rect != mbr && rect.contains(br))) {
items->append(item);
keep = true;
}
+ } else {
+ // Rect intersects/contains item's shape
+ if (QRectF_intersects(rect, mbr)) {
+ if (path == QPainterPath())
+ path.addRect(rect);
+ if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) {
+ items->append(item);
+ keep = true;
+ }
+ }
}
}
@@ -1581,18 +1724,22 @@ void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items,
}
}
+
void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items,
const QGraphicsItem *parent,
const QPolygonF &polygon,
Qt::ItemSelectionMode mode) const
{
- QPainterPath path;
bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape);
- QRectF polyRect = _q_adjustedRect(polygon.boundingRect());
- QRectF r = !parentClip ? polyRect : polyRect.intersected(_q_adjustedRect(parent->boundingRect()));
+ if (parentClip && parent->d_ptr->isClippedAway())
+ return;
+ QRectF polyRect(polygon.boundingRect());
+ _q_adjustRect(&polyRect);
+ QRectF r = !parentClip ? polyRect : polyRect.intersected(adjustedItemBoundingRect(parent));
if (r.isEmpty())
return;
+ QPainterPath path;
QList<QGraphicsItem *> &children = parent->d_ptr->children;
for (int i = 0; i < children.size(); ++i) {
QGraphicsItem *item = children.at(i);
@@ -1600,32 +1747,34 @@ void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items,
continue;
// Skip invisible items.
- if (!item->d_ptr->visible || qFuzzyCompare(item->effectiveOpacity() + 1, qreal(1.0)))
+ if (item->d_ptr->isInvisible())
continue;
- // ### _q_adjustedRect is only needed because QRectF::intersects,
- // QRectF::contains and QTransform::map() and friends don't work with
- // flat rectangles.
- QRectF br = _q_adjustedRect(item->boundingRect());
bool keep = false;
- if (mode >= Qt::ContainsItemBoundingRect) {
- // Polygon contains/intersects item's bounding rect
- if (path == QPainterPath())
- path.addPolygon(polygon);
- if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(item->mapRectToParent(br)))
- || (mode == Qt::ContainsItemBoundingRect && path.contains(item->mapRectToParent(br)))) {
- items->append(item);
- keep = true;
- }
- } else {
- // Polygon contains/intersects item's shape
- if (QRectF_intersects(polyRect, item->mapRectToParent(br))) {
+ if (!item->d_ptr->isClippedAway()) {
+ // ### _q_adjustedRect is only needed because QRectF::intersects,
+ // QRectF::contains and QTransform::map() and friends don't work with
+ // flat rectangles.
+ const QRectF br(adjustedItemBoundingRect(item));
+ if (mode >= Qt::ContainsItemBoundingRect) {
+ // Polygon contains/intersects item's bounding rect
if (path == QPainterPath())
path.addPolygon(polygon);
- if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) {
+ if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(item->mapRectToParent(br)))
+ || (mode == Qt::ContainsItemBoundingRect && path.contains(item->mapRectToParent(br)))) {
items->append(item);
keep = true;
}
+ } else {
+ // Polygon contains/intersects item's shape
+ if (QRectF_intersects(polyRect, item->mapRectToParent(br))) {
+ if (path == QPainterPath())
+ path.addPolygon(polygon);
+ if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) {
+ items->append(item);
+ keep = true;
+ }
+ }
}
}
@@ -1642,30 +1791,52 @@ void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items,
Qt::ItemSelectionMode mode) const
{
bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape);
- QPainterPath intersectedPath = !parentClip ? path : path.intersected(parent->shape());
- if (intersectedPath.isEmpty())
+ if (parentClip && parent->d_ptr->isClippedAway())
+ return;
+ QRectF pathRect(path.boundingRect());
+ _q_adjustRect(&pathRect);
+ QRectF r = !parentClip ? pathRect : pathRect.intersected(adjustedItemBoundingRect(parent));
+ if (r.isEmpty())
return;
QList<QGraphicsItem *> &children = parent->d_ptr->children;
for (int i = 0; i < children.size(); ++i) {
QGraphicsItem *item = children.at(i);
+ if (item->d_ptr->hasTransform && !item->transform().isInvertible())
+ continue;
// Skip invisible items.
- if (!item->d_ptr->visible || qFuzzyCompare(item->effectiveOpacity(), qreal(0.0)))
+ if (item->d_ptr->isInvisible())
continue;
- QTransform x = item->sceneTransform();
-
- bool ok;
- QTransform xinv = x.inverted(&ok);
- if (ok) {
- QPainterPath mappedPath = xinv.map(path);
- if (itemCollidesWithPath(item, mappedPath, mode)) {
- items->append(item);
- if (!item->d_ptr->children.isEmpty())
- childItems_helper(items, item, mappedPath, mode);
+ bool keep = false;
+ if (!item->d_ptr->isClippedAway()) {
+ // ### _q_adjustedRect is only needed because QRectF::intersects,
+ // QRectF::contains and QTransform::map() and friends don't work with
+ // flat rectangles.
+ const QRectF br(adjustedItemBoundingRect(item));
+ if (mode >= Qt::ContainsItemBoundingRect) {
+ // Polygon contains/intersects item's bounding rect
+ if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(item->mapRectToParent(br)))
+ || (mode == Qt::ContainsItemBoundingRect && path.contains(item->mapRectToParent(br)))) {
+ items->append(item);
+ keep = true;
+ }
+ } else {
+ // Path contains/intersects item's shape
+ if (QRectF_intersects(pathRect, item->mapRectToParent(br))) {
+ if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) {
+ items->append(item);
+ keep = true;
+ }
+ }
}
}
+
+ if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) {
+ // Recurse into children that clip children.
+ childItems_helper(items, item, item->mapFromParent(path), mode);
+ }
}
}
@@ -2339,17 +2510,8 @@ QList<QGraphicsItem *> QGraphicsScene::items() const
*/
QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const
{
- QList<QGraphicsItem *> itemsAtPoint;
-
- // Find all items within a 1x1 rect area starting at pos. This can be
- // inefficient for scenes that use small coordinates (like unity
- // coordinates), or for detailed graphs. ### The index should support
- // fetching items at a pos to avoid this limitation.
- foreach (QGraphicsItem *item, items(QRectF(pos, QSizeF(1, 1)), Qt::IntersectsItemBoundingRect)) {
- if (item->contains(item->mapFromScene(pos)))
- itemsAtPoint << item;
- }
- return itemsAtPoint;
+ Q_D(const QGraphicsScene);
+ return d->items_helper(pos);
}
@@ -2625,6 +2787,8 @@ void QGraphicsScene::clear()
d->lastItemCount = 0;
d->bspTree.clear();
d->largestUntransformableItem = QRectF();
+ d->allItemsIgnoreHoverEvents = true;
+ d->allItemsUseDefaultCursor = true;
}
/*!
@@ -2734,8 +2898,9 @@ void QGraphicsScene::addItem(QGraphicsItem *item)
// Notify the item that its scene is changing, and allow the item to
// react.
- QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(item->itemChange(QGraphicsItem::ItemSceneChange,
- qVariantFromValue<QGraphicsScene *>(this)));
+ const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange,
+ qVariantFromValue<QGraphicsScene *>(this)));
+ QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(newSceneVariant);
if (targetScene != this) {
if (targetScene && item->scene() != targetScene)
targetScene->addItem(item);
@@ -2788,6 +2953,17 @@ void QGraphicsScene::addItem(QGraphicsItem *item)
++d->selectionChanging;
int oldSelectedItemSize = d->selectedItems.size();
+ // Enable mouse tracking if the item accepts hover events or has a cursor set.
+ if (d->allItemsIgnoreHoverEvents && d->itemAcceptsHoverEvents_helper(item)) {
+ d->allItemsIgnoreHoverEvents = false;
+ d->enableMouseTrackingOnViews();
+ }
+ if (d->allItemsUseDefaultCursor && item->hasCursor()) {
+ d->allItemsUseDefaultCursor = false;
+ if (d->allItemsIgnoreHoverEvents) // already enabled otherwise
+ d->enableMouseTrackingOnViews();
+ }
+
// Update selection lists
if (item->isSelected())
d->selectedItems << item;
@@ -2833,7 +3009,7 @@ void QGraphicsScene::addItem(QGraphicsItem *item)
emit selectionChanged();
// Deliver post-change notification
- item->itemChange(QGraphicsItem::ItemSceneHasChanged, qVariantFromValue<QGraphicsScene *>(this));
+ item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant);
}
/*!
@@ -3097,8 +3273,9 @@ void QGraphicsScene::removeItem(QGraphicsItem *item)
// Notify the item that it's scene is changing to 0, allowing the item to
// react.
- QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(item->itemChange(QGraphicsItem::ItemSceneChange,
- qVariantFromValue<QGraphicsScene *>(0)));
+ const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange,
+ qVariantFromValue<QGraphicsScene *>(0)));
+ QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(newSceneVariant);
if (targetScene != 0 && targetScene != this) {
targetScene->addItem(item);
return;
@@ -3171,6 +3348,16 @@ void QGraphicsScene::removeItem(QGraphicsItem *item)
d->unpolishedItems.removeAll(item);
d->dirtyItems.removeAll(item);
+ //We remove all references of item from the sceneEventFilter arrays
+ QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = d->sceneEventFilters.begin();
+ while (iterator != d->sceneEventFilters.end()) {
+ if (iterator.value() == item || iterator.key() == item)
+ iterator = d->sceneEventFilters.erase(iterator);
+ else
+ ++iterator;
+ }
+
+
//Ensure dirty flag have the correct default value so the next time it will be added it will receive updates
item->d_func()->dirty = 0;
item->d_func()->dirtyChildren = 0;
@@ -3198,7 +3385,7 @@ void QGraphicsScene::removeItem(QGraphicsItem *item)
emit selectionChanged();
// Deliver post-change notification
- item->itemChange(QGraphicsItem::ItemSceneHasChanged, qVariantFromValue<QGraphicsScene *>(0));
+ item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant);
}
/*!
@@ -3472,7 +3659,7 @@ QVariant QGraphicsScene::inputMethodQuery(Qt::InputMethodQuery query) const
void QGraphicsScene::update(const QRectF &rect)
{
Q_D(QGraphicsScene);
- if (d->updateAll)
+ if (d->updateAll || (rect.isEmpty() && !rect.isNull()))
return;
// Check if anyone's connected; if not, we can send updates directly to
@@ -4079,6 +4266,9 @@ bool QGraphicsScenePrivate::itemAcceptsHoverEvents_helper(const QGraphicsItem *i
*/
bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent)
{
+ if (allItemsIgnoreHoverEvents)
+ return false;
+
// Find the first item that accepts hover events, reusing earlier
// calculated data is possible.
if (cachedItemsUnderMouse.isEmpty()) {
@@ -4478,7 +4668,7 @@ static void _q_paintItem(QGraphicsItem *item, QPainter *painter,
? proxy->widget()->windowOpacity() : 1.0;
const qreal oldPainterOpacity = painter->opacity();
- if (qFuzzyCompare(windowOpacity + 1, qreal(1.0)))
+ if (qFuzzyIsNull(windowOpacity))
return;
// Set new painter opacity.
if (windowOpacity < 1.0)
@@ -4581,7 +4771,9 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
// Item's (local) bounding rect
QRectF brect = item->boundingRect();
- if (_q_adjustedRect(brect).isEmpty())
+ QRectF adjustedBrect(brect);
+ _q_adjustRect(&adjustedBrect);
+ if (adjustedBrect.isEmpty())
return;
// Fetch the off-screen transparent buffer and exposed area info.
@@ -4612,16 +4804,17 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
QSize pixmapSize;
bool fixedCacheSize = false;
+ QRectF brectAligned = brect.toAlignedRect();
if ((fixedCacheSize = itemCache->fixedSize.isValid())) {
pixmapSize = itemCache->fixedSize;
} else {
- pixmapSize = brect.toAlignedRect().size();
+ pixmapSize = brectAligned.size().toSize();
}
// Create or recreate the pixmap.
int adjust = itemCache->fixedSize.isValid() ? 0 : 2;
QSize adjustSize(adjust*2, adjust*2);
- QRectF br = brect.adjusted(-adjust, -adjust, adjust, adjust);
+ QRectF br = brectAligned.adjusted(-adjust, -adjust, adjust, adjust);
if (pix.isNull() || (!fixedCacheSize && (pixmapSize + adjustSize) != pix.size())) {
pix = QPixmap(pixmapSize + adjustSize);
itemCache->exposed.clear();
@@ -4631,9 +4824,11 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
// Redraw any newly exposed areas.
if (itemCache->allExposed || !itemCache->exposed.isEmpty()) {
// Fit the item's bounding rect into the pixmap's coordinates.
- const QPointF scale(pixmapSize.width() / brect.width(), pixmapSize.height() / brect.height());
QTransform itemToPixmap;
- itemToPixmap.scale(scale.x(), scale.y());
+ if (fixedCacheSize) {
+ const QPointF scale(pixmapSize.width() / brect.width(), pixmapSize.height() / brect.height());
+ itemToPixmap.scale(scale.x(), scale.y());
+ }
itemToPixmap.translate(-br.x(), -br.y());
// Generate the item's exposedRect and map its list of expose
@@ -4750,11 +4945,9 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
if (newCacheIndent != deviceData->cacheIndent || deviceRect.size() != pix.size()) {
QPoint diff = newCacheIndent - deviceData->cacheIndent;
QPixmap newPix(deviceRect.size());
- // ### Investigate removing this fill (test with Plasma and
- // graphicssystem raster).
- newPix.fill(Qt::transparent);
if (!pix.isNull()) {
QPainter newPixPainter(&newPix);
+ newPixPainter.setCompositionMode(QPainter::CompositionMode_Source);
newPixPainter.drawPixmap(-diff, pix);
newPixPainter.end();
}
@@ -4786,8 +4979,9 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
if (itemCache->allExposed || !itemCache->exposed.isEmpty() || !scrollExposure.isEmpty()) {
// Construct an item-to-pixmap transform.
QPointF p = deviceRect.topLeft();
- QTransform itemToPixmap = QTransform::fromTranslate(-p.x(), -p.y());
- itemToPixmap = painter->worldTransform() * itemToPixmap;
+ QTransform itemToPixmap = painter->worldTransform();
+ if (!p.isNull())
+ itemToPixmap *= QTransform::fromTranslate(-p.x(), -p.y());
// Map the item's logical expose to pixmap coordinates.
QRegion pixmapExposed = scrollExposure;
@@ -5114,16 +5308,19 @@ void QGraphicsScene::itemUpdated(QGraphicsItem *item, const QRectF &rect)
// Deliver the actual update.
if (!d->updateAll) {
if (d->views.isEmpty() || ((d->connectedSignals & d->changedSignalMask) && !item->d_ptr->itemIsUntransformable()
- && qFuzzyCompare(item->boundingRegionGranularity(), qreal(0.0)))) {
+ && qFuzzyIsNull(item->boundingRegionGranularity()))) {
// This block of code is kept for compatibility. Since 4.5, by default
// QGraphicsView does not connect the signal and we use the below
// method of delivering updates.
update(item->sceneBoundingRect());
} else {
// ### Remove _q_adjustedRects().
- QRectF boundingRect = _q_adjustedRect(item->boundingRect());
- if (!rect.isNull())
- boundingRect &= _q_adjustedRect(rect);
+ QRectF boundingRect(adjustedItemBoundingRect(item));
+ if (!rect.isNull()) {
+ QRectF adjustedRect(rect);
+ _q_adjustRect(&adjustedRect);
+ boundingRect &= adjustedRect;
+ }
// Update each view directly.
for (int i = 0; i < d->views.size(); ++i)
@@ -5135,6 +5332,9 @@ void QGraphicsScene::itemUpdated(QGraphicsItem *item, const QRectF &rect)
d->resetDirtyItemsLater();
}
+ if (!item->isVisible())
+ return; // Hiding an item won't effect the largestUntransformableItem/sceneRect.
+
// Update d->largestUntransformableItem by mapping this item's bounding
// rect back to the topmost untransformable item's untransformed
// coordinate system (which sort of equals the 1:1 coordinate system of an
@@ -5150,7 +5350,9 @@ void QGraphicsScene::itemUpdated(QGraphicsItem *item, const QRectF &rect)
// defined scene rect.
if (!d->hasSceneRect) {
QRectF oldGrowingItemsBoundingRect = d->growingItemsBoundingRect;
- d->growingItemsBoundingRect |= _q_adjustedRect(item->sceneBoundingRect());
+ QRectF adjustedItemSceneBoundingRect(item->sceneBoundingRect());
+ _q_adjustRect(&adjustedItemSceneBoundingRect);
+ d->growingItemsBoundingRect |= adjustedItemSceneBoundingRect;
if (d->growingItemsBoundingRect != oldGrowingItemsBoundingRect)
emit sceneRectChanged(d->growingItemsBoundingRect);
}
diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h
index 7f441da..9ace725 100644
--- a/src/gui/graphicsview/qgraphicsscene_p.h
+++ b/src/gui/graphicsview/qgraphicsscene_p.h
@@ -168,6 +168,9 @@ public:
Qt::DropAction lastDropAction;
QList<QGraphicsItem *> cachedItemsUnderMouse;
QList<QGraphicsItem *> hoverItems;
+ bool allItemsIgnoreHoverEvents;
+ bool allItemsUseDefaultCursor;
+ void enableMouseTrackingOnViews();
QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownPos;
QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownScenePos;
QMap<Qt::MouseButton, QPoint> mouseGrabberButtonDownScreenPos;
@@ -200,6 +203,7 @@ public:
void mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent);
QGraphicsWidget *windowForItem(const QGraphicsItem *item) const;
+ QList<QGraphicsItem *> items_helper(const QPointF &pos) const;
QList<QGraphicsItem *> items_helper(const QRectF &rect,
Qt::ItemSelectionMode mode,
Qt::SortOrder order) const;
@@ -211,6 +215,9 @@ public:
Qt::SortOrder order) const;
void childItems_helper(QList<QGraphicsItem *> *items,
const QGraphicsItem *parent,
+ const QPointF &pos) const;
+ void childItems_helper(QList<QGraphicsItem *> *items,
+ const QGraphicsItem *parent,
const QRectF &rect,
Qt::ItemSelectionMode mode) const;
void childItems_helper(QList<QGraphicsItem *> *items,
diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp
index a661441..a9d585c 100644
--- a/src/gui/graphicsview/qgraphicsview.cpp
+++ b/src/gui/graphicsview/qgraphicsview.cpp
@@ -587,6 +587,10 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event)
return;
if (!scene)
return;
+ if (scene->d_func()->allItemsIgnoreHoverEvents && scene->d_func()->allItemsUseDefaultCursor
+ && !event->buttons()) { // forward event to the scene if something is pressed.
+ return; // No need to process this event further.
+ }
QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
mouseEvent.setWidget(q->viewport());
@@ -614,6 +618,16 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event)
}
#ifndef QT_NO_CURSOR
+ // If all the items ignore hover events, we don't look-up any items
+ // in QGraphicsScenePrivate::dispatchHoverEvent, hence the
+ // cachedItemsUnderMouse list will be empty. We therefore do the look-up
+ // for cursor items here if not all items use the default cursor.
+ if (scene->d_func()->allItemsIgnoreHoverEvents && !scene->d_func()->allItemsUseDefaultCursor
+ && scene->d_func()->cachedItemsUnderMouse.isEmpty()) {
+ scene->d_func()->cachedItemsUnderMouse = scene->d_func()->itemsAtPosition(mouseEvent.screenPos(),
+ mouseEvent.scenePos(),
+ mouseEvent.widget());
+ }
// Find the topmost item under the mouse with a cursor.
foreach (QGraphicsItem *item, scene->d_func()->cachedItemsUnderMouse) {
if (item->hasCursor()) {
@@ -790,6 +804,19 @@ QRegion QGraphicsViewPrivate::mapToViewRegion(const QGraphicsItem *item, const Q
return item->boundingRegion(itv) & itv.mapRect(rect).toAlignedRect();
}
+// QRectF::intersects() returns false always if either the source or target
+// rectangle's width or height are 0. This works around that problem.
+static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item)
+{
+ Q_ASSERT(item);
+ QRectF boundingRect(item->boundingRect());
+ if (!boundingRect.width())
+ boundingRect.adjust(-0.00001, 0, 0.00001, 0);
+ if (!boundingRect.height())
+ boundingRect.adjust(0, -0.00001, 0, 0.00001);
+ return boundingRect;
+}
+
/*!
\internal
*/
@@ -801,38 +828,37 @@ void QGraphicsViewPrivate::itemUpdated(QGraphicsItem *item, const QRectF &rect)
updateLater();
QRectF updateRect = rect;
- if (item->isClipped()) {
+ if ((item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) || item->d_ptr->children.isEmpty()) {
+ updateRect &= adjustedItemBoundingRect(item);
+ if (updateRect.isEmpty())
+ return;
+ }
+
+ QGraphicsItem *clipItem = item;
+ if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) {
// Minimize unnecessary redraw.
- QGraphicsItem *p = item;
- while ((p = p->d_ptr->parent)) {
- if (p->flags() & QGraphicsItem::ItemClipsChildrenToShape) {
- updateRect &= p->itemTransform(item).mapRect(p->boundingRect());
- if (updateRect.isNull())
+ QGraphicsItem *parent = item;
+ while ((parent = parent->d_ptr->parent)) {
+ if (parent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) {
+ // Map update rect to the current parent and itersect with its bounding rect.
+ updateRect = clipItem->itemTransform(parent).mapRect(updateRect)
+ & adjustedItemBoundingRect(parent);
+ if (updateRect.isEmpty())
return;
+ clipItem = parent;
}
- if (!(p->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren))
+ if (!(parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren))
break;
}
-
- if (updateRect.isNull())
- return;
}
- // Map the rect to view coordinates.
- QRect vr = viewport->rect();
-
- if (!item->d_ptr->hasBoundingRegionGranularity) {
- QRect r = mapToViewRect(item, updateRect) & vr;
- if (r.isNull())
- return;
- this->updateRect(r);
- } else {
- QRegion r = mapToViewRegion(item, updateRect) & vr;
- if (r.isEmpty())
- return;
- updateRegion(r);
- }
+ // Map update rect from clipItem coordinates to view coordinates.
+ Q_ASSERT(clipItem);
+ if (!item->d_ptr->hasBoundingRegionGranularity)
+ this->updateRect(mapToViewRect(clipItem, updateRect) & viewport->rect());
+ else
+ updateRegion(mapToViewRegion(clipItem, updateRect) & viewport->rect());
}
void QGraphicsViewPrivate::updateLater()
@@ -855,16 +881,19 @@ void QGraphicsViewPrivate::_q_updateLaterSlot()
const QList<QGraphicsItem *> &dirtyItems = scene->d_func()->dirtyItems;
for (int i = 0; i < dirtyItems.size(); ++i) {
const QGraphicsItem *item = dirtyItems.at(i);
+ if (item->d_ptr->discardUpdateRequest(/*ignoreClipping=*/false,
+ /*ignoreVisibleBit=*/false,
+ /*ignoreDirtyBit=*/true)) {
+ continue;
+ }
QTransform x = item->sceneTransform() * viewTransform;
- QRect viewRect = x.mapRect(item->boundingRect()).toAlignedRect() & vr;
- if (!viewRect.isNull())
- updateRect(viewRect);
+ updateRect(x.mapRect(item->boundingRect()).toAlignedRect() & vr);
}
dirtyRectCount += dirtyRects.size();
bool noUpdate = !fullUpdatePending && viewportUpdateMode == QGraphicsView::FullViewportUpdate;
- if ((dirtyRectCount > 0 || !dirtyBoundingRect.isNull()) && !fullUpdatePending && !noUpdate) {
+ if ((dirtyRectCount > 0 || !dirtyBoundingRect.isEmpty()) && !fullUpdatePending && !noUpdate) {
if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate
|| (viewportUpdateMode == QGraphicsView::SmartViewportUpdate
&& dirtyRectCount >= QGRAPHICSVIEW_REGION_RECT_THRESHOLD)) {
@@ -921,6 +950,9 @@ void QGraphicsViewPrivate::updateAll()
void QGraphicsViewPrivate::updateRegion(const QRegion &r)
{
+ if (r.isEmpty())
+ return;
+
Q_Q(QGraphicsView);
// Rect intersects viewport - update everything?
@@ -969,6 +1001,9 @@ void QGraphicsViewPrivate::updateRegion(const QRegion &r)
void QGraphicsViewPrivate::updateRect(const QRect &r)
{
+ if (r.isEmpty())
+ return;
+
Q_Q(QGraphicsView);
// Rect intersects viewport - update everything?
@@ -1025,103 +1060,71 @@ void QGraphicsViewPrivate::freeStyleOptionsArray(QStyleOptionGraphicsItem *array
extern QPainterPath qt_regionToPath(const QRegion &region);
-QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion,
- const QTransform &worldTransform,
- bool *allItems) const
+/*!
+ ### Adjustments in findItems: mapToScene(QRect) forces us to adjust the
+ input rectangle by (0, 0, 1, 1), because it uses QRect::bottomRight()
+ (etc) when mapping the rectangle to a polygon (which is _wrong_). In
+ addition, as QGraphicsItem::boundingRect() is defined in logical space,
+ but the default pen for QPainter is cosmetic with a width of 0, QPainter
+ is at risk of painting 1 pixel outside the bounding rect. Therefore we
+ must search for items with an adjustment of (-1, -1, 1, 1).
+*/
+QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion, bool *allItems) const
{
Q_Q(const QGraphicsView);
- QList<QGraphicsItem *> itemList;
- QSet<QGraphicsItem *> tmp;
- bool simpleTransform = worldTransform.type() <= QTransform::TxScale;
-
- QPainterPath path = qt_regionToPath(exposedRegion);
- *allItems = path.contains(q->mapFromScene(scene->d_func()->growingItemsBoundingRect).boundingRect());
- QList<QRectF> exposedRects;
- QList<QPolygonF> exposedPolys;
-
- // Transform the exposed viewport rects to scene rects or polygons
- foreach (const QRect &rect, exposedRegion.rects()) {
- QPolygonF exposedPoly = q->mapToScene(rect.adjusted(-1, -1, 1, 1));
- QRectF exposedRect = exposedPoly.boundingRect();
- if (!simpleTransform)
- exposedPolys << exposedPoly;
- exposedRects << exposedRect;
- }
-
- // Find which items need to be drawn.
- if (*allItems) {
+
+ // Step 1) If all items are contained within the expose region, then
+ // return a list of all visible items.
+ const QRectF exposedRegionSceneBounds = q->mapToScene(exposedRegion.boundingRect().adjusted(-1, -1, 1, 1))
+ .boundingRect();
+ if (exposedRegionSceneBounds.contains(scene->d_func()->growingItemsBoundingRect)) {
+ Q_ASSERT(allItems);
+ *allItems = true;
+
// All items are guaranteed within the exposed region, don't bother using the index.
- foreach (QGraphicsItem *item, scene->items()) {
+ QList<QGraphicsItem *> itemList(scene->items());
+ int i = 0;
+ while (i < itemList.size()) {
// But we only want to include items that are visible
- if (item->isVisible())
- itemList << item;
- }
- } else if (simpleTransform) {
- // Simple rect lookups will do.
- if (exposedRects.size() > 1) {
- foreach (const QRectF &rect, exposedRects) {
- foreach (QGraphicsItem *item, scene->d_func()->items_helper(rect, Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */)) {
- if (!tmp.contains(item)) {
- tmp << item;
- itemList << item;
- }
- }
- }
- } else {
- itemList += scene->d_func()->items_helper(exposedRects[0], Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */);
- }
- } else {
- // Polygon lookup is necessary.
- if (exposedRects.size() > 1) {
- foreach (const QPolygonF &poly, exposedPolys) {
- foreach (QGraphicsItem *item, scene->d_func()->items_helper(poly, Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */)) {
- if (!tmp.contains(item)) {
- tmp << item;
- itemList << item;
- }
- }
- }
- } else {
- itemList += scene->d_func()->items_helper(exposedPolys[0], Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */);
+ if (!itemList.at(i)->isVisible())
+ itemList.removeAt(i);
+ else
+ ++i;
}
+
+ // Sort the items.
+ QGraphicsScenePrivate::sortItems(&itemList, Qt::DescendingOrder, scene->d_func()->sortCacheEnabled);
+ return itemList;
}
- // Check for items that ignore inherited transformations, and add them if
- // necessary.
- QRectF untr = scene->d_func()->largestUntransformableItem;
- if (!*allItems && !untr.isNull()) {
- // Map the largest untransformable item subtree boundingrect from view
- // to scene coordinates, and use this to expand all exposed rects in
- // search for untransformable items.
- QRectF ltri = matrix.inverted().mapRect(untr);
- ltri.adjust(-untr.width(), -untr.height(), untr.width(), untr.height());
-
- foreach (const QRect &rect, exposedRegion.rects()) {
- QRectF exposed = q->mapToScene(rect.adjusted(-1, -1, 1, 1)).boundingRect();
- exposed.adjust(-ltri.width(), -ltri.height(), ltri.width(), ltri.height());
-
- foreach (QGraphicsItem *item, scene->d_func()->estimateItemsInRect(exposed)) {
- if (item->d_ptr->itemIsUntransformable()) {
- if (!tmp.contains(item)) {
- QPainterPath rectPath;
- rectPath.addRect(rect);
- QPainterPath path = item->deviceTransform(q->viewportTransform()).inverted().map(rectPath);
- if (item->collidesWithPath(path, Qt::IntersectsItemBoundingRect)) {
- itemList << item;
- tmp << item;
- }
- }
- }
- }
- }
+ // Step 2) If the expose region is a simple rect and the view is only
+ // translated or scaled, search for items using
+ // QGraphicsScene::items(QRectF).
+ bool simpleRectLookup = (scene->d_func()->largestUntransformableItem.isNull()
+ && exposedRegion.numRects() == 1 && matrix.type() <= QTransform::TxScale);
+ if (simpleRectLookup) {
+ return scene->d_func()->items_helper(exposedRegionSceneBounds,
+ Qt::IntersectsItemBoundingRect,
+ Qt::DescendingOrder);
}
- tmp.clear();
- // Sort the items.
- QGraphicsScenePrivate::sortItems(&itemList, Qt::DescendingOrder,
- scene->d_func()->sortCacheEnabled);
+ // If the region is complex or the view has a complex transform, adjust
+ // the expose region, convert it to a path, and then search for items
+ // using QGraphicsScene::items(QPainterPath);
+ QRegion adjustedRegion;
+ foreach (const QRect &r, exposedRegion.rects())
+ adjustedRegion += r.adjusted(-1, -1, 1, 1);
+
+ const QPainterPath exposedPath(qt_regionToPath(adjustedRegion));
+ if (scene->d_func()->largestUntransformableItem.isNull()) {
+ const QPainterPath exposedScenePath(q->mapToScene(exposedPath));
+ return scene->d_func()->items_helper(exposedScenePath,
+ Qt::IntersectsItemBoundingRect,
+ Qt::DescendingOrder);
+ }
- return itemList;
+ // NB! Path must be in viewport coordinates.
+ return itemsInArea(exposedPath, Qt::IntersectsItemBoundingRect, Qt::DescendingOrder);
}
void QGraphicsViewPrivate::generateStyleOptions(const QList<QGraphicsItem *> &itemList,
@@ -1699,6 +1702,12 @@ void QGraphicsView::setScene(QGraphicsScene *scene)
d->recalculateContentSize();
d->lastCenterPoint = sceneRect().center();
d->keepLastCenterPoint = true;
+ // We are only interested in mouse tracking if items accept
+ // hover events or use non-default cursors.
+ if (!d->scene->d_func()->allItemsIgnoreHoverEvents
+ || !d->scene->d_func()->allItemsUseDefaultCursor) {
+ d->viewport->setMouseTracking(true);
+ }
} else {
d->recalculateContentSize();
}
@@ -2224,7 +2233,8 @@ QList<QGraphicsItem *> QGraphicsView::items() const
certainly room for improvement.
*/
QList<QGraphicsItem *> QGraphicsViewPrivate::itemsInArea(const QPainterPath &path,
- Qt::ItemSelectionMode mode) const
+ Qt::ItemSelectionMode mode,
+ Qt::SortOrder order) const
{
Q_Q(const QGraphicsView);
@@ -2274,8 +2284,8 @@ QList<QGraphicsItem *> QGraphicsViewPrivate::itemsInArea(const QPainterPath &pat
}
// ### Insertion sort would be faster.
- QGraphicsScenePrivate::sortItems(&result, Qt::AscendingOrder,
- scene->d_func()->sortCacheEnabled);
+ if (order != Qt::SortOrder(-1))
+ QGraphicsScenePrivate::sortItems(&result, order, scene->d_func()->sortCacheEnabled);
return result;
}
@@ -2303,7 +2313,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const
QTransform xinv = viewportTransform().inverted();
return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1)));
}
- return d->scene->items(mapToScene(pos.x(), pos.y(), 2, 2));
+ return d->scene->items(mapToScene(pos.x(), pos.y(), 1, 1));
}
QPainterPath path;
@@ -2465,10 +2475,11 @@ QPolygonF QGraphicsView::mapToScene(const QRect &rect) const
return QPolygonF();
QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll());
- QPointF tl = scrollOffset + rect.topLeft();
- QPointF tr = scrollOffset + rect.topRight();
- QPointF br = scrollOffset + rect.bottomRight();
- QPointF bl = scrollOffset + rect.bottomLeft();
+ QRect r = rect.adjusted(0, 0, 1, 1);
+ QPointF tl = scrollOffset + r.topLeft();
+ QPointF tr = scrollOffset + r.topRight();
+ QPointF br = scrollOffset + r.bottomRight();
+ QPointF bl = scrollOffset + r.bottomLeft();
QPolygonF poly;
poly.resize(4);
@@ -2809,7 +2820,12 @@ void QGraphicsView::setupViewport(QWidget *widget)
widget->setAutoFillBackground(true);
}
- widget->setMouseTracking(true);
+ // We are only interested in mouse tracking if items
+ // accept hover events or use non-default cursors.
+ if (d->scene && (!d->scene->d_func()->allItemsIgnoreHoverEvents
+ || !d->scene->d_func()->allItemsUseDefaultCursor)) {
+ widget->setMouseTracking(true);
+ }
widget->setAcceptDrops(acceptDrops());
}
@@ -3280,7 +3296,7 @@ void QGraphicsView::mouseMoveEvent(QMouseEvent *event)
}
// Update old rubberband
- if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate && !d->rubberBandRect.isNull()) {
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate && !d->rubberBandRect.isEmpty()) {
if (d->viewportUpdateMode != FullViewportUpdate)
viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
else
@@ -3456,13 +3472,13 @@ void QGraphicsView::paintEvent(QPaintEvent *event)
exposedRegion = viewport()->rect();
else if (d->viewportUpdateMode == BoundingRectViewportUpdate)
exposedRegion = event->rect();
- QRectF exposedSceneRect = mapToScene(exposedRegion.boundingRect().adjusted(0, 0, 1, 1)).boundingRect();
+ QRectF exposedSceneRect = mapToScene(exposedRegion.boundingRect()).boundingRect();
// Set up the painter
QPainter painter(viewport());
QTransform original = painter.worldTransform();
#ifndef QT_NO_RUBBERBAND
- if (d->rubberBanding && !d->rubberBandRect.isNull())
+ if (d->rubberBanding && !d->rubberBandRect.isEmpty())
painter.save();
#endif
// Set up render hints
@@ -3481,7 +3497,7 @@ void QGraphicsView::paintEvent(QPaintEvent *event)
// Find all exposed items
bool allItems = false;
- QList<QGraphicsItem *> itemList = d->findItems(exposedRegion, viewTransform, &allItems);
+ QList<QGraphicsItem *> itemList = d->findItems(exposedRegion, &allItems);
#ifdef QGRAPHICSVIEW_DEBUG
int exposedTime = stopWatch.elapsed();
@@ -3558,7 +3574,7 @@ void QGraphicsView::paintEvent(QPaintEvent *event)
#ifndef QT_NO_RUBBERBAND
// Rubberband
- if (d->rubberBanding && !d->rubberBandRect.isNull()) {
+ if (d->rubberBanding && !d->rubberBandRect.isEmpty()) {
painter.restore();
QStyleOptionRubberBand option;
option.initFrom(viewport());
@@ -3632,30 +3648,30 @@ void QGraphicsView::scrollContentsBy(int dx, int dy)
if (isRightToLeft())
dx = -dx;
- if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate
- && d->viewportUpdateMode != QGraphicsView::FullViewportUpdate) {
- for (int i = 0; i < d->dirtyRects.size(); ++i)
- d->dirtyRects[i].translate(dx, dy);
- for (int i = 0; i < d->dirtyRegions.size(); ++i)
- d->dirtyRegions[i].translate(dx, dy);
- }
-
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate) {
+ if (d->viewportUpdateMode != QGraphicsView::FullViewportUpdate) {
+ for (int i = 0; i < d->dirtyRects.size(); ++i)
+ d->dirtyRects[i].translate(dx, dy);
+ for (int i = 0; i < d->dirtyRegions.size(); ++i)
+ d->dirtyRegions[i].translate(dx, dy);
+ if (d->accelerateScrolling) {
#ifndef QT_NO_RUBBERBAND
- // Update old rubberband
- if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate && !d->rubberBandRect.isNull()) {
- if (d->viewportUpdateMode != FullViewportUpdate)
- viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
- else
- viewport()->update();
- }
+ // Update new and old rubberband regions
+ if (!d->rubberBandRect.isEmpty()) {
+ QRegion rubberBandRegion(d->rubberBandRegion(viewport(), d->rubberBandRect));
+ rubberBandRegion += rubberBandRegion.translated(-dx, -dy);
+ viewport()->update(rubberBandRegion);
+ }
#endif
-
- if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){
- if (d->accelerateScrolling && d->viewportUpdateMode != FullViewportUpdate)
- viewport()->scroll(dx, dy);
- else
+ viewport()->scroll(dx, dy);
+ } else {
+ viewport()->update();
+ }
+ } else {
viewport()->update();
+ }
}
+
d->updateLastCenterPoint();
if ((d->cacheMode & CacheBackground)
diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h
index 2109673..a76279e 100644
--- a/src/gui/graphicsview/qgraphicsview_p.h
+++ b/src/gui/graphicsview/qgraphicsview_p.h
@@ -85,7 +85,8 @@ public:
qint64 verticalScroll() const;
QList<QGraphicsItem *> itemsInArea(const QPainterPath &path,
- Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ Qt::ItemSelectionMode mode = Qt::IntersectsItemShape,
+ Qt::SortOrder = Qt::AscendingOrder) const;
QPointF mousePressItemPoint;
QPointF mousePressScenePoint;
@@ -172,9 +173,7 @@ public:
void updateRegion(const QRegion &region);
bool updateSceneSlotReimplementedChecked;
- QList<QGraphicsItem *> findItems(const QRegion &exposedRegion,
- const QTransform &worldTransform,
- bool *allItems) const;
+ QList<QGraphicsItem *> findItems(const QRegion &exposedRegion, bool *allItems) const;
void generateStyleOptions(const QList<QGraphicsItem *> &itemList,
QGraphicsItem **itemArray,
diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp
index 64ec0e7..2e7d82a 100644
--- a/src/gui/graphicsview/qgraphicswidget.cpp
+++ b/src/gui/graphicsview/qgraphicswidget.cpp
@@ -366,32 +366,33 @@ void QGraphicsWidget::resize(const QSizeF &size)
void QGraphicsWidget::setGeometry(const QRectF &rect)
{
QGraphicsWidgetPrivate *wd = QGraphicsWidget::d_func();
- const QGraphicsLayoutItemPrivate *d = QGraphicsLayoutItem::d_ptr;
- setAttribute(Qt::WA_Resized);
- QRectF newGeom = rect;
- newGeom.setSize(rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize))
- .boundedTo(effectiveSizeHint(Qt::MaximumSize)));
- if (newGeom == d->geom)
- return;
-
- // Update and prepare to change the geometry (remove from index).
- if (wd->scene) {
- if (rect.topLeft() != d->geom.topLeft())
- wd->fullUpdateHelper(true);
- else
- update();
- }
- prepareGeometryChange();
-
- // setPos triggers ItemPositionChange, which can adjust position
+ QGraphicsLayoutItemPrivate *d = QGraphicsLayoutItem::d_ptr;
+ QRectF newGeom;
QPointF oldPos = d->geom.topLeft();
- wd->inSetGeometry = 1;
- wd->setPosHelper(newGeom.topLeft(), /* update = */ false);
- wd->inSetGeometry = 0;
- newGeom.moveTopLeft(pos());
-
- if (newGeom == d->geom)
- return;
+ if (!wd->inSetPos) {
+ setAttribute(Qt::WA_Resized);
+ newGeom = rect;
+ newGeom.setSize(rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize))
+ .boundedTo(effectiveSizeHint(Qt::MaximumSize)));
+ if (newGeom == d->geom)
+ return;
+
+ // setPos triggers ItemPositionChange, which can adjust position
+ wd->inSetGeometry = 1;
+ wd->setPosHelper(newGeom.topLeft());
+ wd->inSetGeometry = 0;
+ newGeom.moveTopLeft(pos());
+
+ if (newGeom == d->geom)
+ return;
+
+ // Update and prepare to change the geometry (remove from index) if the size has changed.
+ if (wd->scene) {
+ if (rect.topLeft() == d->geom.topLeft()) {
+ prepareGeometryChange();
+ }
+ }
+ }
// Update the layout item geometry
bool moved = oldPos != pos();
@@ -401,6 +402,11 @@ void QGraphicsWidget::setGeometry(const QRectF &rect)
event.setOldPos(oldPos);
event.setNewPos(pos());
QApplication::sendEvent(this, &event);
+ if (wd->inSetPos) {
+ //set the new pos
+ d->geom.moveTopLeft(pos());
+ return;
+ }
}
QSizeF oldSize = size();
QGraphicsLayoutItem::setGeometry(newGeom);
@@ -1016,9 +1022,11 @@ QVariant QGraphicsWidget::itemChange(GraphicsItemChange change, const QVariant &
break;
case ItemPositionHasChanged:
if (!d->inSetGeometry) {
+ d->inSetPos = 1;
// Ensure setGeometry is called (avoid recursion when setPos is
// called from within setGeometry).
setGeometry(QRectF(pos(), size()));
+ d->inSetPos = 0 ;
}
break;
case ItemParentChange: {
@@ -1627,6 +1635,11 @@ void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags)
else
d->scene->d_func()->addPopup(this);
}
+
+ if (d->scene && d->scene->d_func()->allItemsIgnoreHoverEvents && d->hasDecoration()) {
+ d->scene->d_func()->allItemsIgnoreHoverEvents = false;
+ d->scene->d_func()->enableMouseTrackingOnViews();
+ }
}
/*!
diff --git a/src/gui/graphicsview/qgraphicswidget_p.cpp b/src/gui/graphicsview/qgraphicswidget_p.cpp
index 789f8da..06ffe73 100644
--- a/src/gui/graphicsview/qgraphicswidget_p.cpp
+++ b/src/gui/graphicsview/qgraphicswidget_p.cpp
@@ -66,15 +66,16 @@ void QGraphicsWidgetPrivate::init(QGraphicsItem *parentItem, Qt::WindowFlags wFl
isWidget = 1; // QGraphicsItem::isWidget() returns true.
focusNext = focusPrev = q;
focusPolicy = Qt::NoFocus;
+
+ if (!parentItem)
+ adjustWindowFlags(&wFlags);
+ windowFlags = wFlags;
+
q->setParentItem(parentItem);
q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType));
q->setGraphicsItem(q);
resolveLayoutDirection();
-
- if (!parentItem)
- adjustWindowFlags(&wFlags);
- windowFlags = wFlags;
q->unsetWindowFrameMargins();
}
qreal QGraphicsWidgetPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const
diff --git a/src/gui/graphicsview/qgraphicswidget_p.h b/src/gui/graphicsview/qgraphicswidget_p.h
index 455a129..53eaa31 100644
--- a/src/gui/graphicsview/qgraphicswidget_p.h
+++ b/src/gui/graphicsview/qgraphicswidget_p.h
@@ -86,6 +86,7 @@ public:
inheritedFontResolveMask(0),
inSetGeometry(0),
polished(0),
+ inSetPos(0),
focusPolicy(Qt::NoFocus),
focusNext(0),
focusPrev(0),
@@ -195,6 +196,7 @@ public:
quint32 attributes : 10;
quint32 inSetGeometry : 1;
quint32 polished: 1;
+ quint32 inSetPos : 1;
// Focus
Qt::FocusPolicy focusPolicy;