diff options
Diffstat (limited to 'src/gui/graphicsview/qgraphicsitem.cpp')
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 2034 |
1 files changed, 1415 insertions, 619 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index aa37981..555f7b0 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -91,30 +91,33 @@ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 0 - The boundingRect() function has many different purposes. QGraphicsScene - bases its item index on boundingRect(), and QGraphicsView uses it both for - culling invisible items, and for determining the area that needs to be - recomposed when drawing overlapping items. In addition, QGraphicsItem's - collision detection mechanisms use boundingRect() to provide an efficient - cut-off. The fine grained collision algorithm in collidesWithItem() is based - on calling shape(), which returns an accurate outline of the item's shape - as a QPainterPath. - - QGraphicsScene expects all items boundingRect() and shape() to remain - unchanged unless it is notified. If you want to change an item's geometry - in any way, you must first call prepareGeometryChange() to allow - QGraphicsScene to update its bookkeeping. + The boundingRect() function has many different purposes. + QGraphicsScene bases its item index on boundingRect(), and + QGraphicsView uses it both for culling invisible items, and for + determining the area that needs to be recomposed when drawing + overlapping items. In addition, QGraphicsItem's collision + detection mechanisms use boundingRect() to provide an efficient + cut-off. The fine grained collision algorithm in + collidesWithItem() is based on calling shape(), which returns an + accurate outline of the item's shape as a QPainterPath. + + QGraphicsScene expects all items boundingRect() and shape() to + remain unchanged unless it is notified. If you want to change an + item's geometry in any way, you must first call + prepareGeometryChange() to allow QGraphicsScene to update its + bookkeeping. Collision detection can be done in two ways: \list 1 - \o Reimplement shape() to return an accurate shape for your item, and rely - on the default implementation of collidesWithItem() to do shape-shape - intersection. This can be rather expensive if the shapes are complex. + \o Reimplement shape() to return an accurate shape for your item, + and rely on the default implementation of collidesWithItem() to do + shape-shape intersection. This can be rather expensive if the + shapes are complex. - \o Reimplement collidesWithItem() to provide your own custom item and shape - collision algorithm. + \o Reimplement collidesWithItem() to provide your own custom item + and shape collision algorithm. \endlist @@ -130,21 +133,42 @@ \img graphicsview-parentchild.png + \section1 Transformation + QGraphicsItem supports affine transformations in addition to its base - position, pos(). To change the item's transformation, you can either pass - a transformation matrix to setTransform(), or call one of the convenience - functions rotate(), scale(), translate(), or shear(). Item transformations - accumulate from parent to child, so if both a parent and child item are - rotated 90 degrees, the child's total transformation will be 180 degrees. - Similarly, if the item's parent is scaled to 2x its original size, its - children will also be twice as large. An item's transformation does not - affect its own local geometry; all geometry functions (e.g., contains(), + position, pos(). To change the item's transformation, you can pass + a transformation matrix to setTransform() + + Item transformations accumulate from parent to child, so if both a + parent and child item are rotated 90 degrees, the child's total + transformation will be 180 degrees. Similarly, if the item's + parent is scaled to 2x its original size, its children will also + be twice as large. An item's transformation does not affect its + own local geometry; all geometry functions (e.g., contains(), update(), and all the mapping functions) still operate in local coordinates. For convenience, QGraphicsItem provides the functions - sceneTransform(), which returns the item's total transformation matrix - (including its position and all parents' positions and transformations), - and scenePos(), which returns its position in scene coordinates. To reset - an item's matrix, call resetTransform(). + sceneTransform(), which returns the item's total transformation + matrix (including its position and all parents' positions and + transformations), and scenePos(), which returns its position in + scene coordinates. To reset an item's matrix, call + resetTransform(). + + Another way to apply transformation to an item is to use the , or + set the different transformation properties (transformOrigin, + x/y/zRotation, x/yScale, horizontal/verticalShear). Those + properties come in addition to the base transformation + + The order you set the transformation properties does not affect + the resulting transformation The resulting transformation is + always computed in the following order + + \code + [Origin] [Base] [RotateX] [RotateY] [RotateZ] [Shear] [Scale] [-Origin] + \endcode + + Where [Base] is the stransformation set by setTransform + + \section1 Painting The paint() function is called by QGraphicsView to paint the item's contents. The item has no background or default fill of its own; whatever @@ -161,6 +185,8 @@ high z-values. Stacking order applies to sibling items; parents are always drawn before their children. + \section1 Events + QGraphicsItem receives events from QGraphicsScene through the virtual function sceneEvent(). This function distributes the most common events to a set of convenience event handlers: @@ -186,6 +212,8 @@ by the virtual function sceneEventFilter(). You can remove item event filters by calling removeSceneEventFilter(). + \section1 Custom Data + Sometimes it's useful to register custom data with an item, be it a custom item, or a standard item. You can call setData() on any item to store data in it using a key-value pair (the key being an integer, and the value is a @@ -274,6 +302,27 @@ this flag, the child will be stacked behind it. This flag is useful for drop shadow effects and for decoration objects that follow the parent item's geometry without drawing on top of it. + + \value ItemUsesExtendedStyleOption The item makes use of either + QStyleOptionGraphicsItem::exposedRect or QStyleOptionGraphicsItem::matrix. + By default, the exposedRect is initialized to the item's boundingRect and + the matrix is untransformed. Enable this flag for more fine-grained values. + Note that QStyleOptionGraphicsItem::levelOfDetail is unaffected by this flag + and is always initialized to 1. + Use QStyleOptionGraphicsItem::levelOfDetailFromTransform for a more + fine-grained value. + + \value ItemHasNoContents The item does not paint anything (i.e., calling + paint() on the item has no effect). You should set this flag on items that + do not need to be painted to ensure that Graphics View avoids unnecessary + painting preparations. This flag was introduced in Qt 4.6. + + \value ItemSendsGeometryChanges The item enables itemChange() + notifications for ItemPositionChange, ItemPositionHasChanged, + ItemMatrixChange, ItemTransformChange, and ItemTransformHasChanged. For + performance reasons, these notifications are disabled by default. You must + enable this flag to receive notifications for position and transform + changes. This flag was introduced in Qt 4.6. */ /*! @@ -312,33 +361,37 @@ changing. This value is obsolete; you can use ItemTransformChange instead. \value ItemPositionChange The item's position changes. This notification - is only sent when the item's local position changes, relative to its - parent, has changed (i.e., as a result of calling setPos() or - moveBy()). The value argument is the new position (i.e., a QPointF). You - can call pos() to get the original position. Do not call setPos() or - moveBy() in itemChange() as this notification is delivered; instead, you - can return the new, adjusted position from itemChange(). After this - notification, QGraphicsItem immediately sends the ItemPositionHasChanged - notification if the position changed. + is sent if the ItemSendsGeometryChanges flag is enabled, and when the + item's local position changes, relative to its parent (i.e., as a result + of calling setPos() or moveBy()). The value argument is the new position + (i.e., a QPointF). You can call pos() to get the original position. Do + not call setPos() or moveBy() in itemChange() as this notification is + delivered; instead, you can return the new, adjusted position from + itemChange(). After this notification, QGraphicsItem immediately sends the + ItemPositionHasChanged notification if the position changed. \value ItemPositionHasChanged The item's position has changed. This - notification is only sent after the item's local position, relative to its - parent, has changed. The value argument is the new position (the same as - pos()), and QGraphicsItem ignores the return value for this notification - (i.e., a read-only notification). + notification is sent if the ItemSendsGeometryChanges flag is enabled, and + after the item's local position, relative to its parent, has changed. The + value argument is the new position (the same as pos()), and QGraphicsItem + ignores the return value for this notification (i.e., a read-only + notification). \value ItemTransformChange The item's transformation matrix changes. This - notification is only sent when the item's local transformation matrix - changes (i.e., as a result of calling setTransform(), or one of the - convenience transformation functions, such as rotate()). The value - argument is the new matrix (i.e., a QTransform); to get the old matrix, - call transform(). Do not call setTransform() or any of the transformation - convenience functions in itemChange() as this notification is delivered; - instead, you can return the new matrix from itemChange(). + notification is send if the ItemSendsGeometryChanges flag is enabled, and + when the item's local transformation matrix changes (i.e., as a result of + calling setTransform(). The value argument is the new matrix (i.e., a + QTransform); to get the old matrix, call transform(). Do not call + setTransform() or set any of the transformation properties in itemChange() + as this notification is delivered; instead, you can return the new matrix + from itemChange(). This notification is not sent if you change the + transformation properties. \value ItemTransformHasChanged The item's transformation matrix has - changed. This notification is only sent after the item's local - trasformation matrix has changed. The value argument is the new matrix + changed either because setTransform is called, or one of the + transformation properties is changed. This notification is sent if the + ItemSendsGeometryChanges flag is enabled, and after the item's local + transformation matrix has changed. The value argument is the new matrix (same as transform()), and QGraphicsItem ignores the return value for this notification (i.e., a read-only notification). @@ -522,6 +575,11 @@ #include <private/qtextdocumentlayout_p.h> #include <private/qtextengine_p.h> +#ifdef Q_WS_X11 +#include <private/qt_x11_p.h> +#include <private/qpixmap_x11_p.h> +#endif + #include <math.h> QT_BEGIN_NAMESPACE @@ -559,29 +617,6 @@ Q_GLOBAL_STATIC(QGraphicsItemCustomDataStore, qt_dataStore) /*! \internal - Removes the first instance of \a child from \a children. This is a - heuristic approach that assumes that it's common to remove items from the - start or end of the list. -*/ -static void qt_graphicsitem_removeChild(QGraphicsItem *child, QList<QGraphicsItem *> *children) -{ - const int n = children->size(); - for (int i = 0; i < (n + 1) / 2; ++i) { - if (children->at(i) == child) { - children->removeAt(i); - return; - } - int j = n - i - 1; - if (children->at(j) == child) { - children->removeAt(j); - return; - } - } -} - -/*! - \internal - Returns a QPainterPath of \a path when stroked with the \a pen. Ignoring dash pattern. */ @@ -754,12 +789,43 @@ QPointF QGraphicsItemPrivate::genericMapFromScene(const QPointF &pos, /*! \internal - Returns true if this item or any of its ancestors are untransformable. + Combines this item's position and transform onto \a transform. + + If you need to change this function (e.g., adding more transformation + modes / options), make sure to change all places marked with COMBINE. +*/ +void QGraphicsItemPrivate::combineTransformToParent(QTransform *x, const QTransform *viewTransform) const +{ + // COMBINE + if (itemIsUntransformable() && viewTransform) { + *x = q_ptr->deviceTransform(*viewTransform); + } else { + if (transformData) + *x *= transformData->computedFullTransform(); + if (!pos.isNull()) + *x *= QTransform::fromTranslate(pos.x(), pos.y()); + } +} + +/*! + \internal + + Combines this item's position and transform onto \a transform. + + If you need to change this function (e.g., adding more transformation + modes / options), make sure to change QGraphicsItem::deviceTransform() as + well. */ -bool QGraphicsItemPrivate::itemIsUntransformable() const +void QGraphicsItemPrivate::combineTransformFromParent(QTransform *x, const QTransform *viewTransform) const { - return (flags & QGraphicsItem::ItemIgnoresTransformations) - || (ancestorFlags & AncestorIgnoresTransformations); + // COMBINE + if (itemIsUntransformable() && viewTransform) { + *x = q_ptr->deviceTransform(*viewTransform); + } else { + x->translate(pos.x(), pos.y()); + if (transformData) + *x = transformData->computedFullTransform(x); + } } /*! @@ -781,11 +847,217 @@ QVariant QGraphicsItemPrivate::inputMethodQueryHelper(Qt::InputMethodQuery query /*! \internal + Make sure not to trigger any pure virtual function calls (e.g., + prepareGeometryChange) if the item is in its destructor, i.e. + inDestructor is 1. +*/ +void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) +{ + Q_Q(QGraphicsItem); + if (newParent == q) { + qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this); + return; + } + if (newParent == parent) + return; + + const QVariant newParentVariant(q->itemChange(QGraphicsItem::ItemParentChange, + qVariantFromValue<QGraphicsItem *>(newParent))); + newParent = qVariantValue<QGraphicsItem *>(newParentVariant); + if (newParent == parent) + return; + + if (QGraphicsWidget *w = isWidget ? static_cast<QGraphicsWidget *>(q) : q->parentWidget()) { + // Update the child focus chain; when reparenting a widget that has a + // focus child, ensure that that focus child clears its focus child + // chain from our parents before it's reparented. + if (QGraphicsWidget *focusChild = w->focusWidget()) + focusChild->clearFocus(); + } + + // We anticipate geometry changes. If the item is deleted, it will be + // removed from the index at a later stage, and the whole scene will be + // updated. + if (!inDestructor) + q_ptr->prepareGeometryChange(); + + const QVariant thisPointerVariant(qVariantFromValue<QGraphicsItem *>(q)); + if (parent) { + // Remove from current parent + parent->d_ptr->removeChild(q); + parent->itemChange(QGraphicsItem::ItemChildRemovedChange, thisPointerVariant); + } + + // Update toplevelitem list. If this item is being deleted, its parent + // will be 0 but we don't want to register/unregister it in the TLI list. + if (scene && !inDestructor) { + if (parent && !newParent) { + scene->d_func()->registerTopLevelItem(q); + } else if (!parent && newParent) { + scene->d_func()->unregisterTopLevelItem(q); + } + } + + if ((parent = newParent)) { + bool implicitUpdate = false; + if (parent->d_func()->scene && parent->d_func()->scene != scene) { + // Move this item to its new parent's scene + parent->d_func()->scene->addItem(q); + implicitUpdate = true; + } else if (!parent->d_func()->scene && scene) { + // Remove this item from its former scene + scene->removeItem(q); + } + + parent->d_ptr->addChild(q); + parent->itemChange(QGraphicsItem::ItemChildAddedChange, thisPointerVariant); + if (!implicitUpdate && scene) { + scene->d_func()->markDirty(q_ptr, QRect(), + /*invalidateChildren=*/false, + /*maybeDirtyClipPath=*/true); + } + + // Inherit ancestor flags from the new parent. + updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); + updateAncestorFlag(QGraphicsItem::ItemClipsChildrenToShape); + updateAncestorFlag(QGraphicsItem::ItemIgnoresTransformations); + + // Update item visible / enabled. + if (parent->isVisible() != visible) { + if (!parent->isVisible() || !explicitlyHidden) + setVisibleHelper(parent->isVisible(), /* explicit = */ false, /* update = */ !implicitUpdate); + } + if (parent->isEnabled() != enabled) { + if (!parent->isEnabled() || !explicitlyDisabled) + setEnabledHelper(parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate); + } + + } else { + // Inherit ancestor flags from the new parent. + updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); + updateAncestorFlag(QGraphicsItem::ItemClipsChildrenToShape); + updateAncestorFlag(QGraphicsItem::ItemIgnoresTransformations); + + if (!inDestructor) { + // 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 (scene) { + scene->d_func()->markDirty(q_ptr, QRect(), + /*invalidateChildren=*/false, + /*maybeDirtyClipPath=*/true); + } + } + } + + if (scene) { + // Invalidate any sort caching; arrival of a new item means we need to + // resort. + scene->d_func()->invalidateSortCache(); + } + + // Resolve depth. + resolveDepth(parent ? parent->d_ptr->depth : -1); + dirtySceneTransform = 1; + + // Deliver post-change notification + q->itemChange(QGraphicsItem::ItemParentHasChanged, newParentVariant); + + if (isObject) + emit static_cast<QGraphicsObject *>(q)->parentChanged(); +} + +/*! + \internal + + Returns the bounding rect of this item's children (excluding itself). +*/ +void QGraphicsItemPrivate::childrenBoundingRectHelper(QTransform *x, QRectF *rect) +{ + if (!dirtyChildrenBoundingRect) { + *rect |= x->mapRect(childrenBoundingRect); + return; + } + + for (int i = 0; i < children.size(); ++i) { + QGraphicsItem *child = children.at(i); + QGraphicsItemPrivate *childd = child->d_ptr; + bool hasPos = !childd->pos.isNull(); + if (hasPos || childd->transformData) { + // COMBINE + QTransform matrix = childd->transformToParent() * *x; + *rect |= matrix.mapRect(child->boundingRect()); + if (!childd->children.isEmpty()) + childd->childrenBoundingRectHelper(&matrix, rect); + } else { + *rect |= x->mapRect(child->boundingRect()); + if (!childd->children.isEmpty()) + childd->childrenBoundingRectHelper(x, rect); + } + } + + childrenBoundingRect = *rect; + dirtyChildrenBoundingRect = 0; +} + +void QGraphicsItemPrivate::initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform, + const QRegion &exposedRegion, bool allItems) const +{ + Q_ASSERT(option); + Q_Q(const QGraphicsItem); + + // Initialize standard QStyleOption values. + const QRectF brect = q->boundingRect(); + option->state = QStyle::State_None; + option->rect = brect.toRect(); + option->levelOfDetail = 1; + option->exposedRect = brect; + if (selected) + option->state |= QStyle::State_Selected; + if (enabled) + option->state |= QStyle::State_Enabled; + if (q->hasFocus()) + option->state |= QStyle::State_HasFocus; + if (scene) { + if (scene->d_func()->hoverItems.contains(q_ptr)) + option->state |= QStyle::State_MouseOver; + if (q == scene->mouseGrabberItem()) + option->state |= QStyle::State_Sunken; + } + + if (!(flags & QGraphicsItem::ItemUsesExtendedStyleOption)) + return; + + // Initialize QStyleOptionGraphicsItem specific values (matrix, exposedRect). + option->matrix = worldTransform.toAffine(); //### discards perspective + + if (!allItems) { + // Determine the item's exposed area + option->exposedRect = QRectF(); + const QTransform reverseMap = worldTransform.inverted(); + const QVector<QRect> exposedRects(exposedRegion.rects()); + for (int i = 0; i < exposedRects.size(); ++i) { + option->exposedRect |= reverseMap.mapRect(exposedRects.at(i)); + if (option->exposedRect.contains(brect)) + break; + } + option->exposedRect &= brect; + } +} + +/*! + \internal + Empty all cached pixmaps from the pixmap cache. */ void QGraphicsItemCache::purge() { QPixmapCache::remove(key); + key = QPixmapCache::Key(); QMutableMapIterator<QPaintDevice *, DeviceData> it(deviceData); while (it.hasNext()) { DeviceData &data = it.next().value(); @@ -853,27 +1125,22 @@ QGraphicsItem::QGraphicsItem(QGraphicsItemPrivate &dd, QGraphicsItem *parent, */ QGraphicsItem::~QGraphicsItem() { - clearFocus(); + d_ptr->inDestructor = 1; d_ptr->removeExtraItemCache(); - QVariant variant; - foreach (QGraphicsItem *child, d_ptr->children) { - if (QGraphicsItem *parent = child->parentItem()) { - qVariantSetValue<QGraphicsItem *>(variant, child); - parent->itemChange(ItemChildRemovedChange, variant); - } - delete child; + clearFocus(); + if (!d_ptr->children.isEmpty()) { + QList<QGraphicsItem *> oldChildren = d_ptr->children; + qDeleteAll(oldChildren); + Q_ASSERT(d_ptr->children.isEmpty()); } - d_ptr->children.clear(); - if (QGraphicsItem *parent = parentItem()) { - qVariantSetValue<QGraphicsItem *>(variant, this); - parent->itemChange(ItemChildRemovedChange, variant); - qt_graphicsitem_removeChild(this, &parent->d_func()->children); - } if (d_ptr->scene) - d_ptr->scene->d_func()->_q_removeItemLater(this); + d_ptr->scene->d_func()->removeItemHelper(this); + else + d_ptr->setParentItemHelper(0); + delete d_ptr->transformData; delete d_ptr; qt_dataStore()->data.remove(this); @@ -956,6 +1223,20 @@ QGraphicsItem *QGraphicsItem::topLevelItem() const /*! \since 4.4 + Returns a pointer to the item's parent, cast to a QGraphicsObject. returns 0 if the parent item + is not a QGraphicsObject. + + \sa parentItem(), childItems() +*/ +QGraphicsObject *QGraphicsItem::parentObject() const +{ + QGraphicsItem *p = d_ptr->parent; + return (p && p->d_ptr->isObject) ? static_cast<QGraphicsObject *>(p) : 0; +} + +/*! + \since 4.4 + Returns a pointer to the item's parent widget. The item's parent widget is the closest parent item that is a widget. @@ -1003,6 +1284,28 @@ QGraphicsWidget *QGraphicsItem::window() const } /*! + \since 4.6 + + Return the graphics item cast to a QGraphicsObject, if the class is actually a + graphics object, 0 otherwise. +*/ +QGraphicsObject *QGraphicsItem::toGraphicsObject() +{ + return d_ptr->isObject ? static_cast<QGraphicsObject *>(this) : 0; +} + +/*! + \since 4.6 + + Return the graphics item cast to a QGraphicsObject, if the class is actually a + graphics object, 0 otherwise. +*/ +const QGraphicsObject *QGraphicsItem::toGraphicsObject() const +{ + return d_ptr->isObject ? static_cast<const QGraphicsObject *>(this) : 0; +} + +/*! Sets this item's parent item to \a parent. If this item already has a parent, it is first removed from the previous parent. If \a parent is 0, this item will become a top-level item. @@ -1015,98 +1318,7 @@ QGraphicsWidget *QGraphicsItem::window() const */ void QGraphicsItem::setParentItem(QGraphicsItem *parent) { - if (parent == this) { - qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this); - return; - } - if (parent == d_ptr->parent) - return; - const QVariant newParentVariant(itemChange(ItemParentChange, qVariantFromValue<QGraphicsItem *>(parent))); - parent = qVariantValue<QGraphicsItem *>(newParentVariant); - if (parent == d_ptr->parent) - return; - - if (QGraphicsWidget *w = d_ptr->isWidget ? static_cast<QGraphicsWidget *>(this) : parentWidget()) { - // Update the child focus chain; when reparenting a widget that has a - // focus child, ensure that that focus child clears its focus child - // chain from our parents before it's reparented. - if (QGraphicsWidget *focusChild = w->focusWidget()) - focusChild->clearFocus(); - } - - // We anticipate geometry changes - prepareGeometryChange(); - - const QVariant thisPointerVariant(qVariantFromValue<QGraphicsItem *>(this)); - if (d_ptr->parent) { - // Remove from current parent - qt_graphicsitem_removeChild(this, &d_ptr->parent->d_func()->children); - d_ptr->parent->itemChange(ItemChildRemovedChange, thisPointerVariant); - } - - if ((d_ptr->parent = parent)) { - bool implicitUpdate = false; - if (parent->d_func()->scene && parent->d_func()->scene != d_ptr->scene) { - // Move this item to its new parent's scene - parent->d_func()->scene->addItem(this); - implicitUpdate = true; - } else if (!parent->d_func()->scene && d_ptr->scene) { - // Remove this item from its former scene - d_ptr->scene->removeItem(this); - } - - d_ptr->parent->d_func()->children << this; - d_ptr->parent->itemChange(ItemChildAddedChange, thisPointerVariant); - if (!implicitUpdate) - d_ptr->updateHelper(QRectF(), false, true); - - // Inherit ancestor flags from the new parent. - d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); - d_ptr->updateAncestorFlag(ItemClipsChildrenToShape); - d_ptr->updateAncestorFlag(ItemIgnoresTransformations); - - // Update item visible / enabled. - if (d_ptr->parent->isVisible() != d_ptr->visible) { - if (!d_ptr->parent->isVisible() || !d_ptr->explicitlyHidden) - d_ptr->setVisibleHelper(d_ptr->parent->isVisible(), /* explicit = */ false, /* update = */ !implicitUpdate); - } - if (d_ptr->parent->isEnabled() != d_ptr->enabled) { - if (!d_ptr->parent->isEnabled() || !d_ptr->explicitlyDisabled) - d_ptr->setEnabledHelper(d_ptr->parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate); - } - - } else { - // Inherit ancestor flags from the new parent. - d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); - d_ptr->updateAncestorFlag(ItemClipsChildrenToShape); - d_ptr->updateAncestorFlag(ItemIgnoresTransformations); - - // Update item visible / enabled. - if (!d_ptr->visible && !d_ptr->explicitlyHidden) - d_ptr->setVisibleHelper(true, /* explicit = */ false); - if (!d_ptr->enabled && !d_ptr->explicitlyDisabled) - d_ptr->setEnabledHelper(true, /* explicit = */ false); - - d_ptr->updateHelper(QRectF(), false, true); - } - - if (d_ptr->scene) { - // Invalidate any sort caching; arrival of a new item means we need to - // resort. - d_ptr->scene->d_func()->invalidateSortCache(); - } - - // Resolve opacity. - d_ptr->updateEffectiveOpacity(); - - // Resolve depth. - d_ptr->resolveDepth(parent ? parent->d_ptr->depth : -1); - - // Invalidate transform cache. - d_ptr->invalidateSceneTransformCache(); - - // Deliver post-change notification - itemChange(QGraphicsItem::ItemParentHasChanged, newParentVariant); + d_ptr->setParentItemHelper(parent); } /*! @@ -1211,7 +1423,9 @@ static void _q_qgraphicsItemSetFlag(QGraphicsItem *item, QGraphicsItem::Graphics item was selected, and \a flags does not enabled ItemIsSelectable, the item is automatically unselected. - By default, no flags are enabled. + By default, no flags are enabled. (QGraphicsWidget enables the + ItemSendsGeometryChanges flag by default in order to track position + changes.) \sa flags(), setFlag() */ @@ -1228,7 +1442,7 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) const quint32 geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations); bool fullUpdate = (quint32(flags) & geomChangeFlagsMask) != (d_ptr->flags & geomChangeFlagsMask); if (fullUpdate) - d_ptr->fullUpdateHelper(false, true); + d_ptr->paintedViewBoundingRectsNeedRepaint = 1; // Keep the old flags to compare the diff. GraphicsItemFlags oldFlags = this->flags(); @@ -1236,11 +1450,6 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) // Update flags. d_ptr->flags = flags; - // Reresolve effective opacity if the opacity flags change. - static const quint32 opacityFlagsMask = ItemIgnoresParentOpacity | ItemDoesntPropagateOpacityToChildren; - 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 // is unset. @@ -1268,8 +1477,19 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) d_ptr->updateAncestorFlag(ItemIgnoresTransformations); } - // ### Why updateHelper? - d_ptr->updateHelper(QRectF(), false, true); + if ((flags & ItemStacksBehindParent) != (oldFlags & ItemStacksBehindParent)) { + // Ensure child item sorting is up to date when toggling this flag. + if (d_ptr->parent) + d_ptr->parent->d_ptr->needSortChildren = 1; + else if (d_ptr->scene) + d_ptr->scene->d_func()->needSortTopLevelItems = 1; + } + + if (d_ptr->scene) { + d_ptr->scene->d_func()->markDirty(this, QRectF(), + /*invalidateChildren=*/true, + /*maybeDirtyClipPath*/true); + } // Notify change. itemChange(ItemFlagsHaveChanged, quint32(flags)); @@ -1332,12 +1552,6 @@ void QGraphicsItem::setCacheMode(CacheMode mode, const QSize &logicalCacheSize) cache->purge(); if (mode == ItemCoordinateCache) { - if (cache->key.isEmpty()) { - // Generate new simple pixmap cache key. - QString tmp; - tmp.sprintf("qgv-%p", this); - cache->key = tmp; - } if (lastMode == mode && cache->fixedSize == logicalCacheSize) noVisualChange = true; cache->fixedSize = logicalCacheSize; @@ -1415,7 +1629,9 @@ void QGraphicsItem::setCursor(const 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()))) { @@ -1539,7 +1755,12 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); if (c) c->purge(); - updateHelper(QRectF(), /* force = */ true); + if (scene) { + scene->d_func()->markDirty(q_ptr, QRectF(), + /*invalidateChildren=*/false, + /*maybeDirtyClipPath=*/false, + /*force=*/true); + } } // Certain properties are dropped as an item becomes invisible. @@ -1592,6 +1813,9 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo // Deliver post-change notification. q_ptr->itemChange(QGraphicsItem::ItemVisibleHasChanged, newVisibleVariant); + + if (isObject) + emit static_cast<QGraphicsObject *>(q_ptr)->visibleChanged(); } /*! @@ -1702,8 +1926,8 @@ void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bo enabled = newEnabledVariant.toBool(); // Schedule redraw. - if (update) - updateHelper(); + if (update && scene) + scene->d_func()->markDirty(q_ptr); foreach (QGraphicsItem *child, children) { if (!newEnabled || !child->d_ptr->explicitlyDisabled) @@ -1712,6 +1936,9 @@ void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bo // Deliver post-change notification. q_ptr->itemChange(QGraphicsItem::ItemEnabledHasChanged, newEnabledVariant); + + if (isObject) + emit static_cast<QGraphicsObject *>(q_ptr)->enabledChanged(); } /*! @@ -1804,9 +2031,8 @@ void QGraphicsItem::setSelected(bool selected) return; d_ptr->selected = newSelected; - d_ptr->updateHelper(); - if (d_ptr->scene) { + d_ptr->scene->d_func()->markDirty(this); QGraphicsScenePrivate *sceneD = d_ptr->scene->d_func(); if (selected) { sceneD->selectedItems << this; @@ -1842,12 +2068,7 @@ void QGraphicsItem::setSelected(bool selected) */ qreal QGraphicsItem::opacity() const { - if (d_ptr->hasOpacity) { - QVariant o = d_ptr->extra(QGraphicsItemPrivate::ExtraOpacity); - if (!o.isNull()) - return o.toDouble(); - } - return qreal(1.0); + return d_ptr->opacity; } /*! @@ -1863,11 +2084,7 @@ 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()); + return d_ptr->effectiveOpacity(); } /*! @@ -1902,30 +2119,24 @@ void QGraphicsItem::setOpacity(qreal opacity) newOpacity = qBound<qreal>(0.0, newOpacity, 1.0); // No change? Done. - if (qFuzzyCompare(newOpacity, this->opacity())) + if (newOpacity == d_ptr->opacity) return; - // Assign local opacity. - if (qFuzzyCompare(newOpacity, qreal(1.0))) { - // Opaque, unset opacity. - d_ptr->hasOpacity = 0; - d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraOpacity); - } else { - d_ptr->hasOpacity = 1; - d_ptr->setExtra(QGraphicsItemPrivate::ExtraOpacity, double(newOpacity)); - } - - // Resolve effective opacity. - if (QGraphicsItem *p = d_ptr->parent) - d_ptr->resolveEffectiveOpacity(p->effectiveOpacity()); - else - d_ptr->resolveEffectiveOpacity(1.0); + d_ptr->opacity = newOpacity; // Notify change. - itemChange(ItemOpacityHasChanged, newOpacity); + itemChange(ItemOpacityHasChanged, newOpacityVariant); // Update. - d_ptr->fullUpdateHelper(/*childrenOnly=*/false, /*maybeDirtyClipPath=*/false, /*ignoreOpacity=*/true); + if (d_ptr->scene) + d_ptr->scene->d_func()->markDirty(this, QRectF(), + /*invalidateChildren=*/true, + /*maybeDirtyClipPath=*/false, + /*force=*/false, + /*ignoreOpacity=*/true); + + if (d_ptr->isObject) + emit static_cast<QGraphicsObject *>(this)->opacityChanged(); } /*! @@ -2051,7 +2262,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(); + } } /*! @@ -2061,7 +2278,7 @@ void QGraphicsItem::setAcceptHoverEvents(bool enabled) */ void QGraphicsItem::setAcceptsHoverEvents(bool enabled) { - d_ptr->acceptsHover = quint32(enabled); + setAcceptHoverEvents(enabled); } /*! @@ -2329,6 +2546,17 @@ QPointF QGraphicsItem::pos() const */ /*! + Set's the \a x coordinate of the item's position. Equivalent to + calling setPos(x, y()). + + \sa x(), setPos() +*/ +void QGraphicsItem::setX(qreal x) +{ + d_ptr->setPosHelper(QPointF(x, d_ptr->pos.y())); +} + +/*! \fn QGraphicsItem::y() const This convenience function is equivalent to calling pos().y(). @@ -2337,6 +2565,17 @@ QPointF QGraphicsItem::pos() const */ /*! + Set's the \a y coordinate of the item's position. Equivalent to + calling setPos(x(), y). + + \sa x(), setPos() +*/ +void QGraphicsItem::setY(qreal y) +{ + d_ptr->setPosHelper(QPointF(d_ptr->pos.x(), y)); +} + +/*! Returns the item's position in scene coordinates. This is equivalent to calling \c mapToScene(0, 0). @@ -2350,35 +2589,37 @@ QPointF QGraphicsItem::scenePos() const /*! \internal - Sets the position \a pos and notifies the change. If \a update is true, - the item is also updated; otherwise it is not updated before and after the - change. + Sets the position \a pos. */ void QGraphicsItemPrivate::setPosHelper(const QPointF &pos) { Q_Q(QGraphicsItem); - if (this->pos == pos) - return; - - // Notify the item that the position is changing. - const QVariant newPosVariant(q->itemChange(QGraphicsItem::ItemPositionChange, pos)); - QPointF newPos = newPosVariant.toPointF(); - if (newPos == this->pos) - return; - - // Update and repositition. inSetPosHelper = 1; - updateCachedClipPathFromSetPosHelper(newPos); - if (scene) { - fullUpdateHelper(true); + updateCachedClipPathFromSetPosHelper(pos); + if (scene) q->prepareGeometryChange(); + QPointF oldPos = this->pos; + this->pos = pos; + dirtySceneTransform = 1; + inSetPosHelper = 0; + if (isObject) { + if (pos.x() != oldPos.x()) + emit static_cast<QGraphicsObject *>(q_ptr)->xChanged(); + if (pos.y() != oldPos.y()) + emit static_cast<QGraphicsObject *>(q_ptr)->yChanged(); } - this->pos = newPos; - invalidateSceneTransformCache(); +} - // Send post-notification. - q->itemChange(QGraphicsItem::ItemPositionHasChanged, newPosVariant); - inSetPosHelper = 0; +/*! + \internal + + Sets the transform \a transform. +*/ +void QGraphicsItemPrivate::setTransformHelper(const QTransform &transform) +{ + q_ptr->prepareGeometryChange(); + transformData->transform = transform; + dirtySceneTransform = 1; } /*! @@ -2393,7 +2634,26 @@ void QGraphicsItemPrivate::setPosHelper(const QPointF &pos) */ void QGraphicsItem::setPos(const QPointF &pos) { - d_ptr->setPosHelper(pos); + if (d_ptr->pos == pos) + return; + + // Update and repositition. + if (!(d_ptr->flags & ItemSendsGeometryChanges)) { + d_ptr->setPosHelper(pos); + return; + } + + // Notify the item that the position is changing. + const QVariant newPosVariant(itemChange(ItemPositionChange, qVariantFromValue<QPointF>(pos))); + QPointF newPos = newPosVariant.toPointF(); + if (newPos == d_ptr->pos) + return; + + // Update and repositition. + d_ptr->setPosHelper(newPos); + + // Send post-notification. + itemChange(QGraphicsItem::ItemPositionHasChanged, newPosVariant); } /*! @@ -2466,19 +2726,391 @@ QMatrix QGraphicsItem::matrix() const /*! \since 4.3 - Returns this item's transformation matrix. If no matrix has been set, the - identity matrix is returned. + Returns this item's transformation matrix. + + Either the one set by setTransform, or the resulting transformation from + all the transfmation properties + + If no matrix or transformation property has been set, the + identity matrix is returned. \sa setTransform(), sceneTransform() */ QTransform QGraphicsItem::transform() const { - if (!d_ptr->hasTransform) + if (!d_ptr->transformData) return QTransform(); - return qVariantValue<QTransform>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform)); + return d_ptr->transformData->transform; +} + +/*! + \since 4.6 + + Returns the rotation around the X axis. + + The default is 0 + + \warning The value doesn't take in account any rotation set with + the setTransform() method. + + \sa setXRotation(), {Transformations} +*/ +qreal QGraphicsItem::xRotation() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->xRotation; +} + +/*! + \since 4.6 + + Sets the rotation around the X axis to \a angle degrees. + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa xRotation(), setTransformOrigin(), {Transformations} +*/ +void QGraphicsItem::setXRotation(qreal angle) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->xRotation = angle; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the rotation around the Y axis. + + The default is 0 + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa setYRotation(), {Transformations} +*/ +qreal QGraphicsItem::yRotation() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->yRotation; +} + +/*! + \since 4.6 + + Sets the rotation around the Y axis to \a angle degrees. + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa yRotation(), setTransformOrigin() {Transformations} +*/ +void QGraphicsItem::setYRotation(qreal angle) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->yRotation = angle; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the rotation around the Z axis. + + The default is 0 + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa setZRotation(), {Transformations} +*/ +qreal QGraphicsItem::zRotation() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->zRotation; +} + +/*! + \since 4.6 + + Sets the rotation around the Z axis to \a angle degrees. + + \warning The value doesn't take in account any rotation set with the setTransform() method. + + \sa zRotation(), setTransformOrigin(), {Transformations} +*/ +void QGraphicsItem::setZRotation(qreal angle) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->zRotation = angle; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + This convenience function set the rotation angles around the 3 axes + to \a x, \a y and \a z. + + \sa setXRotation(), setYRotation(), setZRotation(), {Transformations} +*/ +void QGraphicsItem::setRotation(qreal x, qreal y, qreal z) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->xRotation = x; + d_ptr->transformData->yRotation = y; + d_ptr->transformData->zRotation = z; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the scale factor on the X axis. + + The default is 1 + + \warning The value doesn't take in account any scaling set with the setTransform() method. + + \sa setXScale(), {Transformations} +*/ +qreal QGraphicsItem::xScale() const +{ + if (!d_ptr->transformData) + return 1; + return d_ptr->transformData->xScale; +} + +/*! + \since 4.6 + + Sets the scale factor on the X axis to \a factor. + + \warning The value doesn't take in account any scaling set with the setTransform() method. + + \sa xScale(), setTransformOrigin(), {Transformations} +*/ +void QGraphicsItem::setXScale(qreal factor) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->xScale = factor; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the scale factor on the Y axis. + + The default is 1 + + \warning The value doesn't take in account any scaling set with the setTransform() method. + + \sa setYScale(), {Transformations} +*/ +qreal QGraphicsItem::yScale() const +{ + if (!d_ptr->transformData) + return 1; + return d_ptr->transformData->yScale; +} + +/*! + \since 4.6 + + Sets the scale factor on the Y axis to \a factor. + + \warning The value doesn't take in account any scaling set with the setTransform() method. + + \sa yScale(), setTransformOrigin(), {Transformations} +*/ +void QGraphicsItem::setYScale(qreal factor) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->yScale = factor; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + This convenience function set the scaling factors on X and Y axis to \a sx and \a sy. + + \warning The value doesn't take in account any scaling set with the setTransform() method. + + \sa setXScale(), setYScale(), {Transformations} +*/ +void QGraphicsItem::setScale(qreal sx, qreal sy) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->xScale = sx; + d_ptr->transformData->yScale = sy; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the horizontal shear. + + The default is 0 + + \warning The value doesn't take in account any shearing set with the setTransform() method. + + \sa setHorizontalShear(), {Transformations} +*/ +qreal QGraphicsItem::horizontalShear() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->horizontalShear; +} + +/*! + \since 4.6 + + Sets the horizontal shear to \a shear. + + \warning The value doesn't take in account any shearing set with the setTransform() method. + + \sa horizontalShear(), {Transformations} +*/ +void QGraphicsItem::setHorizontalShear(qreal shear) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->horizontalShear = shear; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the vertical shear. + + The default is 0 + + \warning The value doesn't take in account any shearing set with the setTransform() method. + + \sa setHorizontalShear(), {Transformations} +*/ +qreal QGraphicsItem::verticalShear() const +{ + if (!d_ptr->transformData) + return 0; + return d_ptr->transformData->verticalShear; +} + +/*! + \since 4.6 + + Sets the vertical shear to \a shear. + + \warning The value doesn't take in account any shearing set with the setTransform() method. + + \sa verticalShear(), {Transformations} +*/ +void QGraphicsItem::setVerticalShear(qreal shear) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->verticalShear = shear; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + This convenience function sets the horizontal shear to \a sh and the vertical shear to \a sv. + + \warning The value doesn't take in account any shearing set with the setTransform() method. + + \sa setHorizontalShear(), setVerticalShear() +*/ +void QGraphicsItem::setShear(qreal sh, qreal sv) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->horizontalShear = sh; + d_ptr->transformData->verticalShear = sv; + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; +} + +/*! + \since 4.6 + + Returns the origin point used for transformation in item coordinate. + + The default is QPointF(0,0). + + \sa setTransformOrigin(), {Transformations} +*/ +QPointF QGraphicsItem::transformOrigin() const +{ + if (!d_ptr->transformData) + return QPointF(0,0); + return QPointF(d_ptr->transformData->xOrigin, d_ptr->transformData->yOrigin); +} + +/*! + \since 4.6 + + Sets the \a origin for transformation in item coordinate + + \sa transformOrigin(), {Transformations} +*/ +void QGraphicsItem::setTransformOrigin(const QPointF &origin) +{ + prepareGeometryChange(); + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + d_ptr->transformData->xOrigin = origin.x(); + d_ptr->transformData->yOrigin = origin.y(); + d_ptr->transformData->onlyTransform = false; + d_ptr->dirtySceneTransform = 1; } /*! + \fn void QGraphicsItem::setTransformOrigin(qreal x, qreal y) + + \since 4.6 + \overload + + Sets the origin for the transformation to the point + composed of \a x and \a y. + + \sa setTransformOrigin(), {Transformations} +*/ + + +/*! \obsolete Use sceneTransform() instead. @@ -2504,51 +3136,23 @@ QMatrix QGraphicsItem::sceneMatrix() const \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 4 Unlike transform(), which returns only an item's local transformation, this - function includes the item's (and any parents') position. + function includes the item's (and any parents') position, and all the transfomation properties. - \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate System} + \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate System}, {Transformations} */ QTransform QGraphicsItem::sceneTransform() const { - // Check if there's any entry in the transform cache. - QGraphicsScenePrivate *sd = d_ptr->scene ? d_ptr->scene->d_func() : 0; - int index = d_ptr->sceneTransformIndex; - if (sd && index != -1 && sd->validTransforms.testBit(index)) - return sd->sceneTransformCache[index]; - - // Calculate local transform. - QTransform m; - if (d_ptr->hasTransform) { - m = transform(); - 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. - if (d_ptr->parent) { - m *= d_ptr->parent->sceneTransform(); - // Don't cache toplevels - if (sd) { - if (index == -1) { - if (!sd->freeSceneTransformSlots.isEmpty()) { - index = sd->freeSceneTransformSlots.last(); - sd->freeSceneTransformSlots.pop_back(); - } else { - index = sd->sceneTransformCache.size(); - } - d_ptr->sceneTransformIndex = index; - if (index >= sd->validTransforms.size()) { - sd->validTransforms.resize(index + 1); - sd->sceneTransformCache.resize(index + 1); - } - } - sd->validTransforms.setBit(index, 1); - sd->sceneTransformCache[index] = m; - } + if (d_ptr->dirtySceneTransform) { + // This item and all its descendants have dirty scene transforms. + // We're about to validate this item's scene transform, so we have to + // invalidate all the children; otherwise there's no way for the descendants + // to detect that the ancestor has changed. + d_ptr->invalidateChildrenSceneTransform(); } - return m; + + QGraphicsItem *that = const_cast<QGraphicsItem *>(this); + d_ptr->ensureSceneTransformRecursive(&that); + return d_ptr->sceneTransform; } /*! @@ -2598,17 +3202,17 @@ QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) c // First translate the base untransformable item. QPointF mappedPoint = (untransformedAncestor->sceneTransform() * viewportTransform).map(QPointF(0, 0)); + + // COMBINE QTransform matrix; matrix.translate(mappedPoint.x(), mappedPoint.y()); - matrix = untransformedAncestor->transform() * matrix; + if (untransformedAncestor->d_ptr->transformData) + matrix = untransformedAncestor->d_ptr->transformData->computedFullTransform(&matrix); // Then transform and translate all children. for (int i = 0; i < parents.size(); ++i) { const QGraphicsItem *parent = parents.at(i); - QPointF pos = parent->pos(); - QTransform moveMatrix; - moveMatrix.translate(pos.x(), pos.y()); - matrix = (parent->transform() * moveMatrix) * matrix; + parent->d_ptr->combineTransformFromParent(&matrix); } return matrix; @@ -2651,51 +3255,40 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co if (parent == other) { 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()); + QTransform x; + d_ptr->combineTransformFromParent(&x); + return x; } // This is other's parent if (otherParent == this) { const QPointF &otherPos = other->d_ptr->pos; - if (other->d_ptr->hasTransform) { - QTransform otherToParent = other->transform(); - if (!otherPos.isNull()) - otherToParent *= QTransform::fromTranslate(otherPos.x(), otherPos.y()); + if (other->d_ptr->transformData) { + QTransform otherToParent; + other->d_ptr->combineTransformFromParent(&otherToParent); return otherToParent.inverted(ok); - } else { - if (ok) - *ok = true; - return QTransform::fromTranslate(-otherPos.x(), -otherPos.y()); } + if (ok) + *ok = true; + return QTransform::fromTranslate(-otherPos.x(), -otherPos.y()); } // Siblings if (parent == otherParent) { - bool hasTr = d_ptr->hasTransform; - bool otherHasTr = other->d_ptr->hasTransform; + // COMBINE const QPointF &itemPos = d_ptr->pos; const QPointF &otherPos = other->d_ptr->pos; - - if (!hasTr && !otherHasTr) { + if (!d_ptr->transformData && !other->d_ptr->transformData) { QPointF delta = itemPos - otherPos; if (ok) *ok = true; return QTransform::fromTranslate(delta.x(), delta.y()); } - QTransform itemToParent = QTransform::fromTranslate(itemPos.x(), itemPos.y()); - if (hasTr) - itemToParent = itemPos.isNull() ? transform() : transform() * itemToParent; - - QTransform otherToParent = QTransform::fromTranslate(otherPos.x(), otherPos.y()); - if (otherHasTr) - otherToParent = otherPos.isNull() ? other->transform() : other->transform() * otherToParent; - + QTransform itemToParent; + d_ptr->combineTransformFromParent(&itemToParent); + QTransform otherToParent; + other->d_ptr->combineTransformFromParent(&otherToParent); return itemToParent * otherToParent.inverted(ok); } @@ -2731,11 +3324,7 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co QTransform x; const QGraphicsItem *p = child; do { - const QGraphicsItemPrivate *pd = p->d_ptr; - if (pd->hasTransform) - x *= p->transform(); - if (!pd->pos.isNull()) - x *= QTransform::fromTranslate(pd->pos.x(), pd->pos.y()); + p->d_ptr->combineTransformToParent(&x); } while ((p = p->d_ptr->parent) && p != root); if (parentOfOther) return x.inverted(ok); @@ -2753,37 +3342,34 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co Use setTransform() instead. - \sa transform(), rotate(), scale(), shear(), translate(), {The Graphics View Coordinate System} + \sa transform(), {The Graphics View Coordinate System} */ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) { - QTransform oldTransform = this->transform(); - QTransform newTransform; - if (!combine) - newTransform = QTransform(matrix); - else - newTransform = QTransform(matrix) * oldTransform; - if (oldTransform == newTransform) - return; + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; - // Notify the item that the matrix is changing. - QVariant newTransformVariant(itemChange(ItemMatrixChange, - qVariantFromValue<QMatrix>(newTransform.toAffine()))); - newTransform = QTransform(qVariantValue<QMatrix>(newTransformVariant)); - if (oldTransform == newTransform) + QTransform newTransform(combine ? QTransform(matrix) * d_ptr->transformData->transform : QTransform(matrix)); + if (d_ptr->transformData->transform == newTransform) return; // Update and set the new transformation. - d_ptr->fullUpdateHelper(true, true); - prepareGeometryChange(); - d_ptr->hasTransform = !newTransform.isIdentity(); - d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform); - d_ptr->invalidateSceneTransformCache(); + if (!(d_ptr->flags & ItemSendsGeometryChanges)) { + d_ptr->setTransformHelper(newTransform); + return; + } + + // Notify the item that the transformation matrix is changing. + const QVariant newMatrixVariant = qVariantFromValue<QMatrix>(newTransform.toAffine()); + newTransform = QTransform(qVariantValue<QMatrix>(itemChange(ItemMatrixChange, newMatrixVariant))); + if (d_ptr->transformData->transform == newTransform) + return; + // Update and set the new transformation. + d_ptr->setTransformHelper(newTransform); + // Send post-notification. - // NB! We have to change the value from QMatrix to QTransform. - qVariantSetValue<QTransform>(newTransformVariant, newTransform); - itemChange(ItemTransformHasChanged, newTransformVariant); + itemChange(ItemTransformHasChanged, qVariantFromValue<QTransform>(newTransform)); } /*! @@ -2801,32 +3387,34 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) to map an item coordiate to a scene coordinate, or mapFromScene() to map from scene coordinates to item coordinates. - \sa transform(), rotate(), scale(), shear(), translate(), {The Graphics View Coordinate System} + \warning using this function doesnt affect the value of the transformation properties + + \sa transform(), setRotation(), setScale(), setShear(), setTransformOrigin(), {The Graphics View Coordinate System}, {Transformations} */ void QGraphicsItem::setTransform(const QTransform &matrix, bool combine) { - QTransform oldTransform = this->transform(); - QTransform newTransform; - if (!combine) - newTransform = matrix; - else - newTransform = matrix * oldTransform; - if (oldTransform == newTransform) + if (!d_ptr->transformData) + d_ptr->transformData = new QGraphicsItemPrivate::TransformData; + + QTransform newTransform(combine ? matrix * d_ptr->transformData->transform : matrix); + if (d_ptr->transformData->transform == newTransform) return; + // Update and set the new transformation. + if (!(d_ptr->flags & ItemSendsGeometryChanges)) { + d_ptr->setTransformHelper(newTransform); + return; + } + // Notify the item that the transformation matrix is changing. const QVariant newTransformVariant(itemChange(ItemTransformChange, qVariantFromValue<QTransform>(newTransform))); newTransform = qVariantValue<QTransform>(newTransformVariant); - if (oldTransform == newTransform) + if (d_ptr->transformData->transform == newTransform) return; // Update and set the new transformation. - d_ptr->fullUpdateHelper(true, true); - prepareGeometryChange(); - d_ptr->hasTransform = !newTransform.isIdentity(); - d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform); - d_ptr->invalidateSceneTransformCache(); + d_ptr->setTransformHelper(newTransform); // Send post-notification. itemChange(ItemTransformHasChanged, newTransformVariant); @@ -2845,8 +3433,9 @@ void QGraphicsItem::resetMatrix() /*! \since 4.3 - Resets this item's transformation matrix to the identity matrix. This is - equivalent to calling \c setTransform(QTransform()). + Resets this item's transformation matrix to the identity matrix or + all the transformation properties to their default values. + This is equivalent to calling \c setTransform(QTransform()). \sa setTransform(), transform() */ @@ -2856,6 +3445,9 @@ void QGraphicsItem::resetTransform() } /*! + \obsolete + Use setZRotation() instead + Rotates the current item transformation \a angle degrees clockwise around its origin. To translate around an arbitrary point (x, y), you need to combine translation and rotation with setTransform(). @@ -2864,6 +3456,8 @@ void QGraphicsItem::resetTransform() \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 6 + \warning using this functionhas no effect on the zRotation value + \sa setTransform(), transform(), scale(), shear(), translate() */ void QGraphicsItem::rotate(qreal angle) @@ -2872,6 +3466,9 @@ void QGraphicsItem::rotate(qreal angle) } /*! + \obsolete + Use setScale() instead + Scales the current item transformation by (\a sx, \a sy) around its origin. To scale from an arbitrary point (x, y), you need to combine translation and scaling with setTransform(). @@ -2880,7 +3477,9 @@ void QGraphicsItem::rotate(qreal angle) \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 7 - \sa setTransform(), transform(), rotate(), shear(), translate() + \warning using this function has no effect on the xScale or yScale value + + \sa setTransform(), transform() */ void QGraphicsItem::scale(qreal sx, qreal sy) { @@ -2888,9 +3487,14 @@ void QGraphicsItem::scale(qreal sx, qreal sy) } /*! + \obsolete + Use setShear instead. + Shears the current item transformation by (\a sh, \a sv). - \sa setTransform(), transform(), rotate(), scale(), translate() + \warning using this function has no effect on the horizontalShear or verticalShear value + + \sa setTransform(), transform() */ void QGraphicsItem::shear(qreal sh, qreal sv) { @@ -2898,13 +3502,16 @@ void QGraphicsItem::shear(qreal sh, qreal sv) } /*! + \obsolete + Use setPos() or setTransformOrigin() instead. + Translates the current item transformation by (\a dx, \a dy). If all you want is to move an item, you should call moveBy() or setPos() instead; this function changes the item's translation, which is conceptually separate from its position. - \sa setTransform(), transform(), rotate(), scale(), shear() + \sa setTransform(), transform() */ void QGraphicsItem::translate(qreal dx, qreal dy) { @@ -2949,11 +3556,11 @@ qreal QGraphicsItem::zValue() const /*! Sets the Z-value, or the elevation, of the item, to \a z. The elevation decides the stacking order of sibling (neighboring) items. An item of high - Z-value will be drawn on top of an item with a lower Z-value if they - share the same parent item. In addition, children of an item will always be drawn - on top of the parent, regardless of the child's Z-value. Sibling items - that share the same Z-value will be drawn in an undefined order, although - the order will stay the same for as long as the items live. + Z-value will be drawn on top of an item with a lower Z-value if they share + the same parent item. In addition, children of an item will always be + drawn on top of the parent, regardless of the child's Z-value. Sibling + items that share the same Z-value will be drawn in order of insertion; the + last inserted child is stacked above previous children. \img graphicsview-zorder.png @@ -2981,16 +3588,23 @@ void QGraphicsItem::setZValue(qreal z) qreal newZ = qreal(newZVariant.toDouble()); if (newZ == d_ptr->z) return; - d_ptr->z = z; - d_ptr->fullUpdateHelper(); + d_ptr->z = newZ; + if (d_ptr->parent) + d_ptr->parent->d_ptr->needSortChildren = 1; + else if (d_ptr->scene) + d_ptr->scene->d_func()->needSortTopLevelItems = 1; if (d_ptr->scene) { + d_ptr->scene->d_func()->markDirty(this, QRectF(), /*invalidateChildren=*/true); // Invalidate any sort caching; arrival of a new item means we need to // resort. d_ptr->scene->d_func()->invalidateSortCache(); } itemChange(ItemZValueHasChanged, newZVariant); + + if (d_ptr->isObject) + emit static_cast<QGraphicsObject *>(this)->zChanged(); } /*! @@ -3012,14 +3626,12 @@ void QGraphicsItem::setZValue(qreal z) */ QRectF QGraphicsItem::childrenBoundingRect() const { + if (!d_ptr->dirtyChildrenBoundingRect) + return d_ptr->childrenBoundingRect; + QRectF childRect; - foreach (QGraphicsItem *child, children()) { - QPointF childPos = child->pos(); - QTransform matrix = child->transform(); - if (!childPos.isNull()) - matrix *= QTransform::fromTranslate(childPos.x(), childPos.y()); - childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect()); - } + QTransform x; + d_ptr->childrenBoundingRectHelper(&x, &childRect); return childRect; } @@ -3033,7 +3645,7 @@ QRectF QGraphicsItem::childrenBoundingRect() const Although the item's shape can be arbitrary, the bounding rect is always rectangular, and it is unaffected by the items' - transformation (scale(), rotate(), etc.). + transformation. If you want to change the item's bounding rectangle, you must first call prepareGeometryChange(). This notifies the scene of the imminent change, @@ -3065,12 +3677,13 @@ QRectF QGraphicsItem::childrenBoundingRect() const QRectF QGraphicsItem::sceneBoundingRect() const { // Find translate-only offset + // COMBINE QPointF offset; const QGraphicsItem *parentItem = this; const QGraphicsItemPrivate *itemd; do { itemd = parentItem->d_ptr; - if (itemd->hasTransform) + if (itemd->transformData) break; offset += itemd->pos; } while ((parentItem = itemd->parent)); @@ -3634,7 +4247,7 @@ void QGraphicsItem::setBoundingRegionGranularity(qreal granularity) All painting is done in local coordinates. - \sa setCacheMode(), QPen::width(), {Item Coordinates} + \sa setCacheMode(), QPen::width(), {Item Coordinates}, ItemUsesExtendedStyleOption */ /*! @@ -3647,7 +4260,7 @@ bool QGraphicsItemPrivate::discardUpdateRequest(bool ignoreClipping, bool ignore // 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) + || (!ignoreDirtyBit && fullUpdatePending) || !scene || (scene->d_func()->updateAll && scene->d_func()->hasSceneRect) || (!ignoreClipping && (childrenClippedToShape() && isClippedAway())) @@ -3657,163 +4270,43 @@ bool QGraphicsItemPrivate::discardUpdateRequest(bool ignoreClipping, bool ignore /*! \internal - Asks the scene to mark this item's scene rect as dirty, requesting a - redraw. This does not invalidate any cache. - - The \a force argument is for the update call in setVisible(), which is the - only case where the item's background should be marked as dirty even when - the item isn't visible. + Resolves the stacking depth of this object and all its children. */ -void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force, bool maybeDirtyClipPath) +void QGraphicsItemPrivate::resolveDepth(int parentDepth) { - // 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 (discardUpdateRequest(/*ignoreClipping=*/maybeDirtyClipPath, /*ignoreVisibleBit=*/force)) - return; - - if (rect.isNull()) - dirty = 1; - scene->itemUpdated(q_ptr, rect); + depth = parentDepth + 1; + for (int i = 0; i < children.size(); ++i) + children.at(i)->d_ptr->resolveDepth(depth); } /*! \internal - - Propagates updates to \a item and all its children. */ -void QGraphicsItemPrivate::fullUpdateHelper(bool childrenOnly, bool maybeDirtyClipPath, bool ignoreOpacity) +void QGraphicsItemPrivate::addChild(QGraphicsItem *child) { - if (discardUpdateRequest(/*ignoreClipping=*/maybeDirtyClipPath, /*ignoreVisibleBit=*/false, - /*ignoreDirtyBit=*/true, ignoreOpacity)) { - return; - } - - 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. - QGraphicsItem *p = parent; - QRectF br = q->boundingRect(); - while (p) { - if (p->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) { - bool ok; - QTransform x = q->itemTransform(p, &ok); - if (!ok) - break; - if (x.mapRect(br).contains(p->boundingRect())) - return; - } - p = p->d_ptr->parent; - if (!p || !(p->d_ptr->ancestorFlags & AncestorClipsChildren)) - break; - // ### check one level only - break; - } - } - foreach (QGraphicsItem *child, children) - child->d_ptr->fullUpdateHelper(false, maybeDirtyClipPath, ignoreOpacity); - dirtyChildren = 1; -} - -static inline bool qt_allChildrenCombineOpacity(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 = qt_allChildrenCombineOpacity(parent); - } else { - resolveEffectiveOpacity(1.0); - } - allChildrenCombineOpacity = qt_allChildrenCombineOpacity(q); + needSortChildren = 1; + children.append(child); } /*! \internal - - Resolves and propagates this item's effective opacity to its children. */ -void QGraphicsItemPrivate::resolveEffectiveOpacity(qreal parentEffectiveOpacity) +void QGraphicsItemPrivate::removeChild(QGraphicsItem *child) { - Q_Q(QGraphicsItem); - QGraphicsItem::GraphicsItemFlags myFlags = q->flags(); - QGraphicsItem::GraphicsItemFlags parentFlags = parent ? parent->flags() : QGraphicsItem::GraphicsItemFlags(0); - - // My local opacity is always part of my effective opacity. - qreal myEffectiveOpacity = q->opacity(); - - // If I have a parent, and I don't ignore my parent's opacity, and my - // parent propagates to me, then combine my local opacity with my parent's - // effective opacity into my effective opacity. - if (parent - && !(myFlags & QGraphicsItem::ItemIgnoresParentOpacity) - && !(parentFlags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)) { - myEffectiveOpacity *= parentEffectiveOpacity; - } - - // Set this item's resolved opacity. - if (qFuzzyCompare(myEffectiveOpacity, qreal(1.0))) { - // 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) - children.at(i)->d_ptr->resolveEffectiveOpacity(myEffectiveOpacity); + children.removeOne(child); } /*! \internal - - Resolves the stacking depth of this object and all its children. */ -void QGraphicsItemPrivate::resolveDepth(int parentDepth) +QGraphicsItemCache *QGraphicsItemPrivate::maybeExtraItemCache() const { - depth = parentDepth + 1; - for (int i = 0; i < children.size(); ++i) - children.at(i)->d_ptr->resolveDepth(depth); + return (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); } /*! \internal */ -void QGraphicsItemPrivate::invalidateSceneTransformCache() -{ - if (!scene || (parent && sceneTransformIndex == -1)) - return; - if (sceneTransformIndex != -1) - scene->d_func()->validTransforms.setBit(sceneTransformIndex, 0); - for (int i = 0; i < children.size(); ++i) - children.at(i)->d_ptr->invalidateSceneTransformCache(); -} - QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const { QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); @@ -3825,6 +4318,9 @@ QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const return c; } +/*! + \internal +*/ void QGraphicsItemPrivate::removeExtraItemCache() { QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); @@ -3886,17 +4382,13 @@ void QGraphicsItemPrivate::updateCachedClipPathFromSetPosHelper(const QPointF &n // 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()); + // COMBINE + QTransform thisToParentTransform = QTransform::fromTranslate(newPos.x(), newPos.y()); + if (transformData) + thisToParentTransform = transformData->computedFullTransform(&thisToParentTransform); 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()); - } + thisToParentTransform *= clipParent->d_ptr->transformToParent(); clipParent = clipParent->d_ptr->parent; } @@ -3938,6 +4430,35 @@ void QGraphicsItemPrivate::updateCachedClipPathFromSetPosHelper(const QPointF &n } } +// Traverses all the ancestors up to the top-level and updates the pointer to +// always point to the top-most item that has a dirty scene transform. +// It then backtracks to the top-most dirty item and start calculating the +// scene transform by combining the item's transform (+pos) with the parent's +// cached scene transform (which we at this point know for sure is valid). +void QGraphicsItemPrivate::ensureSceneTransformRecursive(QGraphicsItem **topMostDirtyItem) +{ + Q_ASSERT(topMostDirtyItem); + + if (dirtySceneTransform) + *topMostDirtyItem = q_ptr; + + if (parent) + parent->d_ptr->ensureSceneTransformRecursive(topMostDirtyItem); + + if (*topMostDirtyItem == q_ptr) { + if (!dirtySceneTransform) + return; // OK, neither my ancestors nor I have dirty scene transforms. + *topMostDirtyItem = 0; + } else if (*topMostDirtyItem) { + return; // Continue backtrack. + } + + // COMBINE my transform with the parent's scene transform. + sceneTransform = parent ? parent->d_ptr->sceneTransform : QTransform(); + combineTransformFromParent(&sceneTransform); + dirtySceneTransform = 0; +} + /*! \internal @@ -3980,25 +4501,62 @@ void QGraphicsItem::update(const QRectF &rect) } // Invalidate cache. - if (rect.isNull()) { - cache->allExposed = true; - cache->exposed.clear(); - } else { - cache->exposed.append(rect); + if (!cache->allExposed) { + if (rect.isNull()) { + cache->allExposed = true; + cache->exposed.clear(); + } else { + cache->exposed.append(rect); + } } // Only invalidate cache; item is already dirty. - if (d_ptr->dirty) + if (d_ptr->fullUpdatePending) return; - } else if (d_ptr->discardUpdateRequest()) { - return; } - // Effectively the same as updateHelper(rect); - if (rect.isNull()) - d_ptr->dirty = 1; - d_ptr->scene->itemUpdated(this, rect); + if (d_ptr->scene) + d_ptr->scene->d_func()->markDirty(this, rect); } +/*! + \internal + + Scrolls \a rect in \a pix by \a dx, \a dy. + + ### This can be done much more efficiently by using XCopyArea on X11 with + the same dst and src, and through moving pixels in the raster engine. It + can probably also be done much better on the other paint engines. +*/ +void _q_scrollPixmap(QPixmap *pix, const QRect &rect, int dx, int dy) +{ +#if 0 + QPainter painter(pix); + painter.setClipRect(rect); + painter.drawPixmap(rect.translated(dx, dy), *pix, rect); + painter.end(); +#elif defined Q_WS_X11 + GC gc = XCreateGC(X11->display, pix->handle(), 0, 0); + + XRectangle xrect; + xrect.x = rect.x(); + xrect.y = rect.y(); + xrect.width = rect.width(); + xrect.height = rect.height(); + XSetClipRectangles(X11->display, gc, 0, 0, &xrect, 1, YXBanded); + + XCopyArea(X11->display, pix->handle(), pix->handle(), gc, + rect.x(), rect.y(), rect.width(), rect.height(), + rect.x()+dx, rect.y()+dy); + XFreeGC(X11->display, gc); +#else + QPixmap newPix = *pix; + QPainter painter(&newPix); + painter.setClipRect(rect); + painter.drawPixmap(rect.translated(dx, dy), *pix, rect); + painter.end(); + *pix = newPix; +#endif +} /*! \since 4.4 @@ -4027,11 +4585,45 @@ void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect) if (!d->scene) return; if (d->cacheMode != NoCache) { - // ### This is very slow, and can be done much better. If the cache is - // local and matches the below criteria for rotation and scaling, we - // can easily scroll. And if the cache is in device coordinates, we - // can scroll both the viewport and the cache. - update(rect); + QGraphicsItemCache *c; + bool scrollCache = 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) { + QPixmap pix; + if (QPixmapCache::find(c->key, &pix)) { + // Adjust with 2 pixel margin. Notice the loss of precision + // when converting to QRect. + int adjust = 2; + QRectF br = boundingRect().adjusted(-adjust, -adjust, adjust, adjust); + QRect irect = rect.toRect().translated(-br.x(), -br.y()); + + _q_scrollPixmap(&pix, irect, dx, dy); + + QPixmapCache::replace(c->key, pix); + + // Translate the existing expose. + foreach (QRectF exposedRect, c->exposed) + c->exposed += exposedRect.translated(dx, dy) & rect; + + // Calculate exposure. + QRegion exposed; + QRect r = rect.toRect(); + exposed += r; + exposed -= r.translated(dx, dy); + foreach (QRect rect, exposed.rects()) + update(rect); + d->scene->d_func()->markDirty(this); + } else { + update(rect); + } + } else { + // ### This is very slow, and can be done much better. If the cache is + // local and matches the below criteria for rotation and scaling, we + // can easily scroll. And if the cache is in device coordinates, we + // can scroll both the viewport and the cache. + update(rect); + } return; } @@ -4050,13 +4642,7 @@ void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect) static const QLineF left(0, 0, -1, 0); static const QLineF right(0, 0, 1, 0); - QTransform deviceTr; - if (d->itemIsUntransformable()) { - deviceTr = deviceTransform(view->viewportTransform()); - } else { - deviceTr = sceneTransform() * view->viewportTransform(); - } - + QTransform deviceTr = deviceTransform(view->viewportTransform()); QRect deviceScrollRect = deviceTr.mapRect(scrollRect).toRect(); QLineF v1 = deviceTr.map(right); QLineF v2 = deviceTr.map(down); @@ -4188,7 +4774,10 @@ 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); + // COMBINE + if (!d_ptr->transformData) + return point + d_ptr->pos; + return d_ptr->transformToParent().map(point); } /*! @@ -4253,9 +4842,10 @@ QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QRectF &rect */ QPolygonF QGraphicsItem::mapToParent(const QRectF &rect) const { - QPolygonF p = !d_ptr->hasTransform ? rect : transform().map(rect); - p.translate(d_ptr->pos); - return p; + // COMBINE + if (!d_ptr->transformData) + return rect.translated(d_ptr->pos); + return d_ptr->transformToParent().map(rect); } /*! @@ -4322,8 +4912,10 @@ QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, const QRectF &rec */ QRectF QGraphicsItem::mapRectToParent(const QRectF &rect) const { - QRectF r = !d_ptr->hasTransform ? rect : transform().mapRect(rect); - return r.translated(d_ptr->pos); + // COMBINE + if (!d_ptr->transformData) + return rect.translated(d_ptr->pos); + return d_ptr->transformToParent().mapRect(rect); } /*! @@ -4394,8 +4986,10 @@ QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, const QRectF &r */ QRectF QGraphicsItem::mapRectFromParent(const QRectF &rect) const { - QRectF r = rect.translated(-d_ptr->pos); - return d_ptr->hasTransform ? transform().inverted().mapRect(r) : r; + // COMBINE + if (!d_ptr->transformData) + return rect.translated(-d_ptr->pos); + return d_ptr->transformToParent().inverted().mapRect(rect); } /*! @@ -4454,9 +5048,10 @@ QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPolygonF &p */ QPolygonF QGraphicsItem::mapToParent(const QPolygonF &polygon) const { - QPolygonF p = !d_ptr->hasTransform ? polygon : transform().map(polygon); - p.translate(d_ptr->pos); - return p; + // COMBINE + if (!d_ptr->transformData) + return polygon.translated(d_ptr->pos); + return d_ptr->transformToParent().map(polygon); } /*! @@ -4498,10 +5093,10 @@ QPainterPath QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPainterP */ QPainterPath QGraphicsItem::mapToParent(const QPainterPath &path) const { - QTransform x = QTransform::fromTranslate(d_ptr->pos.x(), d_ptr->pos.y()); - if (d_ptr->hasTransform) - x = transform() * x; - return x.map(path); + // COMBINE + if (!d_ptr->transformData) + return path.translated(d_ptr->pos); + return d_ptr->transformToParent().map(path); } /*! @@ -4550,8 +5145,9 @@ QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPointF &poi */ QPointF QGraphicsItem::mapFromParent(const QPointF &point) const { - if (d_ptr->hasTransform) - return transform().inverted().map(point - d_ptr->pos); + // COMBINE + if (d_ptr->transformData) + return d_ptr->transformToParent().inverted().map(point); return point - d_ptr->pos; } @@ -4618,8 +5214,10 @@ QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QRectF &re */ QPolygonF QGraphicsItem::mapFromParent(const QRectF &rect) const { - QRectF r = rect.translated(-d_ptr->pos); - return d_ptr->hasTransform ? transform().inverted().map(r) : r; + // COMBINE + if (!d_ptr->transformData) + return rect.translated(-d_ptr->pos); + return d_ptr->transformToParent().inverted().map(rect); } /*! @@ -4674,9 +5272,10 @@ QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPolygonF */ QPolygonF QGraphicsItem::mapFromParent(const QPolygonF &polygon) const { - QPolygonF p = polygon; - p.translate(-d_ptr->pos); - return d_ptr->hasTransform ? transform().inverted().map(p) : p; + // COMBINE + if (!d_ptr->transformData) + return polygon.translated(-d_ptr->pos); + return d_ptr->transformToParent().inverted().map(polygon); } /*! @@ -4716,9 +5315,10 @@ 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); + // COMBINE + if (!d_ptr->transformData) + return path.translated(-d_ptr->pos); + return d_ptr->transformToParent().inverted().map(path); } /*! @@ -5236,7 +5836,8 @@ void QGraphicsItem::focusOutEvent(QFocusEvent *event) void QGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); - d_ptr->updateHelper(); + if (d_ptr->scene) + d_ptr->scene->d_func()->markDirty(this); } /*! @@ -5264,7 +5865,8 @@ void QGraphicsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) void QGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); - d_ptr->updateHelper(); + if (d_ptr->scene) + d_ptr->scene->d_func()->markDirty(this); } /*! @@ -5694,7 +6296,6 @@ void QGraphicsItem::addToIndex() } if (d_ptr->scene) d_ptr->scene->d_func()->addToIndex(this); - d_ptr->updateHelper(); } /*! @@ -5710,7 +6311,6 @@ void QGraphicsItem::removeFromIndex() // ### remove from child index only if applicable return; } - d_ptr->updateHelper(); if (d_ptr->scene) d_ptr->scene->d_func()->removeFromIndex(this); } @@ -5731,11 +6331,30 @@ void QGraphicsItem::removeFromIndex() void QGraphicsItem::prepareGeometryChange() { if (d_ptr->scene) { - d_ptr->updateHelper(QRectF(), false, /*maybeDirtyClipPath=*/!d_ptr->inSetPosHelper); + d_ptr->geometryChanged = 1; + d_ptr->paintedViewBoundingRectsNeedRepaint = 1; + QGraphicsScenePrivate *scenePrivate = d_ptr->scene->d_func(); + scenePrivate->markDirty(this, QRectF(), + /*invalidateChildren=*/true, + /*maybeDirtyClipPath=*/!d_ptr->inSetPosHelper); + + // For compatibility reasons, we have to update the item's old geometry + // if someone is connected to the changed signal or the scene has no views. + // Note that this has to be done *after* markDirty to ensure that + // _q_processDirtyItems is called before _q_emitUpdated. + if ((scenePrivate->connectedSignals & scenePrivate->changedSignalMask) + || scenePrivate->views.isEmpty()) { + d_ptr->scene->update(sceneTransform().mapRect(boundingRect())); + } + scenePrivate->removeFromIndex(this); } + QGraphicsItem *parent = this; + while ((parent = parent->d_ptr->parent)) + parent->d_ptr->dirtyChildrenBoundingRect = 1; + if (d_ptr->inSetPosHelper) return; @@ -5757,7 +6376,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()); @@ -5807,6 +6426,172 @@ static void qt_graphicsItem_highlightSelected( } /*! + \class QGraphicsObject + \brief The QGraphicsObject class provides a base class for all graphics items that + require signals, slots and properties. + \since 4.6 + \ingroup graphicsview-api + + The class extends a QGraphicsItem with QObject's signal/slot and property mechanisms. + It maps many of QGraphicsItem's basic setters and getters to properties and adds notification + signals for many of them. +*/ + +/*! + Constructs a QGraphicsObject with \a parent. +*/ +QGraphicsObject::QGraphicsObject(QGraphicsItem *parent) + : QGraphicsItem(parent) +{ + QGraphicsItem::d_ptr->isObject = true; +} + +/*! + \internal +*/ +QGraphicsObject::QGraphicsObject(QGraphicsItemPrivate &dd, QGraphicsItem *parent, QGraphicsScene *scene) + : QGraphicsItem(dd, parent, scene) +{ + QGraphicsItem::d_ptr->isObject = true; +} + +/*! + \property QGraphicsObject::parent + \brief the parent of the item + + \sa QGraphicsItem::setParentItem(), QGraphicsItem::parentObject() +*/ + +/*! + \property QGraphicsObject::id + \brief the id of of the item + + \sa QGraphicsItem::opacity(), QGraphicsItem::setOpacity() +*/ + +/*! + \property QGraphicsObject::opacity + \brief the opacity of the item + + \sa QGraphicsItem::setOpacity(), QGraphicsItem::opacity() +*/ + +/*! + \fn QGraphicsObject::opacityChanged() + + This signal gets emitted whenever the opacity of the item changes + + \sa QGraphicsItem::opacity() +*/ + +/*! + \fn QGraphicsObject::parentChanged() + + This signal gets emitted whenever the parent of the item changes +*/ + +/*! + \property QGraphicsObject::pos + \brief the position of the item + + Describes the items position. + + \sa QGraphicsItem::setPos(), QGraphicsItem::pos() +*/ + +/*! + \property QGraphicsObject::x + \brief the x position of the item + + Describes the items x position. + + \sa QGraphicsItem::setX(), setPos(), xChanged() +*/ + +/*! + \fn QGraphicsObject::xChanged() + + This signal gets emitted whenever the x position of the item changes + + \sa pos() +*/ + +/*! + \property QGraphicsObject::y + \brief the y position of the item + + Describes the items y position. + + \sa QGraphicsItem::setY(), setPos(), yChanged() +*/ + +/*! + \fn QGraphicsObject::yChanged() + + This signal gets emitted whenever the y position of the item changes. + + \sa pos() +*/ + +/*! + \property QGraphicsObject::z + \brief the z value of the item + + Describes the items z value. + + \sa QGraphicsItem::setZValue(), zValue(), zChanged() +*/ + +/*! + \fn QGraphicsObject::zChanged() + + This signal gets emitted whenever the z value of the item changes. + + \sa pos() +*/ + + +/*! + \property QGraphicsObject::enabled + \brief whether the item is enabled or not + + This property is declared in QGraphicsItem. + + By default, this property is true. + + \sa QGraphicsItem::isEnabled(), QGraphicsItem::setEnabled(), enabledChanged() +*/ + +/*! + \fn QGraphicsObject::enabledChanged() + + This signal gets emitted whenever the item get's enabled or disabled. + + \sa isEnabled() +*/ + +/*! + \property QGraphicsObject::visible + \brief whether the item is visible or not + + This property is declared in QGraphicsItem. + + By default, this property is true. + + \sa QGraphicsItem::isVisible(), QGraphicsItem::setVisible(), visibleChanged() +*/ + +/*! + \fn QGraphicsObject::visibleChanged() + + This signal gets emitted whenever the visibility of the item changes + + \sa visible +*/ + + + +/*! \class QAbstractGraphicsShapeItem \brief The QAbstractGraphicsShapeItem class provides a common base for all path items. @@ -7396,7 +8181,6 @@ void QGraphicsPixmapItem::setTransformationMode(Qt::TransformationMode mode) { Q_D(QGraphicsPixmapItem); if (mode != d->transformationMode) { - d_ptr->updateHelper(); d->transformationMode = mode; update(); } @@ -7484,9 +8268,7 @@ void QGraphicsPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsIte painter->setRenderHint(QPainter::SmoothPixmapTransform, (d->transformationMode == Qt::SmoothTransformation)); - QRectF exposed = option->exposedRect.adjusted(-1, -1, 1, 1); - exposed &= QRectF(d->offset.x(), d->offset.y(), d->pixmap.width(), d->pixmap.height()); - painter->drawPixmap(exposed, d->pixmap, exposed.translated(-d->offset)); + painter->drawPixmap(d->offset, d->pixmap); if (option->state & QStyle::State_Selected) qt_graphicsItem_highlightSelected(this, painter, option); @@ -7633,6 +8415,7 @@ public: QGraphicsTextItem *qq; }; + /*! Constructs a QGraphicsTextItem, using \a text as the default plain text. \a parent is passed to QGraphicsItem's constructor. @@ -7645,13 +8428,14 @@ QGraphicsTextItem::QGraphicsTextItem(const QString &text, QGraphicsItem *parent , QGraphicsScene *scene #endif ) - : QGraphicsItem(parent, scene), dd(new QGraphicsTextItemPrivate) + : QGraphicsObject(*new QGraphicsItemPrivate, parent, scene), dd(new QGraphicsTextItemPrivate) { dd->qq = this; if (!text.isEmpty()) setPlainText(text); setAcceptDrops(true); setAcceptHoverEvents(true); + setFlags(ItemUsesExtendedStyleOption); } /*! @@ -7666,11 +8450,12 @@ QGraphicsTextItem::QGraphicsTextItem(QGraphicsItem *parent , QGraphicsScene *scene #endif ) - : QGraphicsItem(parent, scene), dd(new QGraphicsTextItemPrivate) + : QGraphicsObject(*new QGraphicsItemPrivate, parent, scene), dd(new QGraphicsTextItemPrivate) { dd->qq = this; setAcceptDrops(true); setAcceptHoverEvents(true); + setFlag(ItemUsesExtendedStyleOption); } /*! @@ -7941,19 +8726,19 @@ bool QGraphicsTextItem::sceneEvent(QEvent *event) void QGraphicsTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { if ((QGraphicsItem::d_ptr->flags & (ItemIsSelectable | ItemIsMovable)) - && (event->buttons() & Qt::LeftButton) && dd->_q_mouseOnEdge(event)) { - // User left-pressed on edge of selectable/movable item, use - // base impl. - dd->useDefaultImpl = true; + && (event->buttons() & Qt::LeftButton) && dd->_q_mouseOnEdge(event)) { + // User left-pressed on edge of selectable/movable item, use + // base impl. + dd->useDefaultImpl = true; } else if (event->buttons() == event->button() - && dd->control->textInteractionFlags() == Qt::NoTextInteraction) { - // User pressed first button on non-interactive item. - dd->useDefaultImpl = true; + && dd->control->textInteractionFlags() == Qt::NoTextInteraction) { + // User pressed first button on non-interactive item. + dd->useDefaultImpl = true; } if (dd->useDefaultImpl) { QGraphicsItem::mousePressEvent(event); - if (!event->isAccepted()) - dd->useDefaultImpl = false; + if (!event->isAccepted()) + dd->useDefaultImpl = false; return; } dd->sendControlEvent(event); @@ -7978,14 +8763,14 @@ void QGraphicsTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (dd->useDefaultImpl) { QGraphicsItem::mouseReleaseEvent(event); - if (dd->control->textInteractionFlags() == Qt::NoTextInteraction - && !event->buttons()) { - // User released last button on non-interactive item. + if (dd->control->textInteractionFlags() == Qt::NoTextInteraction + && !event->buttons()) { + // User released last button on non-interactive item. dd->useDefaultImpl = false; - } else if ((event->buttons() & Qt::LeftButton) == 0) { - // User released the left button on an interactive item. + } else if ((event->buttons() & Qt::LeftButton) == 0) { + // User released the left button on an interactive item. dd->useDefaultImpl = false; - } + } return; } dd->sendControlEvent(event); @@ -8275,9 +9060,9 @@ bool QGraphicsTextItemPrivate::_q_mouseOnEdge(QGraphicsSceneMouseEvent *event) Sets the flags \a flags to specify how the text item should react to user input. - The default for a QGraphicsTextItem is Qt::NoTextInteraction. Setting a - value different to Qt::NoTextInteraction will also set the ItemIsFocusable - QGraphicsItem flag. + The default for a QGraphicsTextItem is Qt::NoTextInteraction. This function + also affects the ItemIsFocusable QGraphicsItem flag by setting it if \a flags + is different from Qt::NoTextInteraction and clearing it otherwise. By default, the text is read-only. To transform the item into an editor, set the Qt::TextEditable flag. @@ -8778,6 +9563,8 @@ void QGraphicsItemGroup::addToGroup(QGraphicsItem *item) return; } + // COMBINE + // ### Use itemTransform() instead. QTransform oldSceneMatrix = item->sceneTransform(); item->setPos(mapFromItem(item, 0, 0)); item->setParentItem(this); @@ -9016,6 +9803,15 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) case QGraphicsItem::ItemStacksBehindParent: str = "ItemStacksBehindParent"; break; + case QGraphicsItem::ItemUsesExtendedStyleOption: + str = "ItemUsesExtendedStyleOption"; + break; + case QGraphicsItem::ItemHasNoContents: + str = "ItemHasNoContents"; + break; + case QGraphicsItem::ItemSendsGeometryChanges: + str = "ItemSendsGeometryChanges"; + break; } debug << str; return debug; @@ -9023,17 +9819,17 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags) { - debug << "("; + debug << '('; bool f = false; for (int i = 0; i < 9; ++i) { if (flags & (1 << i)) { if (f) - debug << "|"; + debug << '|'; f = true; debug << QGraphicsItem::GraphicsItemFlag(int(flags & (1 << i))); } } - debug << ")"; + debug << ')'; return debug; } |